Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add advanced filtering #32

Merged
merged 16 commits into from
Oct 24, 2018
Merged

Conversation

klauspost
Copy link
Contributor

When running tests where multiple request types are used, it if quite difficult to set up proper filtering.

To help this, I made it possible to chain multiple filters and check URL and method.

Furthermore the URL can now be modified.

For future-proofing the Request and Response structs can now be extended with more stuff without breaking compatibility.

If you like this, I can add some tests for the new functionality.

When running tests where multiple request types are used, it if quite difficult to set up proper filtering.

To help this, I made it possible to chain multiple filters and check URL and method.

Furthermore the URL can now be modified.

For future-proofing the Request and Response structs can now be extended with more stuff without breaking compatibility.

If you like this, I can add some tests for the new functionality.
@klauspost
Copy link
Contributor Author

After doing some examples I've decided to re-write some of the stuff...

It is a bit too clunky to use now.

@klauspost
Copy link
Contributor Author

klauspost commented Oct 22, 2018

@seborama I have added examples for using the specific filtering. If there is more I can do, please let me know.

I think it is now reasonably easy to use and allows for much flexibility. Adding some unit tests to the filters should be fairly easy if you like it overall.

@seborama
Copy link
Owner

seborama commented Oct 22, 2018

Hi @klauspost,

Thanks for contributing.
I need a little more context on your use case. Could you expand?
Do you have a working example to illustrate?

Regards

@seborama
Copy link
Owner

I just noticed you have also posted some examples...

@klauspost
Copy link
Contributor Author

klauspost commented Oct 22, 2018

To give the overall picture, I need some tests to test functionality that can do multiple calls to the same endpoint.

The endpoint will return unique IDs, so I need to be able to flexibly adjust content of the returned bodies. URLs typically have IDs as well, so I need to be able to still match content even though IDs change.

A huge pain point was that it wasn't possible to do URL/method matching, so transforms would only happen when specific combinations were hit. This will record requests with request transformations applied, so "transformed" requests would be stored and allow for more flexible matching.

The unmodified request is given to the response filters, so they can still operate on the full information given.

govcr.go Outdated
// Original filter is unmodified.
func (r RequestFilter) OnPath(pathRegEx string) RequestFilter {
if pathRegEx == "" {
pathRegEx = "*"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a valid regex.
Use "." or ".*"

govcr.go Outdated
// Original filter is unmodified.
func (r ResponseFilter) OnPath(pathRegEx string) ResponseFilter {
if pathRegEx == "" {
pathRegEx = "*"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"*" is not a valid regex.
use "." or ".*"

govcr.go Outdated
*r = v
}

// First one or more filters before the current ones.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prepend rather?

govcr.go Outdated
}

// First one or more filters before the current ones.
func (r ResponseFilters) First(filters ...ResponseFilter) ResponseFilters {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prepend rather?

govcr.go Outdated
@@ -270,8 +532,23 @@ type RequestFilterFunc func(http.Header, []byte) (*http.Header, *[]byte)
// Return values:
// - value 1 - Response's amended header
// - value 2 - Response's amended body
// Deprecated: Use ResponseFilterFunc instead.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going to go down this road, let's get rid off ResponseFilterFunc altogether.
This would form v3 of govcr.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Just didn't want to break it unless you were fine with it.

govcr.go Outdated
@@ -28,12 +30,21 @@ const defaultCassettePath = "./govcr-fixtures/"
type VCRConfig struct {
Client *http.Client
ExcludeHeaderFunc ExcludeHeaderFunc

// Deprecated, use RequestFilter
Copy link
Owner

@seborama seborama Oct 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going to go down this road, let's get rid of RequestFilterFunc altogether.
This would form v3 of govcr.

govcr.go Outdated
RequestFilterFunc RequestFilterFunc

// ResponseFilterFunc can be used to modify the header of the response.
// This is useful when a fingerprint is exchanged and expected to match between request and response.
// Deprecated, use ResponseFilter
Copy link
Owner

@seborama seborama Oct 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going to go down this road, let's get rid of ResponseFilterFunc altogether.
This would form v3 of govcr.

}

// print outcome.
fmt.Println("Status code:", resp.StatusCode, " (should be 404 on real and 202 on replay)")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should form the basis of a formal go example test (as in the root folder xxx_examplen_test.go and some unit tests too.


// Add header if method was "GET"
govcr.ResponseFilter(func(resp govcr.Response) govcr.Response {
resp.Header.Add("method-was-get", "true")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

best to use a different value such as present or added etc
when using 'true', this is converted to a boolean by the http client which then cannot be displayed by Println().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what you mean, it prints fine:

method-was-post: true (should be true on replay on POST)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, odd! On my environment the word 'true' is missing (go 1.11.1)


// Add header if method was "POST"
govcr.ResponseFilter(func(resp govcr.Response) govcr.Response {
resp.Header.Add("method-was-post", "true")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

best to use a different value such as present or added etc
when using 'true', this is converted to a boolean by the http client which then cannot be displayed by Println().

govcr.go Outdated
// ResponseFilters is a slice of ResponseFilter
type ResponseFilters []ResponseFilter

// ResponseContext provides the response parameters.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a ResponseContext

govcr.go Outdated
}
}

// AddHeaderValue will add or overwrite a header to the request
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method says RequestAddHeaderValue?

govcr.go Outdated
}
}

// DeleteHeaderKeys will delete one or more header keys on the request
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method says RequestDeleteHeaderKeys

govcr.go Outdated
}
}

// ResponseDeleteHeader will delete one or more headers on the response when returned from vcr playback.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method says ResponseDeleteHeaderKeys

govcr.go Outdated
}
}

