github.com/outbrain/consul@v1.4.5/agent/cache/testing.go (about) 1 package cache 2 3 import ( 4 "reflect" 5 "time" 6 7 "github.com/mitchellh/go-testing-interface" 8 "github.com/stretchr/testify/mock" 9 "github.com/stretchr/testify/require" 10 ) 11 12 // TestCache returns a Cache instance configuring for testing. 13 func TestCache(t testing.T) *Cache { 14 // Simple but lets us do some fine-tuning later if we want to. 15 return New(nil) 16 } 17 18 // TestCacheGetCh returns a channel that returns the result of the Get call. 19 // This is useful for testing timing and concurrency with Get calls. Any 20 // error will be logged, so the result value should always be asserted. 21 func TestCacheGetCh(t testing.T, c *Cache, typ string, r Request) <-chan interface{} { 22 resultCh := make(chan interface{}) 23 go func() { 24 result, _, err := c.Get(typ, r) 25 if err != nil { 26 t.Logf("Error: %s", err) 27 close(resultCh) 28 return 29 } 30 31 resultCh <- result 32 }() 33 34 return resultCh 35 } 36 37 // TestCacheGetChResult tests that the result from TestCacheGetCh matches 38 // within a reasonable period of time (it expects it to be "immediate" but 39 // waits some milliseconds). 40 func TestCacheGetChResult(t testing.T, ch <-chan interface{}, expected interface{}) { 41 t.Helper() 42 43 select { 44 case result := <-ch: 45 if !reflect.DeepEqual(result, expected) { 46 t.Fatalf("Result doesn't match!\n\n%#v\n\n%#v", result, expected) 47 } 48 49 case <-time.After(50 * time.Millisecond): 50 t.Fatalf("Result not sent on channel") 51 } 52 } 53 54 // TestCacheNotifyChResult tests that the expected updated was delivered on a 55 // Notify() chan within a reasonable period of time (it expects it to be 56 // "immediate" but waits some milliseconds). Expected may be given multiple 57 // times and if so these are all waited for and asserted to match but IN ANY 58 // ORDER to ensure we aren't timing dependent. 59 func TestCacheNotifyChResult(t testing.T, ch <-chan UpdateEvent, expected ...UpdateEvent) { 60 t.Helper() 61 62 expectLen := len(expected) 63 if expectLen < 1 { 64 panic("asserting nothing") 65 } 66 67 got := make([]UpdateEvent, 0, expectLen) 68 timeoutCh := time.After(50 * time.Millisecond) 69 70 OUT: 71 for { 72 select { 73 case result := <-ch: 74 // Ignore age as it's non-deterministic 75 result.Meta.Age = 0 76 got = append(got, result) 77 if len(got) == expectLen { 78 break OUT 79 } 80 81 case <-timeoutCh: 82 t.Fatalf("got %d results on chan in 50ms, want %d", len(got), expectLen) 83 } 84 } 85 86 // We already asserted len since you can only get here if we appended enough. 87 // Just check all the results we got are in the expected slice 88 require.ElementsMatch(t, expected, got) 89 } 90 91 // TestRequest returns a Request that returns the given cache key and index. 92 // The Reset method can be called to reset it for custom usage. 93 func TestRequest(t testing.T, info RequestInfo) *MockRequest { 94 req := &MockRequest{} 95 req.On("CacheInfo").Return(info) 96 return req 97 } 98 99 // TestType returns a MockType that can be used to setup expectations 100 // on data fetching. 101 func TestType(t testing.T) *MockType { 102 return testTypeInternal(t, true) 103 } 104 105 // TestTypeNonBlocking returns a MockType that returns false to SupportsBlocking. 106 func TestTypeNonBlocking(t testing.T) *MockType { 107 return testTypeInternal(t, false) 108 } 109 110 func testTypeInternal(t testing.T, enableBlocking bool) *MockType { 111 typ := &MockType{} 112 typ.On("SupportsBlocking").Return(enableBlocking).Maybe() 113 return typ 114 } 115 116 // A bit weird, but we add methods to the auto-generated structs here so that 117 // they don't get clobbered. The helper methods are conveniences. 118 119 // Static sets a static value to return for a call to Fetch. 120 func (m *MockType) Static(r FetchResult, err error) *mock.Call { 121 return m.Mock.On("Fetch", mock.Anything, mock.Anything).Return(r, err) 122 } 123 124 func (m *MockRequest) Reset() { 125 m.Mock = mock.Mock{} 126 }