@@ -133,3 +133,268 @@ func TestCacheCleanup(t *testing.T) {
133133 t .Error ("Expected non-expired entry to remain after cleanup" )
134134 }
135135}
136+
137+ func TestCacheGetNonExistent (t * testing.T ) {
138+ cache := NewCache (1 * time .Second )
139+
140+ _ , found := cache .Get ("non-existent-key" )
141+ if found {
142+ t .Error ("Expected not to find non-existent key" )
143+ }
144+ }
145+
146+ func TestCacheSetOverwrite (t * testing.T ) {
147+ cache := NewCache (1 * time .Second )
148+
149+ cache .Set ("test-key" , "initial-value" )
150+ cache .Set ("test-key" , "updated-value" )
151+
152+ value , found := cache .Get ("test-key" )
153+ if ! found {
154+ t .Error ("Expected to find cached value" )
155+ }
156+ if value .(string ) != "updated-value" {
157+ t .Errorf ("Expected 'updated-value', got '%s'" , value )
158+ }
159+ }
160+
161+ func TestCacheMultipleTypes (t * testing.T ) {
162+ cache := NewCache (1 * time .Second )
163+
164+ // Test different value types
165+ cache .Set ("string" , "test" )
166+ cache .Set ("int" , 42 )
167+ cache .Set ("bool" , true )
168+ cache .Set ("slice" , []string {"a" , "b" , "c" })
169+ cache .Set ("map" , map [string ]int {"x" : 1 , "y" : 2 })
170+
171+ // Verify all types can be retrieved
172+ str , found := cache .Get ("string" )
173+ if ! found || str .(string ) != "test" {
174+ t .Error ("Failed to retrieve string value" )
175+ }
176+
177+ num , found := cache .Get ("int" )
178+ if ! found || num .(int ) != 42 {
179+ t .Error ("Failed to retrieve int value" )
180+ }
181+
182+ b , found := cache .Get ("bool" )
183+ if ! found || b .(bool ) != true {
184+ t .Error ("Failed to retrieve bool value" )
185+ }
186+
187+ slice , found := cache .Get ("slice" )
188+ if ! found || len (slice .([]string )) != 3 {
189+ t .Error ("Failed to retrieve slice value" )
190+ }
191+
192+ m , found := cache .Get ("map" )
193+ if ! found || m .(map [string ]int )["x" ] != 1 {
194+ t .Error ("Failed to retrieve map value" )
195+ }
196+ }
197+
198+ func TestCacheInvalidateNonExistent (t * testing.T ) {
199+ cache := NewCache (1 * time .Second )
200+
201+ // Invalidating non-existent key should not panic
202+ cache .Invalidate ("non-existent" )
203+ }
204+
205+ func TestCacheInvalidatePatternNoMatch (t * testing.T ) {
206+ cache := NewCache (1 * time .Second )
207+
208+ cache .Set ("tasks:all" , "all tasks" )
209+ cache .Set ("projects:all" , "all projects" )
210+
211+ // Invalidate pattern that doesn't match anything
212+ cache .InvalidatePattern ("users:" )
213+
214+ // All original entries should remain
215+ _ , found1 := cache .Get ("tasks:all" )
216+ _ , found2 := cache .Get ("projects:all" )
217+
218+ if ! found1 || ! found2 {
219+ t .Error ("Expected all entries to remain when pattern doesn't match" )
220+ }
221+ }
222+
223+ func TestCacheInvalidatePatternExactMatch (t * testing.T ) {
224+ cache := NewCache (1 * time .Second )
225+
226+ cache .Set ("tasks" , "tasks" )
227+ cache .Set ("tasks:all" , "all tasks" )
228+
229+ // Invalidate with exact prefix
230+ cache .InvalidatePattern ("tasks" )
231+
232+ // Both should be invalidated
233+ _ , found1 := cache .Get ("tasks" )
234+ _ , found2 := cache .Get ("tasks:all" )
235+
236+ if found1 || found2 {
237+ t .Error ("Expected both entries to be invalidated" )
238+ }
239+ }
240+
241+ func TestCacheCleanupDisabled (t * testing.T ) {
242+ cache := NewCache (0 )
243+
244+ // Should not panic when cache is disabled
245+ cache .Cleanup ()
246+ }
247+
248+ func TestCacheInvalidateAllDisabled (t * testing.T ) {
249+ cache := NewCache (0 )
250+
251+ // Should not panic when cache is disabled
252+ cache .InvalidateAll ()
253+ }
254+
255+ func TestCacheInvalidateDisabled (t * testing.T ) {
256+ cache := NewCache (0 )
257+
258+ // Should not panic when cache is disabled
259+ cache .Invalidate ("test-key" )
260+ }
261+
262+ func TestCacheInvalidatePatternDisabled (t * testing.T ) {
263+ cache := NewCache (0 )
264+
265+ // Should not panic when cache is disabled
266+ cache .InvalidatePattern ("test:" )
267+ }
268+
269+ func TestCacheNegativeTTL (t * testing.T ) {
270+ cache := NewCache (- 1 * time .Second )
271+
272+ cache .Set ("test-key" , "test-value" )
273+
274+ _ , found := cache .Get ("test-key" )
275+ if found {
276+ t .Error ("Expected cache to be disabled with negative TTL" )
277+ }
278+ }
279+
280+ func TestCacheCleanupEmptyCache (t * testing.T ) {
281+ cache := NewCache (1 * time .Second )
282+
283+ // Should not panic on empty cache
284+ cache .Cleanup ()
285+ }
286+
287+ func TestCacheInvalidateAllEmptyCache (t * testing.T ) {
288+ cache := NewCache (1 * time .Second )
289+
290+ // Should not panic on empty cache
291+ cache .InvalidateAll ()
292+ }
293+
294+ func TestCacheStartCleanupTimer (t * testing.T ) {
295+ cache := NewCache (50 * time .Millisecond )
296+
297+ cache .Set ("key1" , "value1" )
298+ cache .Set ("key2" , "value2" )
299+
300+ // Start cleanup timer with short interval
301+ cache .StartCleanupTimer (60 * time .Millisecond )
302+
303+ // Wait for cleanup to potentially run
304+ time .Sleep (150 * time .Millisecond )
305+
306+ // Add a new entry
307+ cache .Set ("key3" , "value3" )
308+
309+ // Old entries should be cleaned up automatically
310+ _ , found1 := cache .Get ("key1" )
311+ _ , found2 := cache .Get ("key2" )
312+ _ , found3 := cache .Get ("key3" )
313+
314+ if found1 || found2 {
315+ t .Error ("Expected expired entries to be cleaned up by timer" )
316+ }
317+ if ! found3 {
318+ t .Error ("Expected new entry to remain" )
319+ }
320+ }
321+
322+ func TestCacheStartCleanupTimerDisabled (t * testing.T ) {
323+ cache := NewCache (0 )
324+
325+ // Should not panic when cache is disabled
326+ cache .StartCleanupTimer (100 * time .Millisecond )
327+ }
328+
329+ func TestCacheConcurrentAccess (t * testing.T ) {
330+ cache := NewCache (1 * time .Second )
331+ done := make (chan bool )
332+
333+ // Concurrent writes
334+ for i := 0 ; i < 10 ; i ++ {
335+ go func (n int ) {
336+ for j := 0 ; j < 100 ; j ++ {
337+ cache .Set ("key" , n * 100 + j )
338+ }
339+ done <- true
340+ }(i )
341+ }
342+
343+ // Concurrent reads
344+ for i := 0 ; i < 10 ; i ++ {
345+ go func () {
346+ for j := 0 ; j < 100 ; j ++ {
347+ cache .Get ("key" )
348+ }
349+ done <- true
350+ }()
351+ }
352+
353+ // Wait for all goroutines
354+ for i := 0 ; i < 20 ; i ++ {
355+ <- done
356+ }
357+
358+ // Should not panic and should have a value
359+ _ , found := cache .Get ("key" )
360+ if ! found {
361+ t .Error ("Expected to find value after concurrent access" )
362+ }
363+ }
364+
365+ func TestCacheConcurrentInvalidation (t * testing.T ) {
366+ cache := NewCache (1 * time .Second )
367+ done := make (chan bool )
368+
369+ // Set initial values
370+ for i := 0 ; i < 100 ; i ++ {
371+ cache .Set ("key" , i )
372+ }
373+
374+ // Concurrent invalidations
375+ for i := 0 ; i < 5 ; i ++ {
376+ go func () {
377+ for j := 0 ; j < 20 ; j ++ {
378+ cache .Invalidate ("key" )
379+ }
380+ done <- true
381+ }()
382+ }
383+
384+ // Concurrent sets
385+ for i := 0 ; i < 5 ; i ++ {
386+ go func () {
387+ for j := 0 ; j < 20 ; j ++ {
388+ cache .Set ("key" , j )
389+ }
390+ done <- true
391+ }()
392+ }
393+
394+ // Wait for all goroutines
395+ for i := 0 ; i < 10 ; i ++ {
396+ <- done
397+ }
398+
399+ // Should not panic
400+ }
0 commit comments