From e544a7e468e5bd51652002be6cfd6ee604f945b0 Mon Sep 17 00:00:00 2001 From: ivangonzalezacuna Date: Thu, 10 Oct 2024 09:42:47 +0200 Subject: [PATCH] Add option to matcher to ignore headers The new matcher, which is more strict now, is breaking tests where some of the headers are skipped (like secrets) or change on each request (like tracing spans). The cassete already supports the User-Agentt, and the Authorization has also been added but not released yet. In order to make it more generic and usable for people, I've added a more generic implementation of these options, which will allow us to define which headers we really want to ignore. Signed-off-by: ivangonzalezacuna --- README.md | 2 +- pkg/cassette/cassette.go | 38 ++++++++++++++++++----------------- pkg/cassette/cassette_test.go | 32 +++++++++++++++++++++++++---- 3 files changed, 49 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index b347f84..933e10a 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ hook := func(i *cassette.Interaction) error { opts := []recorder.Option{ recorder.WithCassette("fixtures/filters"), recorder.WithHook(hook, recorder.AfterCaptureHook), - recorder.WithMatcher(cassette.NewDefaultMatcher(cassette.WithIgnoreAuthorization(true))), + recorder.WithMatcher(cassette.NewDefaultMatcher(cassette.WithIgnoreAuthorization())), } r, err := recorder.New(opts...) diff --git a/pkg/cassette/cassette.go b/pkg/cassette/cassette.go index 30acd20..6543a29 100644 --- a/pkg/cassette/cassette.go +++ b/pkg/cassette/cassette.go @@ -202,12 +202,9 @@ type MatcherFunc func(*http.Request, Request) bool // defaultMatcher is the default matcher used to match HTTP requests with // recorded interactions. type defaultMatcher struct { - // If set to true, the default matcher will ignore matching on the - // User-Agent HTTP header. - ignoreUserAgent bool - // If set to true, the default matcher will ignore matching on the - // Authorization HTTP header. - ignoreAuthorization bool + // If set, the default matcher will ignore matching on any of the + // defined headers. + ignoreHeaders []string } // DefaultMatcherOption is a function which configures the default matcher. @@ -215,9 +212,9 @@ type DefaultMatcherOption func(m *defaultMatcher) // WithIgnoreUserAgent is a [DefaultMatcherOption], which configures the default // matcher to ignore matching on the User-Agent HTTP header. -func WithIgnoreUserAgent(val bool) DefaultMatcherOption { +func WithIgnoreUserAgent() DefaultMatcherOption { opt := func(m *defaultMatcher) { - m.ignoreUserAgent = val + m.ignoreHeaders = append(m.ignoreHeaders, "User-Agent") } return opt @@ -225,9 +222,19 @@ func WithIgnoreUserAgent(val bool) DefaultMatcherOption { // WithIgnoreAuthorization is a [DefaultMatcherOption], which configures the default // matcher to ignore matching on the Authorization HTTP header. -func WithIgnoreAuthorization(val bool) DefaultMatcherOption { +func WithIgnoreAuthorization() DefaultMatcherOption { opt := func(m *defaultMatcher) { - m.ignoreAuthorization = val + m.ignoreHeaders = append(m.ignoreHeaders, "Authorization") + } + + return opt +} + +// WithIgnoreHeaders is a [DefaultMatcherOption], which configures the default +// matcher to ignore matching on the defined HTTP headers. +func WithIgnoreHeaders(val ...string) DefaultMatcherOption { + opt := func(m *defaultMatcher) { + m.ignoreHeaders = append(m.ignoreHeaders, val...) } return opt @@ -310,14 +317,9 @@ func (m *defaultMatcher) matcher(r *http.Request, i Request) bool { requestHeader := r.Header.Clone() cassetteRequestHeaders := i.Headers.Clone() - if m.ignoreUserAgent { - delete(requestHeader, "User-Agent") - delete(cassetteRequestHeaders, "User-Agent") - } - - if m.ignoreAuthorization { - delete(requestHeader, "Authorization") - delete(cassetteRequestHeaders, "Authorization") + for _, header := range m.ignoreHeaders { + delete(requestHeader, header) + delete(cassetteRequestHeaders, header) } if !m.deepEqualContents(requestHeader, cassetteRequestHeaders) { diff --git a/pkg/cassette/cassette_test.go b/pkg/cassette/cassette_test.go index 21443a5..cefbe6c 100644 --- a/pkg/cassette/cassette_test.go +++ b/pkg/cassette/cassette_test.go @@ -231,8 +231,8 @@ func TestMatcher(t *testing.T) { }) }) - t.Run("IgnoreUserAgent=true", func(t *testing.T) { - matcherFn := NewDefaultMatcher(WithIgnoreUserAgent(true)) + t.Run("IgnoreUserAgent", func(t *testing.T) { + matcherFn := NewDefaultMatcher(WithIgnoreUserAgent()) t.Run("match", func(t *testing.T) { r, i := getMatcherRequests(t) @@ -251,8 +251,8 @@ func TestMatcher(t *testing.T) { }) }) - t.Run("IgnoreAuthorization=true", func(t *testing.T) { - matcherFn := NewDefaultMatcher(WithIgnoreAuthorization(true)) + t.Run("IgnoreAuthorization", func(t *testing.T) { + matcherFn := NewDefaultMatcher(WithIgnoreAuthorization()) t.Run("match", func(t *testing.T) { r, i := getMatcherRequests(t) @@ -268,4 +268,28 @@ func TestMatcher(t *testing.T) { } }) }) + + t.Run("IgnoreHeaders", func(t *testing.T) { + matcherFn := NewDefaultMatcher(WithIgnoreHeaders("Header-One", "Header-Two"), WithIgnoreUserAgent(), WithIgnoreAuthorization()) + + t.Run("match", func(t *testing.T) { + r, i := getMatcherRequests(t) + + r.Header = http.Header{ + "Header-One": {"foo"}, + "Header-Two": {"foo"}, + "User-Agent": {"foo", "bar"}, + } + + i.Headers = http.Header{ + "Header-One": {"bar"}, + "Header-Two": {"bar"}, + "Authorization": {"Bearer xyz"}, + } + + if b := matcherFn(r, i); !b { + t.Fatalf("request should have matched") + } + }) + }) }