github.com/adowair/kvdb@v0.0.0-20231101174258-ceca93b03596/kv/util_test.go (about) 1 package kv 2 3 import ( 4 "io/fs" 5 "os" 6 "testing" 7 "time" 8 9 "github.com/stretchr/testify/assert" 10 ) 11 12 type testcase struct { 13 name string 14 initialData map[string]string 15 key string 16 expError error 17 targetEntry *Entry 18 finalData map[string]string 19 } 20 21 func setupTestDB(t *testing.T, entries map[string]string) string { 22 dir := t.TempDir() 23 assert.NoError(t, os.Chdir(dir)) 24 for key, value := range entries { 25 os.WriteFile(key, []byte(value), fs.ModePerm) 26 } 27 return dir 28 } 29 30 func checkTestDB(t *testing.T, path string, entries map[string]string) { 31 dir, err := os.ReadDir(path) 32 assert.NoError(t, err) 33 assert.Equal(t, len(entries), len(dir)) 34 for key, data := range entries { 35 assert.FileExists(t, key) 36 actual, err := os.ReadFile(key) 37 assert.NoError(t, err) 38 assert.Equal(t, data, string(actual)) 39 } 40 } 41 42 func TestRead(t *testing.T) { 43 for _, tc := range []testcase{ 44 { 45 name: "TestReadOK", 46 key: "foo", 47 initialData: map[string]string{"foo": "0,0,bar"}, 48 expError: nil, 49 targetEntry: &Entry{ 50 Value: "bar", 51 FirstEdited: time.Unix(0, 0), 52 LastEdited: time.Unix(0, 0), 53 }, 54 finalData: map[string]string{"foo": "0,0,bar"}, 55 }, 56 { 57 name: "TestReadEmptyValue", 58 key: "foo", 59 initialData: map[string]string{"foo": "0,0,"}, 60 expError: ErrBadFormat, 61 targetEntry: nil, 62 finalData: map[string]string{"foo": "0,0,"}, 63 }, 64 { 65 name: "TestReadNotExist", 66 key: "foo", 67 initialData: nil, 68 expError: ErrNotExist, 69 targetEntry: nil, 70 finalData: nil, 71 }, 72 { 73 name: "TestReadDelimiterInValue", 74 key: "foo", 75 initialData: map[string]string{"foo": "0,0,ba,r"}, 76 expError: nil, 77 targetEntry: &Entry{ 78 Value: "ba,r", 79 FirstEdited: time.Unix(0, 0), 80 LastEdited: time.Unix(0, 0), 81 }, 82 finalData: map[string]string{"foo": "0,0,ba,r"}, 83 }, 84 { 85 name: "TestReadNoValue", 86 key: "foo", 87 initialData: map[string]string{"foo": "0,0"}, 88 expError: ErrBadFormat, 89 targetEntry: nil, 90 finalData: map[string]string{"foo": "0,0"}, 91 }, 92 { 93 name: "TestReadNothing", 94 key: "foo", 95 initialData: map[string]string{"foo": ""}, 96 expError: ErrBadFormat, 97 targetEntry: nil, 98 finalData: map[string]string{"foo": ""}, 99 }, 100 { 101 name: "TestReadNotTimestamp", 102 key: "foo", 103 initialData: map[string]string{"foo": "0,alpha,bar"}, 104 expError: ErrBadFormat, 105 targetEntry: nil, 106 finalData: map[string]string{"foo": "0,alpha,bar"}, 107 }, 108 } { 109 t.Run(tc.name, func(t *testing.T) { 110 dir := setupTestDB(t, tc.initialData) 111 112 entry, err := read(tc.key) 113 assert.Equal(t, tc.targetEntry, entry) 114 assert.ErrorIs(t, err, tc.expError) 115 116 checkTestDB(t, dir, tc.finalData) 117 }) 118 } 119 } 120 121 func TestWrite(t *testing.T) { 122 for _, tc := range []testcase{ 123 { 124 name: "TestWriteOK", 125 initialData: nil, 126 key: "foo", 127 targetEntry: &Entry{ 128 Value: "bar", 129 FirstEdited: time.Unix(0, 0), 130 LastEdited: time.Unix(1, 0), 131 }, 132 expError: nil, 133 finalData: map[string]string{"foo": "0,1,bar"}, 134 }, 135 { 136 name: "TestOverWrite", 137 initialData: map[string]string{"foo": "0,0,bar"}, 138 key: "foo", 139 targetEntry: &Entry{ 140 Value: "baz", 141 FirstEdited: time.Unix(0, 0), 142 LastEdited: time.Unix(1, 0), 143 }, 144 expError: nil, 145 finalData: map[string]string{"foo": "0,1,baz"}, 146 }, 147 { 148 name: "TestWriteEmptyValue", 149 initialData: nil, 150 key: "foo", 151 targetEntry: &Entry{ 152 Value: "", 153 FirstEdited: time.Unix(0, 0), 154 LastEdited: time.Unix(1, 0), 155 }, 156 expError: ErrBadFormat, 157 finalData: nil, 158 }, 159 { 160 name: "TestOverWriteEmptyValue", 161 initialData: map[string]string{"foo": "0,0,bar"}, 162 key: "foo", 163 targetEntry: &Entry{ 164 Value: "", 165 FirstEdited: time.Unix(0, 0), 166 LastEdited: time.Unix(1, 0), 167 }, 168 expError: ErrBadFormat, 169 finalData: map[string]string{"foo": "0,0,bar"}, 170 }, 171 { 172 name: "TestWriteEmptyEntry", 173 initialData: nil, 174 key: "foo", 175 targetEntry: &Entry{}, 176 expError: ErrBadFormat, 177 finalData: nil, 178 }, 179 } { 180 t.Run(tc.name, func(t *testing.T) { 181 dir := setupTestDB(t, tc.initialData) 182 183 err := write(tc.key, tc.targetEntry) 184 assert.ErrorIs(t, err, tc.expError) 185 186 checkTestDB(t, dir, tc.finalData) 187 }) 188 } 189 } 190 191 func TestDelete(t *testing.T) { 192 for _, tc := range []testcase{ 193 { 194 name: "TestDeleteOK", 195 initialData: map[string]string{"foo": "0,1,bar"}, 196 key: "foo", 197 targetEntry: &Entry{ 198 Value: "bar", 199 FirstEdited: time.Unix(0, 0), 200 LastEdited: time.Unix(1, 0), 201 }, 202 expError: nil, 203 finalData: nil, 204 }, 205 { 206 name: "TestIdempotentDelete", 207 initialData: nil, 208 key: "foo", 209 targetEntry: &Entry{ 210 Value: "bar", 211 FirstEdited: time.Unix(0, 0), 212 LastEdited: time.Unix(1, 0), 213 }, 214 expError: nil, 215 finalData: nil, 216 }, 217 } { 218 t.Run(tc.name, func(t *testing.T) { 219 testDir := setupTestDB(t, tc.initialData) 220 221 err := delete(tc.key) 222 assert.ErrorIs(t, err, tc.expError) 223 224 checkTestDB(t, testDir, tc.finalData) 225 }) 226 } 227 }