diff --git a/test/unit/listers/BUILD.bazel b/test/unit/listers/BUILD.bazel index 4fb78db0c..57cb63fd7 100644 --- a/test/unit/listers/BUILD.bazel +++ b/test/unit/listers/BUILD.bazel @@ -6,6 +6,7 @@ go_library( "certificaterequest.go", "order.go", "secret.go", + "util.go", ], importpath = "github.com/jetstack/cert-manager/test/unit/listers", visibility = ["//visibility:public"], diff --git a/test/unit/listers/certificaterequest.go b/test/unit/listers/certificaterequest.go index a133ee5ec..3f90a40f9 100644 --- a/test/unit/listers/certificaterequest.go +++ b/test/unit/listers/certificaterequest.go @@ -17,10 +17,6 @@ limitations under the License. package listers import ( - "bufio" - "bytes" - "runtime" - "strings" "sync" "testing" @@ -64,21 +60,13 @@ func MockCertificateRequestLister(t *testing.T) *CertificateRequestListerMock { return &CertificateRequestListerMock{t: t} } -// The expectSelector is a label selector of the form: -// "partition in (customerA, customerB),environment!=qa" -// as detailed in -// https://kubernetes.io/docs/concepts/overview/working-with-objects/labels -func (mock *CertificateRequestListerMock) CallList(expectSelector string) *CertificateRequestListerMock { - mock.t.Cleanup(assertWasCalled(mock.t, &mock.gotListCalled, "lister.List", assert.CallerInfo())) - mock.expectListCalled = true - mock.expectListSelector = expectSelector - return mock -} +type CertificateRequestListerMock struct { + t *testing.T + mu sync.Mutex -func (mock *CertificateRequestListerMock) ReturnList(returnList []*cmapi.CertificateRequest, returnErr error) *CertificateRequestListerMock { - mock.returnList = returnList - mock.returnListErr = returnErr - return mock + expectNamespaceCalled, gotNamespaceCalled bool + expectNamespace string + returnNamespaceLister *CertificateRequestListerNamespacedMock } // This mock function does not have a matching ReturnCertificateRequests @@ -92,6 +80,44 @@ func (mock *CertificateRequestListerMock) CallCertificateRequests(expectNamespac return mock.returnNamespaceLister } +func (mock *CertificateRequestListerMock) CallList(_ string) *CertificateRequestListerMock { + mock.t.Error("lister.List is not implemented in the mock, please implement it :-)") + return nil +} + +func (mock *CertificateRequestListerMock) CertificateRequests(gotNamespace string) cmlist.CertificateRequestNamespaceLister { + mock.mu.Lock() + defer mock.mu.Unlock() + assertCanBeCalled(mock.t, mock.expectNamespaceCalled, mock.gotNamespaceCalled, curFuncName(), assert.CallerInfo()) + assert.Equal(mock.t, mock.expectNamespace, gotNamespace) + mock.gotNamespaceCalled = true + return mock.returnNamespaceLister +} + +func (mock *CertificateRequestListerMock) List(gotLabel labels.Selector) (ret []*cmapi.CertificateRequest, err error) { + mock.t.Error("lister.CertificateRequest().List is not implemented in the mock, please implement it :-)") + return nil, nil +} + +type CertificateRequestListerNamespacedMock struct { + t *testing.T + mu sync.Mutex + + expectListCalled, gotListCalled bool + expectListSelector string + returnList []*cmapi.CertificateRequest + returnListErr error +} + +func (mock *CertificateRequestListerNamespacedMock) List(got labels.Selector) (ret []*cmapi.CertificateRequest, err error) { + mock.mu.Lock() + defer mock.mu.Unlock() + assertCanBeCalled(mock.t, mock.expectListCalled, mock.gotListCalled, curFuncName(), assert.CallerInfo()) + mock.gotListCalled = true + assert.Equal(mock.t, mock.expectListSelector, got.String()) + return mock.returnList, mock.returnListErr +} + // The expectSelector is a label selector of the form: // "partition in (customerA, customerB),environment!=qa" // as detailed in @@ -109,115 +135,33 @@ func (mock *CertificateRequestListerNamespacedMock) ReturnList(returnList []*cma return mock } -func (mock *CertificateRequestListerNamespacedMock) CallGet(expectName string) *CertificateRequestListerNamespacedMock { - mock.t.Cleanup(assertWasCalled(mock.t, &mock.gotGetCalled, "lister.CertificateRequest().Get", assert.CallerInfo())) - mock.expectGetCalled = true - mock.expectGetName = expectName - return mock -} - -func (mock *CertificateRequestListerNamespacedMock) ReturnGet(returnGet *cmapi.CertificateRequest, returnErr error) *CertificateRequestListerNamespacedMock { - mock.returnGet = returnGet - mock.returnGetErr = returnErr - return mock -} - -type CertificateRequestListerMock struct { - t *testing.T - mu sync.Mutex - - expectNamespaceCalled, gotNamespaceCalled bool - expectNamespace string - returnNamespaceLister *CertificateRequestListerNamespacedMock - - expectListCalled, gotListCalled bool - expectListSelector string - returnList []*cmapi.CertificateRequest - returnListErr error -} - -func (mock *CertificateRequestListerMock) CertificateRequests(gotNamespace string) cmlist.CertificateRequestNamespaceLister { - mock.mu.Lock() - defer mock.mu.Unlock() - assertCanBeCalled(mock.t, mock.expectNamespaceCalled, mock.gotNamespaceCalled, curFuncName(), assert.CallerInfo()) - assert.Equal(mock.t, mock.expectNamespace, gotNamespace) - mock.gotNamespaceCalled = true - return mock.returnNamespaceLister -} - -func (mock *CertificateRequestListerMock) List(gotLabel labels.Selector) (ret []*cmapi.CertificateRequest, err error) { - mock.mu.Lock() - defer mock.mu.Unlock() - assertCanBeCalled(mock.t, mock.expectListCalled, mock.gotListCalled, curFuncName(), assert.CallerInfo()) - assert.Equal(mock.t, mock.expectListSelector, gotLabel.String()) - mock.gotListCalled = true - return mock.returnList, mock.returnListErr -} - -type CertificateRequestListerNamespacedMock struct { - t *testing.T - mu sync.Mutex - - expectGetCalled, gotGetCalled bool - expectGetName string - returnGet *cmapi.CertificateRequest - returnGetErr error - - expectListCalled, gotListCalled bool - expectListSelector string - returnList []*cmapi.CertificateRequest - returnListErr error -} - -func (mock *CertificateRequestListerNamespacedMock) List(got labels.Selector) (ret []*cmapi.CertificateRequest, err error) { - mock.mu.Lock() - defer mock.mu.Unlock() - assertCanBeCalled(mock.t, mock.expectListCalled, mock.gotListCalled, curFuncName(), assert.CallerInfo()) - mock.gotListCalled = true - assert.Equal(mock.t, mock.expectListSelector, got.String()) - return mock.returnList, mock.returnListErr -} - -func (mock *CertificateRequestListerNamespacedMock) Get(gotName string) (cr *cmapi.CertificateRequest, e error) { - mock.mu.Lock() - defer mock.mu.Unlock() - assertCanBeCalled(mock.t, mock.expectGetCalled, mock.gotGetCalled, curFuncName(), assert.CallerInfo()) - mock.gotGetCalled = true - assert.Equal(mock.t, mock.expectGetName, gotName) +func (mock *CertificateRequestListerNamespacedMock) Get(_ string) (*cmapi.CertificateRequest, error) { + mock.t.Error("lister.CertificateRequest().List is not implemented in the mock, please implement it :-)") return nil, nil } -// Returns the caller's function name with the full package import path. -func curFuncName() (fnName string) { - pc, _, _, ok := runtime.Caller(1) - if !ok { - return "?" - } - - return runtime.FuncForPC(pc).Name() +func (mock *CertificateRequestListerNamespacedMock) CallGet(expectName string) *CertificateRequestListerNamespacedMock { + mock.t.Error("lister.CertificateRequest().List is not implemented in the mock, please implement it :-)") + return nil } +// Whenever a mocked function is called, we need to fail if this call was +// already made or if this call never should have happened. func assertCanBeCalled(t *testing.T, expectCalled, gotCalled bool, funcName string, stackFrames []string) { // No need to show the file:line of the caller of this function since // it belongs to "testing framework". stackFrames = stackFrames[1:] if !expectCalled { - FailWithStack(t, stackFrames, funcName+" is not expected to be called but was called") + failWithStack(t, stackFrames, funcName+" is not expected to be called but was called") } if gotCalled { - FailWithStack(t, stackFrames, funcName+" was expected to run once but was run twice") + failWithStack(t, stackFrames, funcName+" was expected to run once but was run twice") } } -// Since this func is meant to be called with t.Cleanup, the stack frame -// information about the source of the call is lost. Testify usually gives -// us a useful file:line indication of where an assertion failed, and -// t.Cleanup makes this stack trace unreadable with tons of testing.go -// layers. -// -// Until Testify fixes this, we keep track of the caller information right -// from the start with the stackFrames argument, and we use a patched -// version of assert.Fail that can be given a stack frames. +// This function is meant to be run with t.Cleanup. During the cleanup, +// this function checks that the function named funcName has been called as +// expected. func assertWasCalled(t *testing.T, funcWasCalled *bool, funcName string, stackFrames []string) func() { return func() { // We pass this boolean by reference due to the fact that this @@ -232,47 +176,6 @@ func assertWasCalled(t *testing.T, funcWasCalled *bool, funcName string, stackFr // No need to show the file:line of the caller of this function since // it belongs to "testing frqmework". - FailWithStack(t, stackFrames[1:], funcName+" was expected to be called but was not called") + failWithStack(t, stackFrames[1:], funcName+" was expected to be called but was not called") } } - -// FailWithStack does the same as assert.Fail except it gives you the -// ability to give your own stack frames. -func FailWithStack(t *testing.T, stackFrames []string, msg string) { - // The following is a vendored version of Testify's assert.Fail. - type labeledContent struct{ Label, Content string } - content := []labeledContent{ - {Label: "Error Trace", Content: strings.Join(stackFrames, "\n")}, - {Label: "Error", Content: msg}, - {Label: "Test", Content: t.Name()}, - } - - // Helper that re-wrap and indent the "content" fields of the above - // content array. - indentMessageLines := func(message string, longestLabelLen int) string { - buf := new(bytes.Buffer) - for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ { - if i != 0 { - buf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t") - } - buf.WriteString(scanner.Text()) - } - return buf.String() - } - - longestLabelLen := 0 - for _, v := range content { - if len(v.Label) > longestLabelLen { - longestLabelLen = len(v.Label) - } - } - - // Turn the above content slice into a nicely formatted string that - // wraps and properly indented. - var output string - for _, v := range content { - output += "\t" + v.Label + ":" + strings.Repeat(" ", longestLabelLen-len(v.Label)) + "\t" + indentMessageLines(v.Content, longestLabelLen) + "\n" - } - - t.Errorf("\n%s", ""+output) -} diff --git a/test/unit/listers/util.go b/test/unit/listers/util.go new file mode 100644 index 000000000..9734e5b77 --- /dev/null +++ b/test/unit/listers/util.go @@ -0,0 +1,62 @@ +package listers + +import ( + "bufio" + "bytes" + "runtime" + "strings" + "testing" +) + +// Returns the caller's function name with the full package import path. +func curFuncName() (fnName string) { + pc, _, _, _ := runtime.Caller(1) + return runtime.FuncForPC(pc).Name() +} + +// failWithStack does the same as assert.Fail except it gives you the +// ability to give your own stack frames. The purpose of this function is +// just to help the developer who wants to know where a Testify assertion +// failed. +// +// We use that whenever we want to do an assertion that happens in a +// t.Cleanup function, since the t.Cleanup happens outside of the user's +// test file which means the stack frames are totally off. +func failWithStack(t *testing.T, stackFrames []string, msg string) { + // The following is a vendored version of Testify's assert.Fail. + type labeledContent struct{ Label, Content string } + content := []labeledContent{ + {Label: "Error Trace", Content: strings.Join(stackFrames, "\n")}, + {Label: "Error", Content: msg}, + {Label: "Test", Content: t.Name()}, + } + + // Helper that re-wrap and indent the "content" fields of the above + // content array. + indentMessageLines := func(message string, longestLabelLen int) string { + buf := new(bytes.Buffer) + for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ { + if i != 0 { + buf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t") + } + buf.WriteString(scanner.Text()) + } + return buf.String() + } + + longestLabelLen := 0 + for _, v := range content { + if len(v.Label) > longestLabelLen { + longestLabelLen = len(v.Label) + } + } + + // Turn the above content slice into a nicely formatted string that + // wraps and properly indented. + var output string + for _, v := range content { + output += "\t" + v.Label + ":" + strings.Repeat(" ", longestLabelLen-len(v.Label)) + "\t" + indentMessageLines(v.Content, longestLabelLen) + "\n" + } + + t.Errorf("\n%s", ""+output) +}