oficina / sheets Public

Clone
git clone https://amadan.net/oficina/sheets

Plain git — no account needed to clone.

Download

Download this file

1package store_test
2
3import (
4 "amadan.net/oficina/sheets/internal/sheets/store"
5 "errors"
6 "gorm.io/gorm"
7 "testing"
8)
9
10func TestBatchVersionsSurviveClearingAndReopen(t *testing.T) {
11 db, s, id := seeded(t)
12 h, e := store.Open(db, s, id)
13 if e != nil {
14 t.Fatal(e)
15 }
16 jobs := make([]store.CellWrite, 500)
17 for i := range jobs {
18 jobs[i] = store.CellWrite{Row: i, Col: 1, Raw: "1"}
19 }
20 a, e := h.SetBatch(jobs, actor)
21 if e != nil {
22 t.Fatal(e)
23 }
24 h.Release()
25 h, e = store.Open(db, s, id)
26 if e != nil {
27 t.Fatal(e)
28 }
29 for i := range jobs {
30 jobs[i].Raw = ""
31 }
32 b, e := h.SetBatch(jobs, actor)
33 if e != nil {
34 t.Fatal(e)
35 }
36 h.Release()
37 h, e = store.Open(db, s, id)
38 if e != nil {
39 t.Fatal(e)
40 }
41 defer h.Release()
42 c, e := h.SetBatch([]store.CellWrite{{Row: 0, Col: 1, Raw: "2"}}, actor)
43 if e != nil {
44 t.Fatal(e)
45 }
46 if c.Version <= b.Version {
47 t.Fatalf("version regression: paste=%d clear=%d next edit=%d; browser discards latest edit", a.Version, b.Version, c.Version)
48 }
49}
50
51func TestSingleCellVersionSurvivesLastCellClear(t *testing.T) {
52 db, s, id := seeded(t)
53 h, err := store.Open(db, s, id)
54 if err != nil {
55 t.Fatal(err)
56 }
57 first, err := h.SetReport(rowA, colA, "8", actor)
58 if err != nil {
59 t.Fatal(err)
60 }
61 h.Release()
62 h, err = store.Open(db, s, id)
63 if err != nil {
64 t.Fatal(err)
65 }
66 cleared, err := h.SetReport(rowA, colA, "", actor)
67 if err != nil {
68 t.Fatal(err)
69 }
70 h.Release()
71 h, err = store.Open(db, s, id)
72 if err != nil {
73 t.Fatal(err)
74 }
75 defer h.Release()
76 next, err := h.SetReport(rowA, colA, "9", actor)
77 if err != nil {
78 t.Fatal(err)
79 }
80 if cleared.Version <= first.Version || next.Version <= cleared.Version {
81 t.Fatalf("versions regressed: first=%d cleared=%d next=%d", first.Version, cleared.Version, next.Version)
82 }
83}
84
85func TestBlankCrashRetainsCommittedVersion(t *testing.T) {
86 db, s, id := seeded(t)
87 h, err := store.Open(db, s, id)
88 if err != nil {
89 t.Fatal(err)
90 }
91 allocated := h.Version()
92 reset := store.InterruptBlankAfterFirstTransaction(func() error { return errors.New("simulated crash") })
93 _, err = h.SetReport(rowA, colA, "", actor)
94 reset()
95 h.Release()
96 if err == nil {
97 t.Fatal("crash injection failed")
98 }
99 h, err = store.Open(db, s, id)
100 if err != nil {
101 t.Fatal(err)
102 }
103 defer h.Release()
104 next, err := h.SetReport(rowA, colA, "9", actor)
105 if err != nil {
106 t.Fatal(err)
107 }
108 if next.Version <= allocated {
109 t.Fatalf("committed blank stamp=%d next=%d", allocated, next.Version)
110 }
111}
112
113func TestSnapshotVersionSurvivesReopen(t *testing.T) {
114 for _, clear := range []bool{false, true} {
115 t.Run(map[bool]string{false: "populated", true: "cleared"}[clear], func(t *testing.T) {
116 db, s, id := seeded(t)
117 h, err := store.Open(db, s, id)
118 if err != nil {
119 t.Fatal(err)
120 }
121 if clear {
122 if err = h.Set(rowA, colA, "", actor); err != nil {
123 t.Fatal(err)
124 }
125 }
126 snapshot := h.Version()
127 h.Release()
128 h, err = store.Open(db, s, id)
129 if err != nil {
130 t.Fatal(err)
131 }
132 defer h.Release()
133 rep, err := h.SetReport(rowA, colA, "9", actor)
134 if err != nil {
135 t.Fatal(err)
136 }
137 if rep.Version < snapshot {
138 t.Fatalf("snapshot %d exceeds new edit %d", snapshot, rep.Version)
139 }
140 })
141 }
142}
143
144func TestRetainedHandleBatchRollbackKeepsDurableVersion(t *testing.T) {
145 db, s, id := seeded(t)
146 h, err := store.Open(db, s, id)
147 if err != nil {
148 t.Fatal(err)
149 }
150 if err = h.Set(rowA, colA, "", actor); err != nil {
151 t.Fatal(err)
152 }
153 var before uint64
154 if err = db.Raw("SELECT content_version FROM tabs WHERE id = ?", id).Scan(&before).Error; err != nil {
155 t.Fatal(err)
156 }
157 if h.Version() <= before {
158 t.Fatal("fixture did not retain an unused stamp")
159 }
160 rep, err := h.SetBatch([]store.CellWrite{{Row: 0, Col: 0, Raw: "8"}}, actor)
161 if err != nil {
162 t.Fatal(err)
163 }
164 if err = db.Raw("SELECT content_version FROM tabs WHERE id = ?", id).Scan(&before).Error; err != nil {
165 t.Fatal(err)
166 }
167 calls := 0
168 if err = db.Callback().Create().Before("gorm:create").Register("watermark-failure", func(tx *gorm.DB) {
169 if tx.Statement.Table == "events" {
170 calls++
171 if calls == 3 {
172 tx.AddError(errors.New("injected failure"))
173 }
174 }
175 }); err != nil {
176 t.Fatal(err)
177 }
178 _, err = h.SetBatch([]store.CellWrite{{Row: 0, Col: 0, Raw: ""}, {Row: 0, Col: 1, Raw: "9"}}, actor)
179 db.Callback().Create().Remove("watermark-failure")
180 if err == nil {
181 t.Fatal("batch failure not injected")
182 }
183 var after uint64
184 if err = db.Raw("SELECT content_version FROM tabs WHERE id = ?", id).Scan(&after).Error; err != nil {
185 t.Fatal(err)
186 }
187 if after != before {
188 t.Fatalf("watermark survived rollback: %d -> %d", before, after)
189 }
190 h.Release()
191 h, err = store.Open(db, s, id)
192 if err != nil {
193 t.Fatal(err)
194 }
195 defer h.Release()
196 next, err := h.SetReport(rowA, colA, "10", actor)
197 if err != nil {
198 t.Fatal(err)
199 }
200 if next.Version <= rep.Version {
201 t.Fatalf("last successful %d >= reopened edit %d", rep.Version, next.Version)
202 }
203}
204