// Chain one or more filters after the current one and return as single filter.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid comment: method name is Append

@seborama
Copy link
Owner

seborama commented Oct 22, 2018

Hi,

Great contribution! I think this PR should take us straight to the long outstanding v3 of govcr.

I have some comments:

  • the units tests have not been written - ahem...
  • new example tests (as in go xxx_example.go in the root of the project) are needed and some of the visual checks in particular in examples/example6.go can be migrated there.
  • the doc needs updating to reflect the removal of FilterFunc and introduction of Filters
  • I've also posted a few more comments on the PR
  • I'd probably move the RequestFilter and ResponseFilter receiver functions each in their own separate go source file.

On a more philosophical note, I am concerned with examples/example6.go. It shows a deep transfiguration of the recorded track at replay time. This is likely to lead to complex test code that will require a degree maintenance and incur a steep learning curve for new joiners. (This was the original reason behind the imposed usage restrictions.)

Regards.

@klauspost
Copy link
Contributor Author

the units tests have not been written - ahem...

Indeed. Didn't want to spend the time upfront if you didn't like the concept.

new example tests (as in go xxx_example.go in the root of the project) are needed and some of the visual checks in particular in examples/example6.go can be migrated there.

👍

the doc needs updating to reflect the removal of FilterFunc and introduction of Filters
👍

I'd probably move the RequestFilter and ResponseFilter receiver functions each in their own separate go source file.

👍

It shows a deep transfiguration of the recorded track at replay time.

Technically it is not much that couldn't be achieved before, just in a more available interface.

Things like testing external interfaces often includes complex logic, and having it available for advanced needs can be a positive.

@seborama
Copy link
Owner

Sounds good. I realise there's quite a bit in my comments. Don't feel coerced into doing all. I can pick up some of it if you like.

Remove Append and make Prepend operate on pointer and not return new slice.
@klauspost
Copy link
Contributor Author

klauspost commented Oct 24, 2018

Sounds good. I realise there's quite a bit in my comments. Don't feel coerced into doing all. I can pick up some of it if you like.

No problem. I have added tests for all new functions and added the examples as tests as well.

Made a few tweaks. I removed "Append" and changed "Prepend" to not return the updated slice - seems to just be a way of forgetting to re-assign the returned value.

I can't really tell if ExcludeHeaderFunc is the same as RequestDeleteHeaderKeys. As far as I can tell it is. If so, I guess it can just be removed.

I don't have plans for more changes unless you ask me to :)

@seborama
Copy link
Owner

Thanks. Let me know when you're happy with the PR and I'll merge it. It would be nice if at least the docs were updated 😄

@seborama
Copy link
Owner

Sorry I'm reading this on my mobile and I seem to consistently fail to read your messages in whole. I just noticed you said you're done. I'll merge when I get a chance.

Thanks for your time and for sharing your contributions. I'm sure many people will be grateful too.

Bonne chance!

@seborama seborama merged commit a744811 into seborama:master Oct 24, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants