-
Notifications
You must be signed in to change notification settings - Fork 3
CT-2332 | Fix false-success during transport when websockets are turned on #403
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
Open
codingLogan
wants to merge
7
commits into
master
Choose a base branch
from
lr/CT-2332-transport-hardening
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
0a582a8
fix(connecting): do not treat firefly's pre-job CONNECTED update as t…
codingLogan 36c0b65
test(connecting): spell out that firefly's assigned job is observed a…
codingLogan 9e3b11b
fix(connecting): assume most_recent_job_guid is always sent
codingLogan f27654a
test(connecting): cover the 409 path through Connecting and drop the …
codingLogan 07864dc
test(connecting): move the CT-2332 integration tests next to runJobSc…
codingLogan bc1ce76
test(connecting): make the runJobSchedule scenarios read as given/whe…
codingLogan 6dd26a5
test(connecting): let the shared render helper own the store and ApiP…
codingLogan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| import { waitFor } from 'src/utilities/testingLibrary' | ||
| import { POST_MESSAGES } from 'src/const/postMessages' | ||
| import { ReadableStatuses } from 'src/const/Statuses' | ||
| import { JOB_TYPES } from 'src/const/consts' | ||
| import { STEPS, VERIFY_MODE } from 'src/const/Connect' | ||
| import { ACTIONABLE_ERROR_CODES } from 'src/views/actionableError/consts' | ||
| import { | ||
| createFakeBackend, | ||
| createFakeBrokaw, | ||
| expectMemberConnected, | ||
| HttpError, | ||
| Member, | ||
| REDIRECT_JOB_GUID, | ||
| renderConnecting, | ||
| staleOAuthMember, | ||
| } from 'src/utilities/test/connectingOAuthHarness' | ||
|
|
||
| // fadeOut (Velocity) never resolves in jsdom; Connecting's error path dispatches inside its .then. | ||
| vi.mock('src/utilities/Animation', () => ({ fadeOut: vi.fn(() => Promise.resolve()) })) | ||
|
|
||
| /** | ||
| * runJobSchedule$ drives the Connecting step's job schedule. These tests run it through the | ||
| * real <Connecting /> (real store, hook and transport); only the API and brokaw are faked. | ||
| * | ||
| * CT-2332: firefly sets an OAuth member CONNECTED on the redirect before any job exists. | ||
| * Over websockets, a copy of that member update can reach the widget *after* it has started | ||
| * its own job. It looks finished (CONNECTED, not aggregating) but names no job, or the job the | ||
| * member had before. The widget must not mistake it for its job finishing. | ||
| */ | ||
|
|
||
| const OUR_JOB_GUID = `JOB-${JOB_TYPES.VERIFICATION}` | ||
|
|
||
| // The member as firefly leaves it on the OAuth redirect: CONNECTED, idle, no job yet. | ||
| const connectedWithNoJob: Member = { | ||
| ...staleOAuthMember, | ||
| connection_status: ReadableStatuses.CONNECTED, | ||
| } | ||
|
|
||
| const runningJob = (jobGuid: string): Member => ({ | ||
| ...connectedWithNoJob, | ||
| is_being_aggregated: true, | ||
| most_recent_job_guid: jobGuid, | ||
| }) | ||
|
|
||
| const finishedJob = (jobGuid: string): Member => ({ | ||
| ...connectedWithNoJob, | ||
| most_recent_job_guid: jobGuid, | ||
| }) | ||
|
|
||
| const impededWithNoEligibleAccounts = (jobGuid: string): Member => ({ | ||
| ...staleOAuthMember, | ||
| connection_status: ReadableStatuses.IMPEDED, | ||
| most_recent_job_guid: jobGuid, | ||
| error: { error_code: ACTIONABLE_ERROR_CODES.NO_ELIGIBLE_ACCOUNTS }, | ||
| }) | ||
|
|
||
| const expectNoEligibleAccountsScreen = async (widget: ReturnType<typeof renderConnecting>) => { | ||
| // Wait for Connecting to route anywhere, then check where. Before the fix it routed to | ||
| // CONNECTED as soon as the stale update arrived. | ||
| await waitFor(() => expect(widget.currentStep()).toBeDefined(), { timeout: 4000 }) | ||
| expect(widget.currentStep()).toBe(STEPS.ACTIONABLE_ERROR) | ||
| expect(widget.onPostMessage).not.toHaveBeenCalledWith( | ||
| POST_MESSAGES.MEMBER_CONNECTED, | ||
| expect.anything(), | ||
| ) | ||
| } | ||
|
|
||
| describe('runJobSchedule$ through <Connecting /> over websockets', () => { | ||
| afterEach(() => { | ||
| vi.restoreAllMocks() | ||
| }) | ||
|
|
||
| it('ignores the late CONNECTED update with no job and shows the real outcome of the job it started', async () => { | ||
| // Given a first-time OAuth member: no job has ever run on it. | ||
| const backend = createFakeBackend() | ||
| const brokaw = createFakeBrokaw() | ||
| const widget = renderConnecting( | ||
| backend, | ||
| { mode: VERIFY_MODE }, | ||
| { webSocket: brokaw.connection }, | ||
| ) | ||
|
|
||
| // When the widget starts its verification job... | ||
| await widget.runJobCalled() | ||
| // ...and firefly's pre-job update arrives late, looking finished but naming no job... | ||
| await brokaw.memberUpdated(connectedWithNoJob) | ||
| // ...then the widget's job actually finishes with no eligible accounts. | ||
| await brokaw.memberUpdated(impededWithNoEligibleAccounts(OUR_JOB_GUID)) | ||
|
|
||
| // Then the widget shows the error, never a success. | ||
| await expectNoEligibleAccountsScreen(widget) | ||
| }) | ||
|
|
||
| it('ignores the late CONNECTED update that still names a returning member’s previous job', async () => { | ||
| // Given a returning member whose previous job was also a verification. Attributing that | ||
| // old job by type would wrongly complete the schedule. | ||
| const PREVIOUS_JOB_GUID = 'JOB-old' | ||
| const returningMember = finishedJob(PREVIOUS_JOB_GUID) | ||
| const backend = createFakeBackend({ member: returningMember }) | ||
| backend.jobs[PREVIOUS_JOB_GUID] = { guid: PREVIOUS_JOB_GUID, job_type: JOB_TYPES.VERIFICATION } | ||
| const brokaw = createFakeBrokaw() | ||
| const widget = renderConnecting( | ||
| backend, | ||
| { mode: VERIFY_MODE }, | ||
| { webSocket: brokaw.connection, member: returningMember }, | ||
| ) | ||
|
|
||
| // When the widget starts a new verification job... | ||
| await widget.runJobCalled() | ||
| // ...and firefly's pre-job update arrives late, still naming the previous job... | ||
| await brokaw.memberUpdated(returningMember) | ||
| // ...then the new job finishes with no eligible accounts. | ||
| await brokaw.memberUpdated(impededWithNoEligibleAccounts(OUR_JOB_GUID)) | ||
|
|
||
| // Then the widget shows the error, never a success. | ||
| await expectNoEligibleAccountsScreen(widget) | ||
| }) | ||
|
|
||
| it('observes the job firefly assigned when its own runJob is rejected with a 409', async () => { | ||
| // Given firefly already started the verification job on the redirect | ||
| // (disable_background_agg clients), so the widget's own runJob is a duplicate. | ||
| const backend = createFakeBackend() | ||
| backend.runJob.mockImplementationOnce(async () => { | ||
| backend.startJob(REDIRECT_JOB_GUID, JOB_TYPES.VERIFICATION) | ||
| throw new HttpError(409) | ||
| }) | ||
| const brokaw = createFakeBrokaw() | ||
| const widget = renderConnecting( | ||
| backend, | ||
| { mode: VERIFY_MODE }, | ||
| { webSocket: brokaw.connection }, | ||
| ) | ||
|
|
||
| // When the widget's runJob is rejected... | ||
| await widget.runJobCalled() | ||
| // ...the late pre-job update is still ignored... | ||
| await brokaw.memberUpdated(connectedWithNoJob) | ||
| // ...and firefly's job is seen running, then finishing. | ||
| await brokaw.memberUpdated(runningJob(REDIRECT_JOB_GUID)) | ||
| await brokaw.memberUpdated(finishedJob(REDIRECT_JOB_GUID)) | ||
|
|
||
| // Then the widget completes against firefly's job without starting another. | ||
| await expectMemberConnected(widget.onPostMessage) | ||
| expect(backend.runJob).toHaveBeenCalledTimes(1) | ||
| expect(backend.loadJob).toHaveBeenCalledWith(REDIRECT_JOB_GUID) | ||
| }) | ||
| }) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels a lot better. Can we follow our new pattern and put this directly next to the file instead of in a tests folder?