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 "math"
5 "strconv"
6 "testing"
7
8 "amadan.net/oficina/sheets/internal/sheets/store"
9)
10
11func TestVersionWatermarkNeverLowersAndRequiresTab(t *testing.T) {
12 db, _, id := seeded(t)
13 if err := store.AdvanceContentVersionForTest(db, id, 100); err != nil {
14 t.Fatal(err)
15 }
16 if err := store.AdvanceContentVersionForTest(db, id, 4); err != nil {
17 t.Fatal(err)
18 }
19 var mark uint64
20 if err := db.Raw("SELECT content_version FROM tabs WHERE id = ?", id).Scan(&mark).Error; err != nil {
21 t.Fatal(err)
22 }
23 if mark != 100 {
24 t.Fatalf("watermark lowered to %d", mark)
25 }
26 if err := store.AdvanceContentVersionForTest(db, id+1000, 4); err == nil {
27 t.Fatal("missing tab accepted")
28 }
29}
30
31func TestOpenRequiresTabVersionRow(t *testing.T) {
32 db, s, id := seeded(t)
33 h, err := store.Open(db, s, id+1000)
34 if err == nil {
35 h.Release()
36 t.Fatal("missing tab opened")
37 }
38}
39
40func TestMissingTabRefusesRetainedHandleWrite(t *testing.T) {
41 db, s, id := seeded(t)
42 h, err := store.Open(db, s, id)
43 if err != nil {
44 t.Fatal(err)
45 }
46 defer h.Release()
47 if err = db.Delete(&store.Tab{}, id).Error; err != nil {
48 t.Fatal(err)
49 }
50 if _, err = h.SetReport(rowA, colA, "9", actor); err == nil {
51 t.Fatal("write accepted without version row")
52 }
53 if h.Value(rowA, colA).Num != 5 {
54 t.Fatal("failed write changed graph")
55 }
56}
57
58func TestHighWatermarkSurvivesEmptyTabOpen(t *testing.T) {
59 db, s, id := seeded(t)
60 if err := db.Where("tab_id = ?", id).Delete(&store.Cell{}).Error; err != nil {
61 t.Fatal(err)
62 }
63 const high = math.MaxInt64 - 10
64 if err := store.AdvanceContentVersionForTest(db, id, high); err != nil {
65 t.Fatal(err)
66 }
67 h, err := store.Open(db, s, id)
68 if err != nil {
69 t.Fatal(err)
70 }
71 defer h.Release()
72 rep, err := h.SetReport(rowA, colA, "9", actor)
73 if err != nil {
74 t.Fatal(err)
75 }
76 if rep.Version <= high {
77 t.Fatalf("high watermark lost: %d", rep.Version)
78 }
79}
80
81func TestVersionExhaustionRefusesWithoutMutation(t *testing.T) {
82 for _, mark := range []int64{math.MaxInt64, math.MaxInt64 - 1, -1} {
83 t.Run(strconv.FormatInt(mark, 10), func(t *testing.T) {
84 db, s, id := seeded(t)
85 if err := db.Model(&store.Tab{}).Where("id = ?", id).Update("content_version", mark).Error; err != nil {
86 t.Fatal(err)
87 }
88 h, err := store.Open(db, s, id)
89 if err == nil {
90 h.Release()
91 t.Fatal("invalid watermark opened")
92 }
93 })
94 }
95 for _, batch := range []bool{false, true} {
96 t.Run(map[bool]string{false: "single", true: "batch"}[batch], func(t *testing.T) {
97 db, s, id := seeded(t)
98 h, err := store.Open(db, s, id)
99 if err != nil {
100 t.Fatal(err)
101 }
102 defer h.Release()
103 var before store.Tab
104 if err = db.First(&before, id).Error; err != nil {
105 t.Fatal(err)
106 }
107 store.SetNextVersionForTest(h, math.MaxInt64)
108 if batch {
109 _, err = h.SetBatch([]store.CellWrite{{Row: 0, Col: 0, Raw: "9"}}, actor)
110 } else {
111 _, err = h.SetReport(rowA, colA, "9", actor)
112 }
113 if err == nil {
114 t.Fatal("exhausted allocation accepted")
115 }
116 var after store.Tab
117 if err = db.First(&after, id).Error; err != nil {
118 t.Fatal(err)
119 }
120 if after.ContentVersion != before.ContentVersion || h.Value(rowA, colA).Num != 5 {
121 t.Fatal("exhausted write mutated state")
122 }
123 })
124 }
125}
126
127func TestFormulaRecoveryVersionSurvivesClearingNewestCaches(t *testing.T) {
128 db, s, id := seeded(t)
129 h, err := store.Open(db, s, id)
130 if err != nil {
131 t.Fatal(err)
132 }
133 if err = h.Set(rowB, colA, "=A1*2", actor); err != nil {
134 t.Fatal(err)
135 }
136 if err = h.Set(rowA, colA, "8", actor); err != nil {
137 t.Fatal(err)
138 }
139 if h.Value(rowB, colA).Num != 16 {
140 t.Fatal("formula did not recalculate")
141 }
142 h.Release()
143 if err = db.Model(&store.Cell{}).Where("tab_id = ? AND row_id = ?", id, rowB).Update("cache_version", 0).Error; err != nil {
144 t.Fatal(err)
145 }
146 h, err = store.Open(db, s, id)
147 if err != nil {
148 t.Fatal(err)
149 }
150 var tab store.Tab
151 if err = db.First(&tab, id).Error; err != nil {
152 t.Fatal(err)
153 }
154 var cell store.Cell
155 if err = db.Where("tab_id = ? AND row_id = ?", id, rowB).First(&cell).Error; err != nil {
156 t.Fatal(err)
157 }
158 if tab.ContentVersion < cell.CacheVersion {
159 t.Fatal("recovery stamp not retained")
160 }
161 if err = h.Set(rowA, colA, "", actor); err != nil {
162 t.Fatal(err)
163 }
164 if h.Value(rowB, colA).Num != 0 {
165 t.Fatal("blank feeder did not recalculate")
166 }
167 last, err := h.SetReport(rowB, colA, "", actor)
168 if err != nil {
169 t.Fatal(err)
170 }
171 h.Release()
172 h, err = store.Open(db, s, id)
173 if err != nil {
174 t.Fatal(err)
175 }
176 defer h.Release()
177 next, err := h.SetReport(rowA, colA, "9", actor)
178 if err != nil {
179 t.Fatal(err)
180 }
181 if next.Version <= last.Version {
182 t.Fatal("deleting recovered cells lost watermark")
183 }
184}
185