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  }