okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

dawnadd charm tui skillf7fcae1

main
3.8 KiB149 linesraw
1package contracts
2
3import (
4	"errors"
5	"reflect"
6	"testing"
7)
8
9func TestOldResultRejectedBeforeNewDebounce(t *testing.T) {
10	var s searchState
11	a := s.change("a")
12	if !s.begin(a) {
13		t.Fatal("first request not admitted")
14	}
15	b := s.change("b")
16	before := s
17	if s.finish(a, []string{"old"}, nil) {
18		t.Fatal("old result accepted during new debounce")
19	}
20	if !reflect.DeepEqual(before, s) {
21		t.Fatal("rejected result mutated state")
22	}
23	if !s.begin(b) || !s.finish(b, []string{"new"}, nil) {
24		t.Fatal("new request failed")
25	}
26	if s.phase != ready || !reflect.DeepEqual(s.rows, []string{"new"}) {
27		t.Fatalf("unexpected final state: %+v", s)
28	}
29}
30
31func TestStaleFailureCannotOverwriteSuccess(t *testing.T) {
32	var s searchState
33	a := s.change("a")
34	s.begin(a)
35	b := s.change("b")
36	s.begin(b)
37	s.finish(b, []string{"new"}, nil)
38	before := s
39	if s.finish(a, nil, errors.New("old failure")) || !reflect.DeepEqual(before, s) {
40		t.Fatal("stale failure changed successful state")
41	}
42}
43
44func TestStaleCompletionCannotStopNewLoading(t *testing.T) {
45	var s searchState
46	a := s.change("a")
47	s.begin(a)
48	b := s.change("b")
49	s.begin(b)
50	if s.finish(a, nil, nil) || s.phase != loading {
51		t.Fatal("stale completion stopped the new loading state")
52	}
53}
54
55func TestOldDebounceRejected(t *testing.T) {
56	var s searchState
57	a := s.change("a")
58	b := s.change("b")
59	if s.begin(a) || !s.begin(b) {
60		t.Fatal("incorrect debounce ownership")
61	}
62}
63
64func TestDuplicateBeginAndCompletionRejected(t *testing.T) {
65	var s searchState
66	g := s.change("query")
67	if !s.begin(g) || s.begin(g) {
68		t.Fatal("request admission is not single-use")
69	}
70	if !s.finish(g, []string{"first"}, nil) || s.finish(g, []string{"duplicate"}, nil) {
71		t.Fatal("request completion is not single-use")
72	}
73}
74
75func TestSameQueryRetryGetsNewIdentity(t *testing.T) {
76	var s searchState
77	a := s.change("query")
78	s.begin(a)
79	s.finish(a, nil, errors.New("transient"))
80	b := s.change("query")
81	if b == a || s.err != nil || !s.begin(b) || !s.finish(b, []string{"ok"}, nil) {
82		t.Fatalf("retry failed: %+v", s)
83	}
84}
85
86func TestEmptyResultIsReadyNotFailure(t *testing.T) {
87	var s searchState
88	g := s.change("no matches")
89	s.begin(g)
90	if !s.finish(g, nil, nil) || s.phase != ready || s.err != nil || len(s.rows) != 0 {
91		t.Fatalf("empty successful result misclassified: %+v", s)
92	}
93}
94
95func TestStopRejectsCompletions(t *testing.T) {
96	var s searchState
97	g := s.change("query")
98	s.begin(g)
99	s.stop()
100	if s.finish(g, []string{"late"}, nil) || s.phase != stopped {
101		t.Fatal("stopped owner accepted completion")
102	}
103}
104
105func TestAcceptedRowsDoNotAliasCallerSlice(t *testing.T) {
106	var s searchState
107	g := s.change("query")
108	s.begin(g)
109	rows := []string{"before"}
110	s.finish(g, rows, nil)
111	rows[0] = "after" // Sequential mutation, not a concurrent ownership test.
112	if s.rows[0] != "before" {
113		t.Fatal("stored rows alias caller slice")
114	}
115}
116
117func TestResultBeforeBeginRejected(t *testing.T) {
118	var s searchState
119	g := s.change("query")
120	if s.finish(g, []string{"unexpected"}, nil) || s.phase != debouncing {
121		t.Fatal("accepted a result before admitting its request")
122	}
123}
124
125func TestBodyRows(t *testing.T) {
126	cases := []struct {
127		name                  string
128		total, header, footer int
129		want                  int
130		compact               bool
131	}{
132		{"normal", 24, 3, 2, 19, false},
133		{"exact", 5, 3, 2, 0, false},
134		{"header-too-tall", 2, 3, 1, 0, true},
135		{"footer-too-tall", 5, 3, 3, 0, true},
136		{"zero-with-chrome", 0, 1, 1, 0, true},
137		{"zero-empty", 0, 0, 0, 0, false},
138		{"negative-size", -1, 1, 1, 0, true},
139		{"negative-chrome", 5, -2, -3, 5, false},
140	}
141	for _, tc := range cases {
142		t.Run(tc.name, func(t *testing.T) {
143			got, compact := bodyRows(tc.total, tc.header, tc.footer)
144			if got != tc.want || compact != tc.compact {
145				t.Fatalf("got (%d, %v), want (%d, %v)", got, compact, tc.want, tc.compact)
146			}
147		})
148	}
149}