tinyrelayfile
tinyrelay/internal/daemon/collaboration_review_test.go/

internal/daemon/collaboration_review_test.go raw
1package daemon
2
3import (
4 "context"
5 "encoding/json"
6 "strings"
7 "testing"
8 "time"
9
10 "github.com/FelineStateMachine/tinyrelay/internal/event"
11 "github.com/FelineStateMachine/tinyrelay/internal/replication"
12)
13
14func TestReviewAnchorShape(t *testing.T) {
15 base := [][]string{{"a", "30617:" + strings.Repeat("a", 64) + ":test"}, {"E", strings.Repeat("b", 64), "", strings.Repeat("a", 64)}, {"K", "1618"}, {"P", strings.Repeat("a", 64)}}
16 comment := func(kind string, extra ...[]string) event.Event {
17 tags := append([][]string(nil), base...)
18 tags[2] = []string{"K", kind}
19 return event.Event{Kind: 1111, Tags: append(tags, extra...)}
20 }
21 anchor, present, err := reviewAnchorOf(comment("1618", []string{"file", "src/main.go"}, []string{"line", "12", "new"}))
22 if err != nil || !present || anchor != (reviewAnchor{File: "src/main.go", Line: 12, Side: "new"}) {
23 t.Fatalf("valid anchor: %+v %v %v", anchor, present, err)
24 }
25 if _, present, err := reviewAnchorOf(comment("1621")); err != nil || present {
26 t.Fatalf("plain comment: %v %v", present, err)
27 }
28 cases := map[string][][]string{
29 "missing line": {{"file", "a"}},
30 "missing file": {{"line", "1", "new"}},
31 "two files": {{"file", "a"}, {"file", "b"}, {"line", "1", "new"}},
32 "empty path": {{"file", " "}, {"line", "1", "new"}},
33 "zero line": {{"file", "a"}, {"line", "0", "new"}},
34 "padded line": {{"file", "a"}, {"line", "01", "new"}},
35 "word line": {{"file", "a"}, {"line", "one", "new"}},
36 "no side": {{"file", "a"}, {"line", "1"}},
37 "unknown side": {{"file", "a"}, {"line", "1", "left"}},
38 "newline in path": {{"file", "a\nb"}, {"line", "1", "new"}},
39 }
40 for name, extra := range cases {
41 if err := validateReviewAnchor(comment("1618", extra...)); err == nil || !strings.HasPrefix(err.Error(), "blocked: ") {
42 t.Fatalf("%s: %v", name, err)
43 }
44 }
45 if err := validateReviewAnchor(comment("1621", []string{"file", "a"}, []string{"line", "1", "new"})); err == nil || !strings.Contains(err.Error(), "pull request or patch") {
46 t.Fatalf("anchor under an issue: %v", err)
47 }
48 if err := validateReviewAnchor(comment("1617", []string{"file", "a"}, []string{"line", "3", "old"})); err != nil {
49 t.Fatalf("anchor under a patch: %v", err)
50 }
51}
52
53func TestReviewCommentsAreValidatedAndBrowsedWithTheirAnchor(t *testing.T) {
54 ctx := context.Background()
55 _, tenant := testTenant(t)
56 p := tenant.Policy()
57 p.Features.Grasp = true
58 if err := tenant.applyPolicy(ctx, p); err != nil {
59 t.Fatal(err)
60 }
61 owner, _ := event.PublicKey(testOwnerSecret)
62 now := time.Now().Unix()
63 agentTestRepo(t, tenant, "review")
64 coordinate := "30617:" + owner + ":review"
65 pull := signedEvent(t, testOwnerSecret, event.KIND_GIT_PR, now, [][]string{{"a", coordinate}, {"p", owner}, {"subject", "Change"}, {"c", strings.Repeat("1", 40)}, {"clone", "https://example.test/review.git"}}, "Why")
66 if err := publishAs(t, tenant, pull); err != nil {
67 t.Fatalf("pull request rejected: %v", err)
68 }
69 comment := func(createdAt int64, extra ...[]string) event.Event {
70 tags := append([][]string{{"a", coordinate}, {"E", pull.ID, "", owner}, {"K", "1618"}, {"P", owner}, {"e", pull.ID, "", owner}, {"k", "1618"}, {"p", owner}}, extra...)
71 return signedEvent(t, testOwnerSecret, 1111, createdAt, tags, "Review note")
72 }
73 if err := publishAs(t, tenant, comment(now+1, []string{"file", "README"}, []string{"line", "2", "left"})); err == nil || !strings.HasPrefix(err.Error(), "blocked: a review comment") {
74 t.Fatalf("malformed anchor (bad side): %v", err)
75 }
76 if err := publishAs(t, tenant, comment(now+2, []string{"file", "README"}, []string{"line", "2"})); err == nil || !strings.HasPrefix(err.Error(), "blocked: a review comment") {
77 t.Fatalf("malformed anchor (no side): %v", err)
78 }
79 if err := tenant.ingest(ctx, comment(now+3, []string{"file", ""}, []string{"line", "2", "new"}), replication.OriginImport); err == nil || !strings.HasPrefix(err.Error(), "blocked: a review comment") {
80 t.Fatalf("malformed anchor over import: %v", err)
81 }
82 anchored := comment(now+4, []string{"file", "README"}, []string{"line", "2", "new"})
83 if err := publishAs(t, tenant, anchored); err != nil {
84 t.Fatalf("anchored comment rejected: %v", err)
85 }
86 plain := comment(now + 5)
87 if err := publishAs(t, tenant, plain); err != nil {
88 t.Fatalf("plain comment rejected: %v", err)
89 }
90 raw, _ := json.Marshal(map[string]any{"owner": owner, "repo": "review", "event": pull.ID})
91 result, err := tenant.Execute(ctx, "", "browsepull", []json.RawMessage{raw})
92 if err != nil {
93 t.Fatalf("browsepull: %v", err)
94 }
95 encoded, _ := json.Marshal(result)
96 var detail struct {
97 Replies []struct {
98 ID string `json:"id"`
99 File string `json:"file"`
100 Line int `json:"line"`
101 Side string `json:"side"`
102 } `json:"replies"`
103 }
104 if err := json.Unmarshal(encoded, &detail); err != nil || len(detail.Replies) != 2 {
105 t.Fatalf("replies = %s %v", encoded, err)
106 }
107 if detail.Replies[0].ID != plain.ID || detail.Replies[0].File != "" || detail.Replies[0].Line != 0 || detail.Replies[0].Side != "" {
108 t.Fatalf("plain reply carried an anchor: %+v", detail.Replies[0])
109 }
110 if detail.Replies[1].ID != anchored.ID || detail.Replies[1].File != "README" || detail.Replies[1].Line != 2 || detail.Replies[1].Side != "new" {
111 t.Fatalf("anchored reply = %+v", detail.Replies[1])
112 }
113}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs