99 lines
3.3 KiB
JavaScript
99 lines
3.3 KiB
JavaScript
// Standalone JS port of `lib/team.ts::isValidInviteRole` and the
|
|
// org-membership decision used by POST /api/tenants/[name]/assignments.
|
|
|
|
function isValidInviteRole(role) {
|
|
return role === "owner" || role === "user";
|
|
}
|
|
|
|
// Mirrors the assignment-time check: target user must exist in the
|
|
// org's member list. Returns true if assign should proceed.
|
|
function canAssign(targetUserId, orgMembers) {
|
|
return orgMembers.some((m) => m.userId === targetUserId);
|
|
}
|
|
|
|
// Mirrors the dropdown candidate-filter on the AssignedUsersPanel:
|
|
// only `user`-role members who aren't already assigned, excluding
|
|
// owners (who have implicit access).
|
|
function pickCandidates(orgMembers, alreadyAssigned) {
|
|
const assigned = new Set(alreadyAssigned);
|
|
return orgMembers.filter(
|
|
(m) =>
|
|
!assigned.has(m.userId) &&
|
|
m.roles.includes("user") &&
|
|
!m.roles.includes("owner")
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Test fixtures
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const orgMembers = [
|
|
{ userId: "u-1", roles: ["owner"] },
|
|
{ userId: "u-2", roles: ["user"] },
|
|
{ userId: "u-3", roles: ["user"] },
|
|
{ userId: "u-4", roles: [] }, // member with no role yet
|
|
{ userId: "u-5", roles: ["owner", "user"] }, // dual-role
|
|
];
|
|
|
|
let pass = 0, fail = 0;
|
|
|
|
console.log("--- isValidInviteRole ---");
|
|
const inviteCases = [
|
|
["owner", true, "owner is valid"],
|
|
["user", true, "user is valid"],
|
|
["viewer", false, "viewer rejected (dropped in Slice 5)"],
|
|
["platform_admin", false, "platform_admin not invitable"],
|
|
["platform_operator", false, "platform_operator not invitable"],
|
|
["", false, "empty rejected"],
|
|
["OWNER", false, "case-sensitive"],
|
|
];
|
|
for (const [role, expected, note] of inviteCases) {
|
|
const got = isValidInviteRole(role);
|
|
const ok = got === expected;
|
|
console.log(`${ok ? "PASS" : "FAIL"} got=${got} want=${expected} [${note}]`);
|
|
if (ok) pass++; else fail++;
|
|
}
|
|
|
|
console.log("\n--- canAssign (membership check) ---");
|
|
const assignCases = [
|
|
["u-1", true, "owner can be assigned (idempotent for owners)"],
|
|
["u-2", true, "user-role member can be assigned"],
|
|
["u-99", false, "non-member rejected"],
|
|
["", false, "empty userId rejected"],
|
|
];
|
|
for (const [targetId, expected, note] of assignCases) {
|
|
const got = canAssign(targetId, orgMembers);
|
|
const ok = got === expected;
|
|
console.log(`${ok ? "PASS" : "FAIL"} got=${got} want=${expected} [${note}]`);
|
|
if (ok) pass++; else fail++;
|
|
}
|
|
|
|
console.log("\n--- pickCandidates (assign dropdown) ---");
|
|
const candidateCases = [
|
|
{
|
|
assigned: [],
|
|
expected: ["u-2", "u-3"],
|
|
note: "user-role members minus owners (u-5 is owner+user, excluded)",
|
|
},
|
|
{
|
|
assigned: ["u-2"],
|
|
expected: ["u-3"],
|
|
note: "u-2 already assigned, only u-3 remains",
|
|
},
|
|
{
|
|
assigned: ["u-2", "u-3"],
|
|
expected: [],
|
|
note: "everyone assigned",
|
|
},
|
|
];
|
|
for (const c of candidateCases) {
|
|
const got = pickCandidates(orgMembers, c.assigned).map((m) => m.userId);
|
|
const ok = JSON.stringify(got) === JSON.stringify(c.expected);
|
|
console.log(`${ok ? "PASS" : "FAIL"} got=${JSON.stringify(got)} want=${JSON.stringify(c.expected)} [${c.note}]`);
|
|
if (ok) pass++; else fail++;
|
|
}
|
|
|
|
console.log(`\n${pass} pass, ${fail} fail`);
|
|
process.exit(fail === 0 ? 0 : 1);
|