Skip to content

Commit e4b367d

Browse files
authored
Create tableby_test.go
table: add TestSanityTableBy to validate QueryBy and DeleteBy behavior - Verify that QueryBy and DeleteBy panic on nil or empty filter maps - Exercise multi‑bucket setup via Insert and InsertHoles, including hole rows - Test QueryBy for no‑match (nil), single‑column, and multi‑column filters - Ensure QueryBy does not mutate underlying table state - Test DeleteBy for correct deletion counts, overlapping filters, and idempotence - Perform full cleanup and confirm table emptiness after successive DeleteBy calls
1 parent cea4331 commit e4b367d

File tree

1 file changed

+120
-0
lines changed

1 file changed

+120
-0
lines changed

tableby_test.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package table
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestSanityTableBy(t *testing.T) {
9+
// helper to assert panic
10+
expectsPanic := func(name string, fn func()) {
11+
t.Helper()
12+
defer func() {
13+
if r := recover(); r == nil {
14+
t.Errorf("%s: expected panic, but none occurred", name)
15+
}
16+
}()
17+
fn()
18+
}
19+
20+
// 1. QueryBy should panic on nil or empty filters
21+
expectsPanic("QueryBy(nil)", func() { (&Table{}).QueryBy(nil) })
22+
expectsPanic("QueryBy(empty)", func() { (&Table{}).QueryBy(map[int]string{}) })
23+
24+
// 2. DeleteBy should panic on nil or empty filters
25+
expectsPanic("DeleteBy(nil)", func() { (&Table{}).DeleteBy(nil) })
26+
expectsPanic("DeleteBy(empty)", func() { (&Table{}).DeleteBy(map[int]string{}) })
27+
28+
// Prepare a table and multi-bucket scenario
29+
tbl := &Table{}
30+
// bucket1: simple two-column rows
31+
tbl.Insert([][]string{
32+
{"u1", "admin", "active"},
33+
{"u2", "member", "inactive"},
34+
{"u3", "admin", "inactive"},
35+
})
36+
// bucket2: overlapping and new data, including holes
37+
tbl.InsertHoles([][]string{
38+
{"u4", "member", "active"},
39+
nil, // hole row
40+
{"u5", "guest", "active"},
41+
})
42+
// bucket3: more data
43+
tbl.Insert([][]string{
44+
{"u6", "guest", "inactive"},
45+
{"u7", "admin", "active"},
46+
})
47+
48+
// 3. QueryBy no-match returns nil
49+
if got := tbl.QueryBy(map[int]string{0: "noone"}); got != nil {
50+
t.Errorf("QueryBy(no-match) = %v; want nil", got)
51+
}
52+
53+
// 4. QueryBy single-col matches across buckets
54+
adminRows := tbl.QueryBy(map[int]string{1: "admin"})
55+
if len(adminRows) != 3 {
56+
t.Fatalf("QueryBy(admin) count = %d; want 3", len(adminRows))
57+
}
58+
// Verify all returned rows have "admin" in col 1
59+
for _, r := range adminRows {
60+
if r[1] != "admin" {
61+
t.Errorf("QueryBy(admin) returned non-admin row %v", r)
62+
}
63+
}
64+
65+
// 5. QueryBy multi-col filter
66+
activeAdmin := tbl.QueryBy(map[int]string{1: "admin", 2: "active"})
67+
want := [][]string{{"u1", "admin", "active"}, {"u7", "admin", "active"}}
68+
if !reflect.DeepEqual(activeAdmin, want) {
69+
t.Errorf("QueryBy(admin+active) = %v; want %v", activeAdmin, want)
70+
}
71+
72+
// 6. Ensure QueryBy does not mutate underlying data
73+
_ = tbl.QueryBy(map[int]string{2: "inactive"})
74+
allBefore := tbl.All()
75+
tbl.QueryBy(map[int]string{2: "inactive"})
76+
allAfter := tbl.All()
77+
if !reflect.DeepEqual(allBefore, allAfter) {
78+
t.Errorf("QueryBy mutated table: before=%v after=%v", allBefore, allAfter)
79+
}
80+
81+
// 7. DeleteBy removes matching rows
82+
deleted := tbl.DeleteBy(map[int]string{2: "inactive"})
83+
// We had u2, u3, u6 as inactive → 3 deletions
84+
if deleted != 3 {
85+
t.Errorf("DeleteBy(inactive) deleted %d; want 3", deleted)
86+
}
87+
// QueryBy should no longer return any inactive
88+
if got := tbl.QueryBy(map[int]string{2: "inactive"}); got != nil {
89+
t.Errorf("after DeleteBy, QueryBy(inactive) = %v; want nil", got)
90+
}
91+
// Count of any active should remain
92+
if got := tbl.QueryBy(map[int]string{2: "active"}); len(got) == 0 {
93+
t.Error("after DeleteBy(inactive), no active rows found; expected some")
94+
}
95+
96+
// 8. DeleteBy on overlapping filters
97+
// Remove everyone with role="admin"
98+
del2 := tbl.DeleteBy(map[int]string{1: "admin"})
99+
// Only u1 and u7 were admin+active originally; they should now be deleted
100+
if del2 != 2 {
101+
t.Errorf("DeleteBy(admin) deleted %d; want 2", del2)
102+
}
103+
remaining := tbl.All()
104+
// Remaining should be only non-admin, non-inactive: u4 and u5 (and possibly holes already filtered)
105+
for _, r := range remaining {
106+
if r[1] == "admin" || r[2] == "inactive" {
107+
t.Errorf("bad remaining row after DeleteBy: %v", r)
108+
}
109+
}
110+
111+
// 9. DeleteBy complete cleanup
112+
// Now delete all remaining active members
113+
del3 := tbl.DeleteBy(map[int]string{2: "active"})
114+
if del3 == 0 {
115+
t.Errorf("DeleteBy(active) deleted %d; expected >0", del3)
116+
}
117+
if any := tbl.All(); len(any) != 0 {
118+
t.Errorf("table not empty after final DeleteBy: %v", any)
119+
}
120+
}

0 commit comments

Comments
 (0)