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

feat: add pending interceptor check functions to mock agent #1358

Merged
merged 10 commits into from Apr 29, 2022

Conversation

theneva
Copy link
Contributor

@theneva theneva commented Apr 21, 2022

Hi! 👋

This adds functions to check any pending interceptors on a mock agent, as well as a function that throws with a friendly, emoji-ful error message if there are any pending interceptors.

I think this closes #1002

I've never contributed here before, so please let me know if I should change anything! 🙌

(Huge thanks to @SimenB for helping me port it from our own repo)

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

Good work!!!

lib/mock/mock-agent.js Outdated Show resolved Hide resolved
lib/mock/table-formatter.js Outdated Show resolved Hide resolved
@theneva
Copy link
Contributor Author

theneva commented Apr 22, 2022

Thanks for the review! I'm aware of some other nits and I'll address the review comments soon :)

@mcollina
Copy link
Member

Also CI is failing

@theneva
Copy link
Contributor Author

theneva commented Apr 22, 2022

Yep, I think CI fails because I drop ansi colouring in the output if process.env.CI is set. Gonna take a look!

(Is reading that env var okay, or should I add a separate one? As an unfortunate Google Cloud Build user, I really appreciate it when tools default to no colours on CI.)

@codecov-commenter
Copy link

codecov-commenter commented Apr 22, 2022

Codecov Report

Merging #1358 (6af598d) into main (a8c2918) will increase coverage by 0.03%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##             main    #1358      +/-   ##
==========================================
+ Coverage   94.25%   94.29%   +0.03%     
==========================================
  Files          45       47       +2     
  Lines        4211     4240      +29     
==========================================
+ Hits         3969     3998      +29     
  Misses        242      242              
Impacted Files Coverage Δ
lib/mock/mock-agent.js 100.00% <100.00%> (ø)
lib/mock/mock-utils.js 100.00% <100.00%> (ø)
lib/mock/pending-interceptors-formatter.js 100.00% <100.00%> (ø)
lib/mock/pluralizer.js 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a8c2918...6af598d. Read the comment docs.

@theneva
Copy link
Contributor Author

theneva commented Apr 22, 2022

I think it should be good now, but the linter will trip until #1361 lands I think

@mcollina
Copy link
Member

Can you rebase? The linter landed

@theneva
Copy link
Contributor Author

theneva commented Apr 23, 2022

I merged main into my branch :) everything passed locally, so hopefully it all works now

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

lgtm

@SimenB could you take a look?

Copy link
Member

@SimenB SimenB left a comment

Choose a reason for hiding this comment

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

This nicely covers the use case I had in mind when opening the issue. Thanks! 👍

docs/api/MockAgent.md Show resolved Hide resolved
lib/mock/mock-agent.js Outdated Show resolved Hide resolved
lib/mock/mock-agent.js Outdated Show resolved Hide resolved
lib/mock/mock-agent.js Outdated Show resolved Hide resolved
@@ -0,0 +1,138 @@
'use strict'
Copy link
Member

Choose a reason for hiding this comment

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

there are no tests for persist, can you add?

Also, I think a persisted interceptor should error if it's never called (I don't know if the logic does that today)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

there are no tests for persist, can you add?

Yep!

Also, I think a persisted interceptor should error if it's never called (I don't know if the logic does that today)

I don't think that's currently possible with the way Undici counts requests, since the times value is only set if times(<number>) has been called. In this PR I "cheat" by treating times as 1 if neither persist nor times is set.

It could definitely be solved by tracking the call count counting upwards to either a limit set by times(), 1 if neither persist nor times is set, or forever if persist is set, before it stops serving the mocked response.

I can give it a try, but that feels like it might belong in a separate pull request. On the other hand, it would trip on a new assertion and thus be a breaking change to the API introduced in this PR, so maybe it belongs here…

Just so I don't waste time on code that will be rejected: @mcollina or @ronag are you open to me changing the call count tracking in this PR so it can all land at once?

Copy link
Member

Choose a reason for hiding this comment

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

Go for it!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done 😅

Copy link
Member

@SimenB SimenB left a comment

Choose a reason for hiding this comment

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

nice!

@theneva
Copy link
Contributor Author

theneva commented Apr 25, 2022

Okay, @SimenB and I sat down together, and greatly simplified the changeset.

