import test from 'node:test'; import assert from 'node:assert/strict'; import { buildInitialMemberUpsertPayload, ensureCreatedProjectWithMembers, persistInitialProjectMembers, selectNonCreatorInitialMembers, } from './createProjectMembersPersist'; test('selectNonCreatorInitialMembers skips creator only', () => { const members = selectNonCreatorInitialMembers( [ { userId: 86, userName: 'A', role: 'creator' }, { userId: 99, userName: 'B', role: 'reviewer' }, ], 86, ); assert.deepEqual( members.map((m) => m.userId), [99], ); }); test('buildInitialMemberUpsertPayload maps reviewer UI role to member API role', () => { assert.deepEqual(buildInitialMemberUpsertPayload({ userId: 99, userName: 'B', role: 'reviewer' }, 86), { user_id: 99, project_role: 'member', document_duty: 'reviewer', }); assert.deepEqual(buildInitialMemberUpsertPayload({ userId: 99, userName: 'B', role: 'viewer' }, 86), { user_id: 99, project_role: 'viewer', document_duty: '', }); }); test('persistInitialProjectMembers awaits each add and stops on failure', async () => { const calls: number[] = []; await assert.rejects( async () => persistInitialProjectMembers({ projectId: 6071, creatorUserId: 86, initialMembers: [ { userId: 86, userName: 'A', role: 'creator' }, { userId: 2, userName: 'B', role: 'reviewer' }, { userId: 3, userName: 'C', role: 'approver' }, ], addMember: async (_projectId, payload) => { calls.push(payload.user_id); if (payload.user_id === 3) { throw new Error('add failed'); } }, }), /add failed/, ); assert.deepEqual(calls, [2, 3]); }); test('ensureCreatedProjectWithMembers skips recreate on bootstrap id and retries members', async () => { let createCalls = 0; const added: number[] = []; const phases: string[] = []; const first = await ensureCreatedProjectWithMembers({ bootstrapProjectId: null, createProject: async () => { createCalls += 1; return { id: 6071 }; }, creatorUserId: 86, initialMembers: [ { userId: 86, userName: 'A', role: 'creator' }, { userId: 99, userName: 'B', role: 'reviewer' }, ], addMember: async (_pid, payload) => { added.push(payload.user_id); }, onPhase: (phase) => { phases.push(phase); }, }); assert.equal(first.projectId, 6071); assert.equal(first.createdNow, true); assert.deepEqual(first.persistedUserIds, [99]); assert.deepEqual(phases, ['creating', 'syncing_members']); let failOnce = true; await assert.rejects( async () => ensureCreatedProjectWithMembers({ bootstrapProjectId: 6071, createProject: async () => { createCalls += 1; return { id: 9999 }; }, creatorUserId: 86, initialMembers: [ { userId: 86, userName: 'A', role: 'creator' }, { userId: 99, userName: 'B', role: 'reviewer' }, ], addMember: async () => { if (failOnce) { failOnce = false; throw new Error('transient'); } }, }), /transient/, ); assert.equal(createCalls, 1, 'must not recreate project on member retry'); const retry = await ensureCreatedProjectWithMembers({ bootstrapProjectId: 6071, createProject: async () => { createCalls += 1; return { id: 9999 }; }, creatorUserId: 86, initialMembers: [ { userId: 86, userName: 'A', role: 'creator' }, { userId: 99, userName: 'B', role: 'reviewer' }, ], addMember: async (_pid, payload) => { added.push(payload.user_id); }, onPhase: (phase) => { phases.push(phase); }, }); assert.equal(retry.createdNow, false); assert.equal(retry.projectId, 6071); assert.equal(createCalls, 1); assert.ok(added.filter((id) => id === 99).length >= 2); assert.deepEqual(phases, ['creating', 'syncing_members', 'syncing_members']); });