| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- // Copyright (c) Tailscale Inc & AUTHORS
- // SPDX-License-Identifier: BSD-3-Clause
- package cache
- import (
- "errors"
- "testing"
- "time"
- )
- var startTime = time.Date(2023, time.March, 1, 0, 0, 0, 0, time.UTC)
- func TestSingleCache(t *testing.T) {
- testTime := startTime
- timeNow := func() time.Time { return testTime }
- c := &Single[string, int]{
- timeNow: timeNow,
- }
- t.Run("NoServeExpired", func(t *testing.T) {
- testCacheImpl(t, c, &testTime, false)
- })
- t.Run("ServeExpired", func(t *testing.T) {
- c.Empty()
- c.ServeExpired = true
- testTime = startTime
- testCacheImpl(t, c, &testTime, true)
- })
- }
- func TestLocking(t *testing.T) {
- testTime := startTime
- timeNow := func() time.Time { return testTime }
- c := NewLocking(&Single[string, int]{
- timeNow: timeNow,
- })
- // Just verify that the inner cache's behaviour hasn't changed.
- testCacheImpl(t, c, &testTime, false)
- }
- func testCacheImpl(t *testing.T, c Cache[string, int], testTime *time.Time, serveExpired bool) {
- var fillTime time.Time
- t.Run("InitialFill", func(t *testing.T) {
- fillTime = testTime.Add(time.Hour)
- val, err := c.Get("key", func() (int, time.Time, error) {
- return 123, fillTime, nil
- })
- if err != nil {
- t.Fatal(err)
- }
- if val != 123 {
- t.Fatalf("got val=%d; want 123", val)
- }
- })
- // Fetching again won't call our fill function
- t.Run("SecondFetch", func(t *testing.T) {
- *testTime = fillTime.Add(-1 * time.Second)
- called := false
- val, err := c.Get("key", func() (int, time.Time, error) {
- called = true
- return -1, fillTime, nil
- })
- if called {
- t.Fatal("wanted no call to fill function")
- }
- if err != nil {
- t.Fatal(err)
- }
- if val != 123 {
- t.Fatalf("got val=%d; want 123", val)
- }
- })
- // Fetching after the expiry time will re-fill
- t.Run("ReFill", func(t *testing.T) {
- *testTime = fillTime.Add(1)
- fillTime = fillTime.Add(time.Hour)
- val, err := c.Get("key", func() (int, time.Time, error) {
- return 999, fillTime, nil
- })
- if err != nil {
- t.Fatal(err)
- }
- if val != 999 {
- t.Fatalf("got val=%d; want 999", val)
- }
- })
- // An error on fetch will serve the expired value.
- t.Run("FetchError", func(t *testing.T) {
- if !serveExpired {
- t.Skipf("not testing ServeExpired")
- }
- *testTime = fillTime.Add(time.Hour + 1)
- val, err := c.Get("key", func() (int, time.Time, error) {
- return 0, time.Time{}, errors.New("some error")
- })
- if err != nil {
- t.Fatal(err)
- }
- if val != 999 {
- t.Fatalf("got val=%d; want 999", val)
- }
- })
- // Fetching a different key re-fills
- t.Run("DifferentKey", func(t *testing.T) {
- *testTime = fillTime.Add(time.Hour + 1)
- var calls int
- val, err := c.Get("key1", func() (int, time.Time, error) {
- calls++
- return 123, fillTime, nil
- })
- if err != nil {
- t.Fatal(err)
- }
- if val != 123 {
- t.Fatalf("got val=%d; want 123", val)
- }
- if calls != 1 {
- t.Errorf("got %d, want 1 call", calls)
- }
- val, err = c.Get("key2", func() (int, time.Time, error) {
- calls++
- return 456, fillTime, nil
- })
- if err != nil {
- t.Fatal(err)
- }
- if val != 456 {
- t.Fatalf("got val=%d; want 456", val)
- }
- if calls != 2 {
- t.Errorf("got %d, want 2 call", calls)
- }
- })
- // Calling Forget with the wrong key does nothing, and with the correct
- // key will drop the cache.
- t.Run("Forget", func(t *testing.T) {
- // Add some time so that previously-cached values don't matter.
- fillTime = testTime.Add(2 * time.Hour)
- *testTime = fillTime.Add(-1 * time.Second)
- const key = "key"
- var calls int
- val, err := c.Get(key, func() (int, time.Time, error) {
- calls++
- return 123, fillTime, nil
- })
- if err != nil {
- t.Fatal(err)
- }
- if val != 123 {
- t.Fatalf("got val=%d; want 123", val)
- }
- if calls != 1 {
- t.Errorf("got %d, want 1 call", calls)
- }
- // Forgetting the wrong key does nothing
- c.Forget("other")
- val, err = c.Get(key, func() (int, time.Time, error) {
- t.Fatal("should not be called")
- panic("unreachable")
- })
- if err != nil {
- t.Fatal(err)
- }
- if val != 123 {
- t.Fatalf("got val=%d; want 123", val)
- }
- // Forgetting the correct key re-fills
- c.Forget(key)
- val, err = c.Get("key2", func() (int, time.Time, error) {
- calls++
- return 456, fillTime, nil
- })
- if err != nil {
- t.Fatal(err)
- }
- if val != 456 {
- t.Fatalf("got val=%d; want 456", val)
- }
- if calls != 2 {
- t.Errorf("got %d, want 2 call", calls)
- }
- })
- }
|