Federated via graft
4.3 kB
118 lines
1package status
2
3import (
4 "testing"
5 "time"
6
7 "graft/internal/state"
8)
9
10func mkEntry(repoPair, series, kind, url, summary string, t time.Time) state.ActivityEntry {
11 return state.ActivityEntry{
12 Activity: state.Activity{
13 RepoPair: repoPair,
14 Series: series,
15 Kind: kind,
16 URL: url,
17 Summary: summary,
18 },
19 OccurredAt: t,
20 }
21}
22
23// TestBuildSeriesRowsAlignsHealthySides verifies the case this test exists
24// for: two sides that actually hold the same content, but where graft only
25// happened to log the push under one of them, must render the exact same
26// number of cells — a real one on the side that logged it, a synthetic one
27// (visually identical, only the tooltip differs) on the side that didn't.
28func TestBuildSeriesRowsAlignsHealthySides(t *testing.T) {
29 base := time.Date(2026, 9, 13, 12, 0, 0, 0, time.UTC)
30 // Both commits are only ever logged under the Forgejo side's own host
31 // (f1.example) — mirroring the real-world case that motivated this
32 // fix, where the sync code happens to log an event's URL against one
33 // particular host even though the content lands everywhere.
34 entries := []state.ActivityEntry{
35 mkEntry("x-f1", "x", "git", "https://f1.example/x/commit/aaa", "commit aaa", base),
36 mkEntry("x-f1", "x", "git", "https://f1.example/x/commit/bbb", "commit bbb", base.Add(time.Minute)),
37 }
38 topology := map[string][]ServerRef{
39 "x": {
40 {Label: "f1.example", URL: "https://f1.example/x", PairName: "x-f1"},
41 {Label: "r1.example", URL: "https://r1.example/x", Radicle: true, PairName: "x-f1"},
42 },
43 }
44 health := map[string]bool{"x": true}
45
46 rows := buildSeriesRows(entries, health, topology, map[string]PairResult{})
47 if len(rows) != 1 {
48 t.Fatalf("expected 1 series row, got %d", len(rows))
49 }
50 sub := map[string]subRow{}
51 for _, s := range rows[0].SubRows {
52 sub[s.Label] = s
53 }
54 f1, r1 := sub["f1.example"], sub["r1.example"]
55 if len(f1.Events) != len(r1.Events) {
56 t.Fatalf("expected aligned cell counts, got f1=%d r1=%d", len(f1.Events), len(r1.Events))
57 }
58 if len(f1.Events) != 2 {
59 t.Fatalf("expected 2 cells per side, got %d", len(f1.Events))
60 }
61 // f1 logged both itself: both real (State == "").
62 for _, e := range f1.Events {
63 if e.State != "" {
64 t.Errorf("expected f1's own events to be real, got State=%q", e.State)
65 }
66 }
67 // r1 never logged either directly: both synthetic, marked "replicated"
68 // since it's the Radicle side — but same count, same order, so the
69 // two rows read as aligned rather than one looking "behind".
70 for _, e := range r1.Events {
71 if e.State != "replicated" {
72 t.Errorf("expected r1's filled-in events to be synthetic/replicated, got State=%q", e.State)
73 }
74 }
75}
76
77// TestBuildSeriesRowsSkipsFillOnError verifies the other half: a side whose
78// last sync pass actually failed must NOT get the synthetic alignment fill
79// — it should show only what it has confirmed itself, plus a non-empty
80// subRow.Error, so a real problem stays visible instead of being papered
81// over by the alignment logic above.
82func TestBuildSeriesRowsSkipsFillOnError(t *testing.T) {
83 base := time.Date(2026, 9, 13, 12, 0, 0, 0, time.UTC)
84 entries := []state.ActivityEntry{
85 mkEntry("x-f1", "x", "git", "https://f1.example/x/commit/aaa", "commit aaa", base),
86 mkEntry("x-f1", "x", "git", "https://f1.example/x/commit/bbb", "commit bbb", base.Add(time.Minute)),
87 }
88 topology := map[string][]ServerRef{
89 "x": {
90 {Label: "f1.example", URL: "https://f1.example/x", PairName: "x-f1"},
91 {Label: "f2.example", URL: "https://f2.example/x", PairName: "x-f2"},
92 },
93 }
94 health := map[string]bool{"x": false}
95 pairResults := map[string]PairResult{
96 "x-f2": {Name: "x-f2", GitError: "read forgejo head: 502 Bad Gateway"},
97 }
98
99 rows := buildSeriesRows(entries, health, topology, pairResults)
100 sub := map[string]subRow{}
101 for _, s := range rows[0].SubRows {
102 sub[s.Label] = s
103 }
104 f2 := sub["f2.example"]
105 if f2.Error == "" {
106 t.Fatal("expected f2's subRow.Error to be set")
107 }
108 if len(f2.Events) != 0 {
109 t.Fatalf("expected no synthetic fill on an errored side, got %d events", len(f2.Events))
110 }
111 f1 := sub["f1.example"]
112 if f1.Error != "" {
113 t.Fatalf("expected f1 (healthy) to have no error, got %q", f1.Error)
114 }
115 if len(f1.Events) != 2 {
116 t.Fatalf("expected f1's own 2 real events, got %d", len(f1.Events))
117 }
118}