mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-19 17:25:19 +03:00
Comments and served API descriptions here had grown references that only make sense to someone with our internal notes: relative paths that escape this tree, filenames and titles of documents nobody outside can open, quoted prose from them, and the name of a component that has no public surface — once in an OpenAPI description, which is published output rather than source. None of it was load-bearing. Every case restates as what the code actually requires, and every rewrite came out shorter: "in the words the host agent reports" for a component name, "republished as addresses are discovered" for a quoted phrase, "a size tier sets vCPU, RAM and the output geometry" for a sentence that had been carrying a path. Internal reasoning is now cited exactly one way, ref(d-NNNN) in a source comment, with the rule that the sentence must still stand if the marker is deleted. CLAUDE.md leads with it, because the previous version of this mistake was made by people who knew the repo was public and it still took ten occurrences to notice, so "be careful" is not a mechanism. Commit messages get the stricter rule and carry no references at all: a comment can be fixed by the next commit and a published message cannot be fixed at all. Git hooks now enforce both halves. The check caught a real one while being written: the CLAUDE.md table spelled out the paths it was prohibiting, which discloses them to exactly the reader it protects against. 138 tests, 0 fail.
83 lines
2.8 KiB
TypeScript
83 lines
2.8 KiB
TypeScript
import { afterAll, describe, expect, test } from 'bun:test';
|
|
|
|
import { Actor } from '../actor.js';
|
|
import { Fixtures } from '../db/fixtures.js';
|
|
import { testDb } from '../db/test.js';
|
|
import { Identifier } from '../id.js';
|
|
import { Member } from './member.js';
|
|
import { Team } from './index.js';
|
|
|
|
const sql = testDb();
|
|
|
|
const createdUserIds: string[] = [];
|
|
|
|
async function newOwner(label: string) {
|
|
const o = await Fixtures.owner(label);
|
|
createdUserIds.push(o.userId);
|
|
return o;
|
|
}
|
|
|
|
afterAll(async () => {
|
|
if (createdUserIds.length > 0) {
|
|
await sql`delete from "user" where id in ${sql(createdUserIds)}`;
|
|
createdUserIds.length = 0;
|
|
}
|
|
});
|
|
|
|
describe('Team.ensurePersonal', () => {
|
|
test('a new user gets exactly one team, and owns it', async () => {
|
|
const owner = await newOwner('team-first');
|
|
|
|
const team = await Team.personalFor(owner.userId);
|
|
expect(team?.id).toBe(owner.teamId);
|
|
expect(team?.ownerId).toBe(owner.userId);
|
|
|
|
const memberships = await Member.listByUser(owner.userId);
|
|
expect(memberships).toHaveLength(1);
|
|
expect(memberships[0]!.role).toBe('owner');
|
|
});
|
|
|
|
test('it is idempotent, because it runs on every login', async () => {
|
|
const owner = await newOwner('team-idempotent');
|
|
|
|
// The auth worker calls this each time somebody signs in, not only when
|
|
// the user is created — that is what backfills accounts made before the
|
|
// call existed. A second call must not mint a second team.
|
|
const again = await Actor.with(
|
|
{ type: 'user', properties: { userID: owner.userId, linkedAccountID: owner.linkedAccountId } },
|
|
() => Team.ensurePersonal({ displayName: 'team-idempotent' })
|
|
);
|
|
|
|
expect(again).toBe(owner.teamId);
|
|
const rows = await sql`select count(*)::int as n from team where owner_id = ${owner.userId}`;
|
|
expect(rows[0]!.n).toBe(1);
|
|
});
|
|
|
|
test('a user who predates the personal team gets one on next login', async () => {
|
|
// The legacy row the migration and this call between them repair: a user
|
|
// created by Steam sign-in before `ensurePersonal` was ever wired up.
|
|
const userId = Identifier.ascending('user');
|
|
createdUserIds.push(userId);
|
|
await sql`insert into "user" (id, name, email) values (${userId}, ${'legacy'}, ${`legacy-${userId}@example.test`})`;
|
|
|
|
expect(await Team.personalFor(userId)).toBeNull();
|
|
|
|
const teamId = await Actor.with(
|
|
{ type: 'user', properties: { userID: userId, linkedAccountID: 'lac_unused' } },
|
|
() => Team.ensurePersonal({ displayName: 'legacy' })
|
|
);
|
|
|
|
expect(teamId).toBeTruthy();
|
|
expect((await Team.personalFor(userId))?.id).toBe(teamId);
|
|
});
|
|
|
|
test('two users with the same display name get distinct slugs', async () => {
|
|
const a = await newOwner('same-name');
|
|
const b = await newOwner('same-name');
|
|
|
|
const teamA = await Team.personalFor(a.userId);
|
|
const teamB = await Team.personalFor(b.userId);
|
|
expect(teamA!.slug).not.toBe(teamB!.slug);
|
|
});
|
|
});
|