package httpapi import ( "bytes" "sync" "sync/atomic" "testing" "time" ) func TestRawContentCacheCoalescesConcurrentLoads(t *testing.T) { t.Parallel() cache := newRawContentCache(8, 8*1024*1024) var loads int32 want := []byte("coalesced") var wg sync.WaitGroup for i := 0; i < 12; i++ { wg.Add(1) go func() { defer wg.Done() got, err, _ := cache.GetOrLoad("shared-id", func() ([]byte, error) { atomic.AddInt32(&loads, 1) time.Sleep(15 * time.Millisecond) return want, nil }) if err != nil { t.Errorf("GetOrLoad() error = %v", err) return } if !bytes.Equal(got, want) { t.Errorf("GetOrLoad() content mismatch = %q, want %q", got, want) } }() } wg.Wait() if got := atomic.LoadInt32(&loads); got != 1 { t.Fatalf("loader call count = %d, want 1", got) } } func TestRawContentCacheEvictionCausesReload(t *testing.T) { t.Parallel() cache := newRawContentCache(1, 3) var loadsA int32 var loadsB int32 if _, err, _ := cache.GetOrLoad("a", func() ([]byte, error) { atomic.AddInt32(&loadsA, 1) return []byte("aaa"), nil }); err != nil { t.Fatalf("GetOrLoad(a) error = %v", err) } if _, err, _ := cache.GetOrLoad("b", func() ([]byte, error) { atomic.AddInt32(&loadsB, 1) return []byte("bbb"), nil }); err != nil { t.Fatalf("GetOrLoad(b) error = %v", err) } if _, err, _ := cache.GetOrLoad("a", func() ([]byte, error) { atomic.AddInt32(&loadsA, 1) return []byte("aaa"), nil }); err != nil { t.Fatalf("GetOrLoad(a second) error = %v", err) } if got := atomic.LoadInt32(&loadsB); got != 1 { t.Fatalf("loadsB = %d, want 1", got) } if got := atomic.LoadInt32(&loadsA); got != 2 { t.Fatalf("loadsA = %d, want 2", got) } }