Feature-wise, the reporter now:

  1. Reports invocation count (timesInvoked) and expected number of invocations
  2. No longer attempts to report counts for consumed interceptors (this can't be done consistently)
  3. Trips on persistent interceptors that have never been invoked
  4. Reports the Origin of pending interceptors

We also switched from calling interceptors "(un)consumed", and instead use "pending". consumed and pending are mostly opposites, with one exception: A persistent interceptor that has been invoked at least once is pending, but not consumed.

This came with more tests and updated docs. It'd be great if you took another look 😄

Edit: Oh, and we renamed the TableFormatter thing to PendingInterceptorsFormatter and pass it an array of PendingInterceptors instead of just treating it like console#table. That should make it a whole lot easier to build other reporters, and I think it made things less confusing.

@@ -107,9 +107,9 @@ function getMockDispatch (mockDispatches, key) {
}

function addMockDispatch (mockDispatches, key, data) {
const baseData = { times: null, persist: false, consumed: false }
const baseData = { timesInvoked: 0, times: 1, persist: false, consumed: false }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we default times to 1 here so we don't have to do null checking everywhere

const replyData = typeof data === 'function' ? { callback: data } : { ...data }
const newMockDispatch = { ...baseData, ...key, data: { error: null, ...replyData } }
const newMockDispatch = { ...baseData, ...key, pending: true, data: { error: null, ...replyData } }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

All interceptors are pending as they are first registered. I think consumed belongs here as well (and not in baseData), but… meh. I suppose it might be possible to register an interceptor that should not be pending by registering it with .times(0), but that sounds too illogical to account for.

@@ -230,25 +230,20 @@ function mockDispatch (opts, handler) {
const key = buildKey(opts)
const mockDispatch = getMockDispatch(this[kDispatches], key)

mockDispatch.timesInvoked++
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We now increase timesInvoked instead of decreasing times (which is now constant).

(Is invoked a good name, or would something like timesIntercepted be better?)

}
// If it's used up and not persistent, mark as consumed
mockDispatch.consumed = !persist && timesInvoked >= times
mockDispatch.pending = timesInvoked < times
Copy link
Contributor Author

Choose a reason for hiding this comment

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

consumed is the same as before, but pending is new.

Persistent: persist ? '✅' : '❌',
Invocations: timesInvoked,
Remaining: persist ? Infinity : times - timesInvoked
}))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we moved the header name mapping here so that the formatter gets proper lowerCamelCased variable names passed in. I think that makes it less annoying to build a new formatter. Otherwise, this does the same thing as before

@@ -0,0 +1,138 @@
'use strict'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done 😅

return
}

const pluralizer = new Pluralizer('interceptor', 'interceptors').pluralize(pending.length)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

the pluralizer API ends up being a little more complicated than it maybe needs to now that we only track one array (instead of consumed, tooFewUses, and persistent as we did before), but I think it's still a nice little utility.

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

lgtm

@SimenB
Copy link
Member

SimenB commented Apr 25, 2022

Okay, @SimenB and I sat down together

Exclude my name from the commit, this is 99.99% @theneva. I just shouted encouragement from the back seat! 😀

That said, this PR is an awesome implementation of my feature request! ♥️

@theneva
Copy link
Contributor Author

theneva commented Apr 29, 2022

Awesome! Is there anything else I can do to help this land? 😄

@mcollina mcollina merged commit 2e5171b into nodejs:main Apr 29, 2022
@theneva theneva deleted the unused-mock-interceptor branch April 29, 2022 20:36
@SimenB
Copy link
Member

SimenB commented Apr 30, 2022

Yay!

@SimenB
Copy link
Member

SimenB commented May 2, 2022

Testing this out, works great 👍

image

not sure if [Function: path] should be attempted to prettify somehow? Looks fine to me, and not sure of any other way it could be better.


Only thing I can think of that might make this even better is to capture an Error for every intercept call, to have a stack trace back to where the interceptor was registered.

KhafraDev pushed a commit to KhafraDev/undici that referenced this pull request Jun 23, 2022
metcoder95 pushed a commit to metcoder95/undici that referenced this pull request Dec 26, 2022
crysmags pushed a commit to crysmags/undici that referenced this pull request Feb 27, 2024
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.

Add nock-like "expectations" to mocks
5 participants