Commit af0982ad6e
af0982ad6edb02b2633f564cb07c0ceb653cfd97
parent: 25ce05686e
Verified · cmc ci/build: success ci/test: success
cmc <hello@cleberg.net> · 2026-10-14 05:13 UTC
show: mark system comments with kind in --json
A line the server posted reaches --json with author "system", which an
account can also be named, and react refuses it. Comments carry no kind,
so their wire shape is unchanged.
Closes #332
Layout: unified · split
internal/control/commentkind_test.go
added
+51
| @@ -0,0 +1,51 @@ |
| |
1 | package control |
| |
2 | |
| |
3 | import ( |
| |
4 | "bytes" |
| |
5 | "encoding/json" |
| |
6 | "testing" |
| |
7 | |
| |
8 | "gitbay.org/gitbay/internal/protocol" |
| |
9 | "gitbay.org/gitbay/internal/store" |
| |
10 | ) |
| |
11 | |
| |
12 | // A line the server posted (a label change, a retarget) says so in |
| |
13 | // --json; a client that offers reactions must not offer them on one, since |
| |
14 | // issue react and mr react refuse it. A comment carries no kind, so the |
| |
15 | // wire shape of every existing comment is unchanged. |
| |
16 | func TestShowMarksSystemComments(t *testing.T) { |
| |
17 | st, repo, uid := newQueueTestRepo(t) |
| |
18 | id, err := st.CreateIssue(repo.ID, uid, "t", "", "md") |
| |
19 | if err != nil { |
| |
20 | t.Fatal(err) |
| |
21 | } |
| |
22 | if err := st.AddIssueComment(id, uid, "me too", "md"); err != nil { |
| |
23 | t.Fatal(err) |
| |
24 | } |
| |
25 | if err := st.AddIssueSystemComment(id, uid, "added label bug"); err != nil { |
| |
26 | t.Fatal(err) |
| |
27 | } |
| |
28 | c, errOut := pruneCtx(st, t.TempDir(), store.User{ID: uid, Username: "alice"}) |
| |
29 | c.JSON = true |
| |
30 | if code := Dispatch(c, []string{"issue", "show", repo.Path(), "1"}); code != protocol.ExitOK { |
| |
31 | t.Fatalf("exit %d: %s", code, errOut) |
| |
32 | } |
| |
33 | var env struct { |
| |
34 | Data struct { |
| |
35 | Comments []map[string]any `json:"comments"` |
| |
36 | } `json:"data"` |
| |
37 | } |
| |
38 | if err := json.Unmarshal(c.Stdout.(*bytes.Buffer).Bytes(), &env); err != nil { |
| |
39 | t.Fatal(err) |
| |
40 | } |
| |
41 | cs := env.Data.Comments |
| |
42 | if len(cs) != 2 { |
| |
43 | t.Fatalf("comments: %v", cs) |
| |
44 | } |
| |
45 | if _, ok := cs[0]["kind"]; ok { |
| |
46 | t.Errorf("a comment carries a kind: %v", cs[0]) |
| |
47 | } |
| |
48 | if cs[1]["kind"] != "system" { |
| |
49 | t.Errorf("a system line is not marked: %v", cs[1]) |
| |
50 | } |
| |
51 | } |
internal/control/issue.go
+1 −1
| @@ -381,7 +381,7 @@ func runIssueShow(c *Ctx, args []string) int { |
| 381 | } |
381 | } |
| 382 | var cs []commentOut |
382 | var cs []commentOut |
| 383 | for _, cm := range comments { |
383 | for _, cm := range comments { |
| 384 | cs = append(cs, commentOut{cm.ID, cm.Author, cm.Body, cm.BodyFormat, cm.CreatedAt, cm.Kind, reactionsOut(rx[cm.ID])}) |
384 | cs = append(cs, commentOut{cm.ID, cm.Author, cm.Body, cm.BodyFormat, cm.CreatedAt, wireKind(cm.Kind), reactionsOut(rx[cm.ID])}) |
| 385 | } |
385 | } |
| 386 | d := IssueShow{issueOut: issueToOut(issue, true), Reactions: reactionsOut(rx[0]), Comments: cs} |
386 | d := IssueShow{issueOut: issueToOut(issue, true), Reactions: reactionsOut(rx[0]), Comments: cs} |
| 387 | canWrite := false |
387 | canWrite := false |
internal/control/mr.go
+1 −1
| @@ -788,7 +788,7 @@ func runMRShow(c *Ctx, args []string) int { |
| 788 | } |
788 | } |
| 789 | var cs []commentOut |
789 | var cs []commentOut |
| 790 | for _, cm := range comments { |
790 | for _, cm := range comments { |
| 791 | cs = append(cs, commentOut{cm.ID, cm.Author, cm.Body, cm.BodyFormat, cm.CreatedAt, cm.Kind, reactionsOut(rx[cm.ID])}) |
791 | cs = append(cs, commentOut{cm.ID, cm.Author, cm.Body, cm.BodyFormat, cm.CreatedAt, wireKind(cm.Kind), reactionsOut(rx[cm.ID])}) |
| 792 | } |
792 | } |
| 793 | var rs []ReviewOut |
793 | var rs []ReviewOut |
| 794 | counts := ReviewersWhoCount(c.Store, repo, reviews) |
794 | counts := ReviewersWhoCount(c.Store, repo, reviews) |
internal/control/thread.go
+17 −7
| @@ -50,13 +50,23 @@ func authorOrWrite(c *Ctx, repo store.Repo, author, what string) int { |
| 50 | |
50 | |
| 51 | // commentOut is one comment as show emits it, for both nouns. |
51 | // commentOut is one comment as show emits it, for both nouns. |
| 52 | type commentOut struct { |
52 | type commentOut struct { |
| 53 | ID int64 `json:"id"` |
53 | ID int64 `json:"id"` |
| 54 | Author string `json:"author"` |
54 | Author string `json:"author"` |
| 55 | Body string `json:"body"` |
55 | Body string `json:"body"` |
| 56 | BodyFormat string `json:"body_format,omitempty"` |
56 | BodyFormat string `json:"body_format,omitempty"` |
| 57 | CreatedAt string `json:"created_at"` |
57 | CreatedAt string `json:"created_at"` |
| 58 | Kind string `json:"-"` // "comment" or "system"; not part of the wire shape |
58 | // Kind is "system" for a line the server posted and "" for a comment, |
| 59 | Reactions []ReactionOut `json:"reactions,omitempty"` |
59 | // so the wire shape of a comment is unchanged. |
| |
60 | Kind string `json:"kind,omitempty"` |
| |
61 | Reactions []ReactionOut `json:"reactions,omitempty"` |
| |
62 | } |
| |
63 | |
| |
64 | // wireKind is the stored comment kind as commentOut carries it. |
| |
65 | func wireKind(kind string) string { |
| |
66 | if kind == "system" { |
| |
67 | return kind |
| |
68 | } |
| |
69 | return "" |
| 60 | } |
70 | } |
| 61 | |
71 | |
| 62 | // thread is what a comment command needs to know about its noun. |
72 | // thread is what a comment command needs to know about its noun. |