fix(core): hold the connection cap on the path a settings screen uses

Connecting a Steam account wrote the row itself, so the limit on how many one
person may connect was enforced on the sign-in path and nowhere else — and
this is the path the settings screen calls, which makes it the one that would
have gone over. It now resolves who is asking and hands over to the single
place the rule lives.

Two things fall out of that. A Steam account already connected to somebody
else is a conflict rather than a silent success returning the other person's
row id, and a Steam id of the wrong shape is refused before a lookup.
This commit is contained in:
Wanjohi
2026-09-05 00:03:53 +03:00
parent 96b0cf8111
commit 1b61d2251e
2 changed files with 48 additions and 28 deletions
+23
View File
@@ -1,7 +1,9 @@
import { afterAll, beforeEach, describe, expect, test } from 'bun:test';
import { Actor } from '../actor.js';
import { testDb } from '../db/test.js';
import { Identifier } from '../id.js';
import { Steam } from '../steam/index.js';
import { Identity } from './identity.js';
import { User } from './index.js';
import { LinkedAccount } from './linked-account.js';
@@ -187,6 +189,27 @@ describe('Identity.linkSteam', () => {
expect(thrown.type).toBe('already_exists');
});
test('the cap holds on the path the settings screen uses', async () => {
const { userID } = await Identity.fromVerifiedEmail({ email: email(7) });
track(userID);
for (let n = 50; n < 54; n++) {
await Identity.linkSteam({ userId: userID, steamId: steamID(n) });
}
let thrown: any = null;
await Actor.with({ type: 'user', properties: { userID, linkedAccountID: '' } }, async () => {
try {
await Steam.link({ steamId: steamID(54) });
} catch (err) {
thrown = err;
}
});
expect(thrown).not.toBeNull();
expect(thrown.code).toBe('invalid_state');
expect(await Identity.listSteam(userID)).toHaveLength(Identity.MAX_STEAM_ACCOUNTS);
});
test('a legacy user is claimed by attaching an email, and keeps its Steam link', async () => {
const legacy = await legacySteamUser(40);