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 posternDB.Close()
19
20 key := "test-concurrency-key"
21 if err := posternDB.KVSet(t.Context(), key, 0); err != nil {
22 t.Fatalf("KVSet setup failed: %v", err)
23 }
24
25 const numGoroutines = 50
26 const incsPerGoroutine = 200
27
28 var wg sync.WaitGroup
29 wg.Add(numGoroutines)
30
31 for range numGoroutines {
32 go func(db *DB) {
33 defer wg.Done()
34 for range incsPerGoroutine {
35 _, err := db.KVInc(t.Context(), key, 1)
36 if err != nil {
37 t.Errorf("KVInc failed during execution: %v", err)
38 return
39 }
40 }
41 }(posternDB)
42 }
43
44 wg.Wait()
45
46 // Retrieve the final count
47 val, err := posternDB.KVGet(t.Context(), key)
48 if err != nil {
49 t.Fatalf("KVGet failed to fetch final value: %v", err)
50 }
51
52 got, ok := val.(int64)
53 if !ok {
54 t.Fatalf("KVGet returned unexpected type: %T, expected int", val)
55 }
56
57 // Catch race conditions
58 expected := int64(numGoroutines * incsPerGoroutine)
59 if got != expected {
60 t.Errorf("Race condition detected! Expected final value %d, got %d", expected, got)
61 }
62
63 // Check return value of next increment
64 res, err := posternDB.KVInc(t.Context(), key, 1)
65 if err != nil {
66 t.Errorf("KVInc failed during execution: %v", err)
67 return
68 }
69
70 if res != expected+1 {
71 t.Errorf("Incrementation mismatch! Expected final value %d, got %d", expected+1, res)
72 }
73}
74
75// TestKVIncString tests that the KVInc function does not increment invalid values
76func TestKVIncString(t *testing.T) {
77 posternDB, err := OpenDB(&Config{
78 DBPath: path.Join(t.TempDir(), "meta.db"),
79 })
80 if err != nil {
81 t.Fatal(err)
82 }
83
84 defer posternDB.Close()
85
86 key := "test-key"
87 if err := posternDB.KVSet(t.Context(), key, "value"); err != nil {
88 t.Fatalf("KVSet setup failed: %v", err)
89 }
90
91 _, err = posternDB.KVInc(t.Context(), key, 1)
92 if err == nil {
93 t.Fatalf("KVInc should have failed during execution due to attempt incrementing a non-integer value")
94 }
95}
96
97// TestKVDelete tests the KVDelete function
98func TestKVDelete(t *testing.T) {
99 posternDB, err := OpenDB(&Config{
100 DBPath: path.Join(t.TempDir(), "meta.db"),
101 })
102 if err != nil {
103 t.Fatal(err)
104 }
105
106 defer posternDB.Close()
107
108 key := "test-key"
109 var testVal int64 = 42
110 if err := posternDB.KVSet(t.Context(), key, testVal); err != nil {
111 t.Fatalf("KVSet setup failed: %v", err)
112 }
113
114 val, err := posternDB.KVPop(t.Context(), key)
115 if err != nil {
116 t.Fatalf("KVGet failed to fetch final value: %v", err)
117 }
118 if val != testVal {
119 t.Errorf("Expected value %d, got %d", testVal, val)
120 }
121
122 _, err = posternDB.KVPop(t.Context(), key)
123 if err == nil {
124 t.Fatalf("KVDelete should have failed during execution due to missing key")
125 }
126}