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

fix: make nock.removeInterceptor ignore scope._persist and update scope.interceptors #2355

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

mbargiel
Copy link
Contributor

@mbargiel mbargiel commented May 21, 2022

Removing an interceptor with nock.removeInterceptor did not remove it from interceptor.scope.interceptors. And if interceptor.scope._persist is truthy, it also did not remove it from interceptor.scope.keyedInterceptors either.

Changes included in this PR:

  • Scope#remove also remove interceptors from scope.interceptors, but only when the key is found.
  • Scope#intercept no longer adds interceptor to this.interceptors. That's done by Scope#add so that this.interceptors and this.keyedInterceptors are consistent and contain the same interceptors.

Other change directly related to the above, but found while adding tests

  • Scope#remove no longer checks this._persist. The function is called from two places:
    • From Interceptor#markConsumed, which also checks scope._persist before calling Scope#remove
    • From nock.removeInterceptor, where it looks like a bug because it removes the interceptor from allInterceptors[baseUrl].interceptors before calling Scope#remove, which then does not remove the interceptor, leading to an inconsistent state.

Fixes #2353

@mbargiel mbargiel changed the title fix: fix nock.removeInterceptor to ignore scope._persist and update scope.interceptors fix: fix nock.removeInterceptor to ignore scope._persist and update scope.interceptors May 21, 2022
@mbargiel
Copy link
Contributor Author

After starting the work to fix #2353, I found another, related issue: if scope._persist is truthy, the interceptor would not be removed from scope.keyedInterceptors but it would still be removed from allInterceptors[baseUrl].interceptors. Let me know if you prefer to have that fix it in a separate PR. It's two commits, so it's easy to split out.

@mbargiel
Copy link
Contributor Author

Hey I realized after opening this PR that the commit messages don't follow the proper convention. I don't know if you squash PRs when merging of if you rebase them, so please let me know if you need me to rewrite the commit messages and force-push.

@mbargiel mbargiel changed the title fix: fix nock.removeInterceptor to ignore scope._persist and update scope.interceptors fix: make nock.removeInterceptor ignore scope._persist and update scope.interceptors Feb 17, 2024
@mbargiel
Copy link
Contributor Author

After starting the work to fix #2353, I found another, related issue: if scope._persist is truthy, the interceptor would not be removed from scope.keyedInterceptors but it would still be removed from allInterceptors[baseUrl].interceptors. Let me know if you prefer to have that fix it in a separate PR. It's two commits, so it's easy to split out.

@mikicho Before I put any additional effort on rebasing this work, could you please let me know if you are OK with taking both fixes in a single PR (this one) or it you want me to open a separate PR to fix Scope#remove when _persist is set?

Note: for consistency between scope.interceptors and scope.keyedInterceptors,
interceptors are now only pushed to scope.interceptors when they are added,
not when they are created. That guarantees both collections are consistent.
@mbargiel mbargiel force-pushed the feature/fix-2353-remove-interceptor-from-scope branch from d4ac1cf to 1e57041 Compare February 17, 2024 16:31
@mikicho
Copy link
Contributor

mikicho commented Feb 17, 2024

@mbargiel Thanks! Does this break current behavior?

@mbargiel
Copy link
Contributor Author

Hi @mikicho!

I don't think it breaks any existing, correct behaviour. It should just fix problematic cases. As I mentioned in the PR description:

Removing an interceptor with nock.removeInterceptor did not remove it from interceptor.scope.interceptors.

This means nock.removeInterceptor resulted in inconsistent scope state.

And if interceptor.scope._persist is truthy, it also did not remove it from interceptor.scope.keyedInterceptors either.

This means that when Scope#_persist is truthy, Scope#remove would leave the scope in an inconsistent state as well.

@mbargiel
Copy link
Contributor Author

Also, there was an inconsistency that arose from Scope#add and Scope#intercept each adding an interceptor to a different collection (this.interceptors and this.keyedInterceptors), which I believe should always be synchronized. The only way to guarantee they are always synchronized is to ensure they are both modified consistently by the same function call - in this case add made more sense, since remove is the symmetric point where they are removed.

This might be a mistake on my part if this.interceptors was somehow supposed to be a lazy cache of sorts, but that didn't feel like it. As a bonus of this change, this.interceptors will now be guaranteed to be sorted in the order of interceptor registration rather than in the order of interceptions. I don't know if this matters or not but it feels more deterministic (determinism is good!) and conceptually simpler/more in line with implicit expectations.

@mbargiel
Copy link
Contributor Author

mbargiel commented Feb 21, 2024

BTW I noticed all my PRs had code formatting issues. Have you considered adding husky and lint-staged to your package automation in order to automatically run prettier in a pre-commit hook? I could share my own configuration if you'd be interested (in a separate PR/issue of course). This would ensure that all contributors automatically have this set up, and running git commit would include an automatic call to prettier to re-format the staged files (important: this only touches staged changes, which is awesome).

@mikicho
Copy link
Contributor

mikicho commented Feb 24, 2024

@mbargiel It looks good in general.
I'm a bit concerned about the behavior changes in the remove persisted interceptor, which may break some users.

I am wondering if we need to include this in the beta and release this as a breaking change.

@gr2m
Copy link
Member

gr2m commented Feb 24, 2024

I am wondering if we need to include this in the beta and release this as a breaking change.

when in doubt, release it as part of the beta

@mbargiel
Copy link
Contributor Author

mbargiel commented Feb 24, 2024

Sure, I have no problem with that. I see now that it's indeed an API change, so it may be prudent to not include it directly in the next non-beta release, if that's your wish.

Just to explain why I believe this is a good change: personally, I think that scope.remove(key, scope.interceptors[0]) not removing the interceptor is very awkward and a violation of the principle of least surprise. (In this case: one can reasonably think that calling the opposite method of add, remove, would effectively remove the entry from the container...) I understood that an interceptor with persist used meant it is not removed when it intercepts a call, so it can intercept indefinitely; I did not understand that it made it unremovable from the scope, and that was a nasty surprise for us. It meant that you cannot share a common scope for a series of tests if you use persist in any one test, because that scope is then forever polluted with a sticky interceptor that won't go away even if you try to remove it; you are forced to trash the whole scope, create a new one and configure it.

@mikicho
Copy link
Contributor

mikicho commented Feb 24, 2024

I see your point.

  1. I'm wondering why these methods are not documented.
  2. Why do we need scope.remove at all if we have nock.removeInterceptor.

Is there a chance scope.remove/add was exposed by mistake? (we use them in removeInterceptor)
@gr2m @Uzlopak, maybe you can shed some light on this.

@gr2m
Copy link
Member

gr2m commented Feb 25, 2024

Is there a chance scope.remove/add was exposed by mistake? (we use them in removeInterceptor)
@gr2m @Uzlopak, maybe you can shed some light on this.

I don't know, might have been before I joined @nock? I'm okay removing access in beta and see if someone complaints, then we'll know 😁

@mikicho
Copy link
Contributor

mikicho commented Feb 25, 2024

@gr2m If we decide to remove them. i suggest deprecate them for 14 and remove in 15.

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.

nock.removeInterceptor with an Interceptor instance does not remove it from its scope
3 participants