2023-03-28 18:39:22 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2018-04-04 03:46:07 +00:00
|
|
|
package cache
|
|
|
|
|
|
|
|
import (
|
2020-06-15 15:01:25 +00:00
|
|
|
"context"
|
2018-04-04 03:46:07 +00:00
|
|
|
"reflect"
|
|
|
|
"time"
|
|
|
|
|
2022-07-05 21:57:15 +00:00
|
|
|
testinf "github.com/mitchellh/go-testing-interface"
|
2018-04-04 03:46:07 +00:00
|
|
|
"github.com/stretchr/testify/mock"
|
2018-10-02 10:27:10 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2018-04-04 03:46:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// TestCacheGetCh returns a channel that returns the result of the Get call.
|
|
|
|
// This is useful for testing timing and concurrency with Get calls. Any
|
|
|
|
// error will be logged, so the result value should always be asserted.
|
2022-07-05 21:57:15 +00:00
|
|
|
func TestCacheGetCh(t testinf.T, c *Cache, typ string, r Request) <-chan interface{} {
|
2018-04-04 03:46:07 +00:00
|
|
|
resultCh := make(chan interface{})
|
|
|
|
go func() {
|
2020-06-15 15:01:25 +00:00
|
|
|
result, _, err := c.Get(context.Background(), typ, r)
|
2018-04-04 03:46:07 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Logf("Error: %s", err)
|
|
|
|
close(resultCh)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
resultCh <- result
|
|
|
|
}()
|
|
|
|
|
|
|
|
return resultCh
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestCacheGetChResult tests that the result from TestCacheGetCh matches
|
|
|
|
// within a reasonable period of time (it expects it to be "immediate" but
|
|
|
|
// waits some milliseconds).
|
2022-07-05 21:57:15 +00:00
|
|
|
func TestCacheGetChResult(t testinf.T, ch <-chan interface{}, expected interface{}) {
|
2018-04-04 03:46:07 +00:00
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case result := <-ch:
|
|
|
|
if !reflect.DeepEqual(result, expected) {
|
|
|
|
t.Fatalf("Result doesn't match!\n\n%#v\n\n%#v", result, expected)
|
|
|
|
}
|
2018-06-03 20:15:09 +00:00
|
|
|
|
2018-04-04 03:46:07 +00:00
|
|
|
case <-time.After(50 * time.Millisecond):
|
2018-06-03 20:15:09 +00:00
|
|
|
t.Fatalf("Result not sent on channel")
|
2018-04-04 03:46:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-02 10:27:10 +00:00
|
|
|
// TestCacheNotifyChResult tests that the expected updated was delivered on a
|
|
|
|
// Notify() chan within a reasonable period of time (it expects it to be
|
|
|
|
// "immediate" but waits some milliseconds). Expected may be given multiple
|
|
|
|
// times and if so these are all waited for and asserted to match but IN ANY
|
|
|
|
// ORDER to ensure we aren't timing dependent.
|
2022-07-05 21:57:15 +00:00
|
|
|
func TestCacheNotifyChResult(t testinf.T, ch <-chan UpdateEvent, expected ...UpdateEvent) {
|
2018-10-02 10:27:10 +00:00
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
expectLen := len(expected)
|
|
|
|
if expectLen < 1 {
|
|
|
|
panic("asserting nothing")
|
|
|
|
}
|
|
|
|
|
|
|
|
got := make([]UpdateEvent, 0, expectLen)
|
2020-08-21 18:05:25 +00:00
|
|
|
timeoutCh := time.After(75 * time.Millisecond)
|
2018-10-02 10:27:10 +00:00
|
|
|
|
|
|
|
OUT:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case result := <-ch:
|
|
|
|
// Ignore age as it's non-deterministic
|
|
|
|
result.Meta.Age = 0
|
|
|
|
got = append(got, result)
|
|
|
|
if len(got) == expectLen {
|
|
|
|
break OUT
|
|
|
|
}
|
|
|
|
|
|
|
|
case <-timeoutCh:
|
2020-08-21 18:05:25 +00:00
|
|
|
t.Fatalf("timeout while waiting for result: got %d results on chan, want %d", len(got), expectLen)
|
2018-10-02 10:27:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We already asserted len since you can only get here if we appended enough.
|
|
|
|
// Just check all the results we got are in the expected slice
|
|
|
|
require.ElementsMatch(t, expected, got)
|
|
|
|
}
|
|
|
|
|
2018-04-04 03:46:07 +00:00
|
|
|
// TestRequest returns a Request that returns the given cache key and index.
|
|
|
|
// The Reset method can be called to reset it for custom usage.
|
2022-07-05 21:57:15 +00:00
|
|
|
func TestRequest(t testinf.T, info RequestInfo) *MockRequest {
|
2018-04-04 03:46:07 +00:00
|
|
|
req := &MockRequest{}
|
2018-04-08 14:08:34 +00:00
|
|
|
req.On("CacheInfo").Return(info)
|
2018-04-04 03:46:07 +00:00
|
|
|
return req
|
|
|
|
}
|
|
|
|
|
2020-04-14 22:29:30 +00:00
|
|
|
// TestType returns a MockType that sets default RegisterOptions.
|
2022-07-05 21:57:15 +00:00
|
|
|
func TestType(t testinf.T) *MockType {
|
2020-04-14 22:29:30 +00:00
|
|
|
typ := &MockType{}
|
|
|
|
typ.On("RegisterOptions").Return(RegisterOptions{
|
|
|
|
SupportsBlocking: true,
|
|
|
|
})
|
|
|
|
return typ
|
2018-09-06 10:34:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TestTypeNonBlocking returns a MockType that returns false to SupportsBlocking.
|
2022-07-05 21:57:15 +00:00
|
|
|
func TestTypeNonBlocking(t testinf.T) *MockType {
|
2018-04-04 03:46:07 +00:00
|
|
|
typ := &MockType{}
|
2020-04-14 22:29:30 +00:00
|
|
|
typ.On("RegisterOptions").Return(RegisterOptions{
|
|
|
|
SupportsBlocking: false,
|
|
|
|
})
|
2018-04-04 03:46:07 +00:00
|
|
|
return typ
|
|
|
|
}
|
|
|
|
|
|
|
|
// A bit weird, but we add methods to the auto-generated structs here so that
|
|
|
|
// they don't get clobbered. The helper methods are conveniences.
|
|
|
|
|
|
|
|
// Static sets a static value to return for a call to Fetch.
|
|
|
|
func (m *MockType) Static(r FetchResult, err error) *mock.Call {
|
|
|
|
return m.Mock.On("Fetch", mock.Anything, mock.Anything).Return(r, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockRequest) Reset() {
|
|
|
|
m.Mock = mock.Mock{}
|
|
|
|
}
|