internal/db/kv_test.go (view raw)
1package db
2
3import (
4 "path"
5 "sync"
6 "testing"
7)
8
9// TestKVInc tests that the KVInc function behaves correctly under concurrent access
10func TestKVInc(t *testing.T) {
11 posternDB, err := OpenDB(&Config{
12 DBPath: path.Join(t.TempDir(), "meta.db"),
13 })
14 if err != nil {
15 t.Fatal(err)
16 }
17
18 defer func(posternDB *DB) {
19 err := posternDB.Close()
20 if err != nil {
21 t.Fatal(err)
22 }
23 }(posternDB)
24
25 key := "test-concurrency-key"
26 if err := posternDB.KVSet(t.Context(), key, 0); err != nil {
27 t.Fatalf("KVSet setup failed: %v", err)
28 }
29
30 const numGoroutines = 50
31 const incsPerGoroutine = 200
32
33 var wg sync.WaitGroup
34 wg.Add(numGoroutines)
35
36 for range numGoroutines {
37 go func(db *DB) {
38 defer wg.Done()
39 for range incsPerGoroutine {
40 _, err := db.KVInc(t.Context(), key, 1)
41 if err != nil {
42 t.Errorf("KVInc failed during execution: %v", err)
43 return
44 }
45 }
46 }(posternDB)
47 }
48
49 wg.Wait()
50
51 // Retrieve the final count
52 val, err := posternDB.KVGet(t.Context(), key)
53 if err != nil {
54 t.Fatalf("KVGet failed to fetch final value: %v", err)
55 }
56
57 got, ok := val.(int64)
58 if !ok {
59 t.Fatalf("KVGet returned unexpected type: %T, expected int", val)
60 }
61
62 // Catch race conditions
63 expected := int64(numGoroutines * incsPerGoroutine)
64 if got != expected {
65 t.Errorf("Race condition detected! Expected final value %d, got %d", expected, got)
66 }
67
68 // Check return value of next increment
69 res, err := posternDB.KVInc(t.Context(), key, 1)
70 if err != nil {
71 t.Errorf("KVInc failed during execution: %v", err)
72 return
73 }
74
75 if res != expected+1 {
76 t.Errorf("Incrementation mismatch! Expected final value %d, got %d", expected+1, res)
77 }
78}
79
80// TestKVList tests that the KVList function returns all stored key value pairs
81func TestKVList(t *testing.T) {
82 posternDB, err := OpenDB(&Config{
83 DBPath: path.Join(t.TempDir(), "meta.db"),
84 })
85 if err != nil {
86 t.Fatal(err)
87 }
88
89 defer func(posternDB *DB) {
90 err := posternDB.Close()
91 if err != nil {
92 t.Fatal(err)
93 }
94 }(posternDB)
95
96 want := map[string]any{
97 "key-1": "value-1",
98 "key-2": int64(42),
99 }
100
101 for key, val := range want {
102 if err := posternDB.KVSet(t.Context(), key, val); err != nil {
103 t.Fatalf("KVSet setup failed: %v", err)
104 }
105 }
106
107 got, err := posternDB.KVList(t.Context())
108 if err != nil {
109 t.Fatalf("KVList failed: %v", err)
110 }
111
112 if len(got) != len(want) {
113 t.Fatalf("Expected %d pairs, got %d", len(want), len(got))
114 }
115
116 for key, expected := range want {
117 val, ok := got[key]
118 if !ok {
119 t.Errorf("Missing key %q in KVList result", key)
120 continue
121 }
122 if val != expected {
123 t.Errorf("Key %q: expected value %v, got %v", key, expected, val)
124 }
125 }
126}
127
128// TestKVIncString tests that the KVInc function does not increment invalid values
129func TestKVIncString(t *testing.T) {
130 posternDB, err := OpenDB(&Config{
131 DBPath: path.Join(t.TempDir(), "meta.db"),
132 })
133 if err != nil {
134 t.Fatal(err)
135 }
136
137 defer func(posternDB *DB) {
138 err := posternDB.Close()
139 if err != nil {
140 t.Fatal(err)
141 }
142 }(posternDB)
143
144 key := "test-key"
145 if err := posternDB.KVSet(t.Context(), key, "value"); err != nil {
146 t.Fatalf("KVSet setup failed: %v", err)
147 }
148
149 _, err = posternDB.KVInc(t.Context(), key, 1)
150 if err == nil {
151 t.Fatalf("KVInc should have failed during execution due to attempt incrementing a non-integer value")
152 }
153}
154
155// TestKVDelete tests the KVDelete function
156func TestKVDelete(t *testing.T) {
157 posternDB, err := OpenDB(&Config{
158 DBPath: path.Join(t.TempDir(), "meta.db"),
159 })
160 if err != nil {
161 t.Fatal(err)
162 }
163
164 defer func(posternDB *DB) {
165 err := posternDB.Close()
166 if err != nil {
167 t.Fatal(err)
168 }
169 }(posternDB)
170
171 key := "test-key"
172 var testVal int64 = 42
173 if err := posternDB.KVSet(t.Context(), key, testVal); err != nil {
174 t.Fatalf("KVSet setup failed: %v", err)
175 }
176
177 val, err := posternDB.KVPop(t.Context(), key)
178 if err != nil {
179 t.Fatalf("KVGet failed to fetch final value: %v", err)
180 }
181 if val != testVal {
182 t.Errorf("Expected value %d, got %d", testVal, val)
183 }
184
185 _, err = posternDB.KVPop(t.Context(), key)
186 if err == nil {
187 t.Fatalf("KVDelete should have failed during execution due to missing key")
188 }
189}