Skip to content

fix(tests): assert the oauth identity link without comparing ids - #1382

Merged
itsalaidbacklife merged 2 commits into
mainfrom
fix/oauth-identity-assertions
Sep 18, 2026
Merged

itsalaidbacklife merged 2 commits into
mainfrom
fix/oauth-identity-assertions

Conversation

@itsalaidbacklife

Copy link
Copy Markdown
Contributor

Issue number

Relevant issue number

What this fixes

Six assertions in google.spec.js and discord.spec.js compared a User id to an Identity id:

expect(status.id).toEqual(status.identities[0].id);

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-disk datastore, sequences seeded from the on-disk max at lift, wipeDatabase not lowering them) is written up in #1381.

Why not just swap .id for .user

That stops the failures but asserts nothing. User.findOne({ id }).populate('identities') already selects only rows whose user matches 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:

expect(status.identities).toHaveLength(1);
expect(status.identities[0].provider).toEqual('google');   // 'discord' in the other spec

This also covers two things the original could not:

  • exactly one identity — the old assertion only inspected [0], so a duplicate-identity bug on repeat login passed silently, which is a live risk given the linking branch in OAuthController
  • the right provider — a Google/Discord mix-up is currently invisible now that both share this code path

expect(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.js reported success when the wipe threw:

} catch (err) {
  return exits.success(false);   // -> now exits.error(err)
}

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

  • Do the tests still pass? (see Run the Tests)
  • Is the code formatted properly? (see Linting (Formatting))
  • For New Features:
    • Have tests been added to cover any new features or fixes?
    • Has the documentation been updated accordingly?

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:

User max id Identity max id Result
main, drifted DB 60 8 6 failed — 65≠13, 66≠14, 68≠16, 61≠9, 62≠10, 64≠12
This branch, same DB 78 8 10 passed

The 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:unit then passed 86/86 on a first run in the state that previously guaranteed 6 failures.

Mutation-tested

Since my objection to the .user alternative was that it can't fail, I checked the replacement can:

Mutation Result
provider → 'discord' in the google spec fails ✓
toHaveLength(2) fails ✓

Full suite

wipeDatabase runs at the top of nearly every spec, so the error-propagation change needed broad coverage rather than a spot check:

  • npm run lint — clean
  • npm run test:unit — 86 / 86
  • Full npx cypress run — 34 specs, 375 passing, 0 failing, 5 pending (pre-existing skips)

Reproducing the original failure

  1. rm -rf .tmp/localDiskDb && npm run start:dev
  2. npx cypress run --spec "tests/e2e/specs/out-of-game/home.spec.js"
  3. Stop the dev server
  4. npm run test:unit — fails on main, passes on this branch

🤖 Generated with Claude Code

https://claude.ai/code/session_01JnmfJeHNxebpfcDzSN8PqV

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
Comment thread api/helpers/wipe-database.js Outdated
@itsalaidbacklife itsalaidbacklife added version-patch An update that warrants a bumping the project's patch version (e.g. 4.0.0 => 4.0.1) dev experience Improvements to the code base that make it easier/better/more enjoyable to contribute to Cuttle labels Sep 18, 2026
@itsalaidbacklife
itsalaidbacklife merged commit fc02d4c into main Sep 18, 2026
9 checks passed
@itsalaidbacklife
itsalaidbacklife deleted the fix/oauth-identity-assertions branch September 18, 2026 14:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dev experience Improvements to the code base that make it easier/better/more enjoyable to contribute to Cuttle version-patch An update that warrants a bumping the project's patch version (e.g. 4.0.0 => 4.0.1)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: OAuth unit tests compare User.id to Identity.id and fail after any Cypress run

1 participant