Skip to content

CT-2332 | Fix false-success during transport when websockets are turned on - #403

Open
codingLogan wants to merge 7 commits into
masterfrom
lr/CT-2332-transport-hardening
Open

codingLogan wants to merge 7 commits into
masterfrom
lr/CT-2332-transport-hardening

Conversation

@codingLogan

@codingLogan codingLogan commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

Currently there is a false-success condition that happens during OAuth, when it really ends in a No DDA scenario

  1. A member is created for OAuth
  2. The member returns from OAuth to firefly, and is marked as CONNECTED by Firefly (and is not yet aggregating)
  3. The widget's signal is to stop polling if CONNECT and is_being_aggregated is false. Because it gets this signal, it thinks the job is done and exits successfully
  4. ⚠️ We expect the UI to show the No DDA messaging, not a Successes

Before (sometimes)

The end of the OAuth Verification flow with No DDA ends in "Success" - Not true though 😞

After (consistent now)

The end of the Verification flow with No DDA ends in the "No Eligible Accounts screen" - 😄 Correct message for the user

Cause

If the websockets are fast and the systems send the RPC messages in the problematic order, it falls into this problem. It is a race-condition type of behavior. This branch fixes the problem.

@codingLogan codingLogan self-assigned this Sep 24, 2026
@codingLogan
codingLogan force-pushed the lr/CT-2332-transport-hardening branch from 230b029 to 3cc9383 Compare September 24, 2026 22:46
…he finished job

Firefly sets an OAuth member to CONNECTED on the redirect before any job
exists. Over websockets that update can arrive after the widget has started
its own job; runJobSchedule$ took it as "done" and, unable to load a job,
assumed the job it had started finished, showing Success! while the real
job ended IMPEDED (CT-2332).

The update names the job the member had before runJob was called: null for
a first job, the previous job's guid for a returning member. runJobSchedule$
now records that guid when it calls runJob and keeps observing while a
CONNECTED idle update still names it. `undefined` is left alone because
hosts are not required to send the field. After a 409 the widget started
nothing, so only the null case is detectable there.

Verified in SAND with websockets on: 50/50 NoDDA runs pass (was ~30%
failing).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@codingLogan
codingLogan force-pushed the lr/CT-2332-transport-hardening branch from 3cc9383 to 0a582a8 Compare September 24, 2026 23:51
codingLogan and others added 2 commits September 24, 2026 18:59
…fter a 409

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The repo is moving internal and the backend always sends the field (a
guid or null), so the gate no longer distinguishes undefined from null.
Drops the test that pinned that distinction and the doc sentence that
told hosts they could omit the field.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@codingLogan
codingLogan marked this pull request as ready for review September 25, 2026 02:32
…runJobSchedule unit tests

The unit file fed hand-built polling states straight into runJobSchedule$,
so it asserted assumptions about what the poller emits rather than what
the rendered widget does. Every case now runs through Connecting with the
real store, hook and transport, driven by the fake API and a Subject in
place of brokaw: the first-time member, the returning member, and now the
409 path where firefly's redirect job is the one observed. All three fail
against the previous runner.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread src/utilities/runJobSchedule.js
* CT-2332: firefly sets the member CONNECTED on the OAuth redirect before any job exists,
* and over websockets that frame can arrive after the widget has started its job.
*/
describe('<Connecting /> after OAuth over websockets', () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

These tests feel pretty difficult to comprehend

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, they are complex because they deal with observables firing based on network events, which are hard to mock correctly. This isn't something that can be really simplified, unfortunately. I did add tests, and uncommented failing e2e tests in the GitLab side to cover more of this though.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'll have good ol' Botly see if we can simplify how it reads

codingLogan and others added 3 commits September 25, 2026 13:47
…hedule

The three websocket scenarios (first-time member, returning member, 409
path) now live in src/utilities/__tests__/runJobSchedule-test.tsx so the
coverage sits beside the code it proves. They still render the real
<Connecting /> with the real store, hook and transport; only the API and
brokaw are faked.

The fake backend and render harness they share with ConnectingOAuthJobs-test
move to src/utilities/test/connectingOAuthHarness.tsx. ConnectingOAuthJobs-test
keeps its original eight tests unchanged and no longer needs the fadeOut mock.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n/then

The Subject plumbing and settle calls moved into the harness behind a fake
brokaw (`createFakeBrokaw().memberUpdated(member)`) and a `runJobCalled()`
step on the rendered widget. Member fixtures are named for what they
represent (connectedWithNoJob, runningJob, finishedJob,
impededWithNoEligibleAccounts) and each test narrates its steps.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rovider in the harness

The harness no longer hand-builds a redux store or nests its own ApiProvider;
it passes preloadedState and apiValue to src/utilities/testingLibrary's
render, like the rest of the suite. Exports are trimmed to what the two test
files use. Test behavior is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@@ -0,0 +1,147 @@
import { waitFor } from 'src/utilities/testingLibrary'

Copy link
Copy Markdown
Collaborator

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?

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