Keep project_role as member/viewer, send document_duty=reviewer for USER_B, and clear the seed 李主管 reviewer so the assigned reviewer is the one that syncs. Co-authored-by: Cursor <cursoragent@cursor.com>
137 lines
4.0 KiB
TypeScript
137 lines
4.0 KiB
TypeScript
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']);
|
|
});
|