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// TestKVIncString tests that the KVInc function does not increment invalid values
81func TestKVIncString(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 key := "test-key"
97 if err := posternDB.KVSet(t.Context(), key, "value"); err != nil {
98 t.Fatalf("KVSet setup failed: %v", err)
99 }
100
101 _, err = posternDB.KVInc(t.Context(), key, 1)
102 if err == nil {
103 t.Fatalf("KVInc should have failed during execution due to attempt incrementing a non-integer value")
104 }
105}
106
107// TestKVDelete tests the KVDelete function
108func TestKVDelete(t *testing.T) {
109 posternDB, err := OpenDB(&Config{
110 DBPath: path.Join(t.TempDir(), "meta.db"),
111 })
112 if err != nil {
113 t.Fatal(err)
114 }
115
116 defer func(posternDB *DB) {
117 err := posternDB.Close()
118 if err != nil {
119 t.Fatal(err)
120 }
121 }(posternDB)
122
123 key := "test-key"
124 var testVal int64 = 42
125 if err := posternDB.KVSet(t.Context(), key, testVal); err != nil {
126 t.Fatalf("KVSet setup failed: %v", err)
127 }
128
129 val, err := posternDB.KVPop(t.Context(), key)
130 if err != nil {
131 t.Fatalf("KVGet failed to fetch final value: %v", err)
132 }
133 if val != testVal {
134 t.Errorf("Expected value %d, got %d", testVal, val)
135 }
136
137 _, err = posternDB.KVPop(t.Context(), key)
138 if err == nil {
139 t.Fatalf("KVDelete should have failed during execution due to missing key")
140 }
141}