fix(tests): assert the oauth identity link without comparing ids - #1382
Merged
Merged
Conversation
google.spec.js and discord.spec.js asserted that a user's id equals
their identity's id:
expect(status.id).toEqual(status.identities[0].id);
status.id is a User primary key; status.identities[0].id is an Identity
primary key. They are separate tables with independent sequences and no
reason to match -- the field carrying the link is identities[0].user.
The assertion only held while the two sequences happened to coincide.
Dev uses sails-disk, so the dev server, Cypress and the Sails unit tests
share one on-disk database in .tmp/localDiskDb. sails-disk seeds each
sequence at lift time from the highest id on disk and keeps it in
memory, and wipeDatabase deletes rows without lowering it. Cypress
creates many users and almost no identities, so User races ahead of
Identity and these six assertions fail until the two realign. It does
not self-heal: each failing run leaves its own users on disk to seed the
next lift. CI never sees it because every job starts from a fresh
checkout with no .tmp.
Swapping .id for .user would stop the failures but assert nothing:
User.findOne({ id }).populate('identities') already selects only rows
whose user matches, so it cannot fail. Instead assert what these three
tests are actually checking -- that an oauth identity got linked to the
account:
expect(status.identities).toHaveLength(1);
expect(status.identities[0].provider).toEqual('google');
That also covers two things the original could not: that exactly one
identity exists, so a duplicate-identity bug on repeat login cannot pass
silently, and that it came from the expected provider, which matters now
that Google and Discord share this code path.
expect(status.id).toEqual(userRes.body) is left alone -- comparing the
session user to the id returned by signup is meaningful.
Also stops wipe-database.js reporting success when the wipe throws,
which left callers running against a partially wiped database with no
signal.
Resolves #1381
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JnmfJeHNxebpfcDzSN8PqV
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Issue number
Relevant issue number
What this fixes
Six assertions in
google.spec.jsanddiscord.spec.jscompared aUserid to anIdentityid:Separate tables, independent sequences, no reason to match — the field carrying the link is
identities[0].user. The assertion only held while the two sequences happened to coincide, so it fails locally after anything creates users without identities.Full mechanism (shared
sails-diskdatastore, sequences seeded from the on-disk max at lift,wipeDatabasenot lowering them) is written up in #1381.Why not just swap
.idfor.userThat stops the failures but asserts nothing.
User.findOne({ id }).populate('identities')already selects only rows whoseusermatches the id being queried, so the comparison can never fail — it would trade a fragile assertion for a vacuous one.Instead, assert what those three tests are actually checking, which is that an OAuth identity got linked to the account:
This also covers two things the original could not:
[0], so a duplicate-identity bug on repeat login passed silently, which is a live risk given the linking branch inOAuthControllerexpect(status.id).toEqual(userRes.body)(4 call sites) is deliberately left alone: comparing the session user to the id returned by signup is meaningful and correct.Also included
api/helpers/wipe-database.jsreported success when the wipe threw:No caller checks that return value, so a partial or failed wipe left every downstream test running against a dirty database with no signal. Flagged as secondary in #1381.
Please check the following
Test-only change plus one helper; the existing specs are the coverage.
Please describe additional details for testing this change
Verified against a live failing baseline
My
.tmp/localDiskDb/was still carrying the drift from reproducing #1381, so there was a genuinely failing starting state rather than a synthetic one:main, drifted DB65≠13,66≠14,68≠16,61≠9,62≠10,64≠12The gap widened to 70 during the run and the tests passed anyway, which is the point — the assertions no longer read id values.
npm run test:unitthen passed 86/86 on a first run in the state that previously guaranteed 6 failures.Mutation-tested
Since my objection to the
.useralternative was that it can't fail, I checked the replacement can:provider→'discord'in the google spectoHaveLength(2)Full suite
wipeDatabaseruns at the top of nearly every spec, so the error-propagation change needed broad coverage rather than a spot check:npm run lint— cleannpm run test:unit— 86 / 86npx cypress run— 34 specs, 375 passing, 0 failing, 5 pending (pre-existing skips)Reproducing the original failure
rm -rf .tmp/localDiskDb && npm run start:devnpx cypress run --spec "tests/e2e/specs/out-of-game/home.spec.js"npm run test:unit— fails onmain, passes on this branch🤖 Generated with Claude Code
https://claude.ai/code/session_01JnmfJeHNxebpfcDzSN8PqV