Hello from MCP server
import { check, getApi, getPb, wait, waitMs, users } from "./common.js";
import { benderTiers } from "./spaceshipBook.js";
export async function userOrgManagementTests(api) {
const testUser = {
email: "userorg.test@planetexpress.com",
password: "testpassword123",
name: "User Org Test User",
};
const orgOwner1 = {
email: "owner1@company.com",
password: "owner1password123",
name: "First Company Owner",
org: "First Test Company",
orgId: "",
bookId: "",
};
const orgOwner2 = {
email: "owner2@company.com",
password: "owner2password123",
name: "Second Company Owner",
org: "Second Test Company",
orgId: "",
bookId: "",
};
// Setup: Register users and create organizations
await check(async () => {
await api.register(testUser);
await api.register(orgOwner1);
await api.register(orgOwner2);
const responseA = await api.createOrg(orgOwner1, orgOwner1.org);
const responseB = await api.createOrg(orgOwner2, orgOwner2.org);
orgOwner1.orgId = responseA.id;
orgOwner2.orgId = responseB.id;
const book1 = await api.createBook(
orgOwner1,
orgOwner1.orgId,
"Book One Tiers",
"tiersBook",
null,
null,
);
const book2 = await api.createBook(
orgOwner2,
orgOwner2.orgId,
"Book Two Tiers",
"tiersBook",
null,
null,
);
orgOwner1.bookId = book1.id;
orgOwner2.bookId = book2.id;
await api.publishChanges(orgOwner1, orgOwner1.orgId, book1.id, benderTiers);
await api.publishChanges(orgOwner2, orgOwner2.orgId, book2.id, benderTiers);
// User joins both organizations
await api.joinOrg(testUser, orgOwner1.orgId);
await api.joinOrg(testUser, orgOwner2.orgId); // activeOrg should be Second Test Company
await api.assignRole(orgOwner1, testUser, "author");
await api.assignRole(orgOwner2, testUser, "author");
return true;
}, "Setup users and organizations, user joins both organizations");
// Test 1: User can update their activeOrg field
await check(async () => {
const updatedUser = await api.updateUserActiveOrg(
testUser,
orgOwner1.orgId,
);
if (updatedUser.activeOrg !== orgOwner1.orgId) {
throw new Error(
"Failed to update user's activeOrg to First Test Company",
);
}
return updatedUser;
}, "User can update their activeOrg to a new organization");
await check(async () => {
// Verify the activeOrg change persists after re-login
await api.login(testUser);
const pb = api.pb;
if (pb.authStore.record.activeOrg !== orgOwner1.orgId) {
throw new Error("activeOrg change should persist after re-login");
}
return pb.authStore.record;
}, "Updated activeOrg persists after re-login");
await check(async () => {
// Test updating to the second organization
const updatedUser = await api.updateUserActiveOrg(
testUser,
orgOwner2.orgId,
);
if (updatedUser.activeOrg !== orgOwner2.orgId) {
throw new Error(
"Failed to update user's activeOrg to Second Test Company",
);
}
return updatedUser;
}, "User can update their activeOrg to a different organization");
// Test 2: User can list all profiles where profile.user matches their user ID
await check(async () => {
const userProfiles = await api.listUserProfiles(testUser);
console.log(userProfiles);
// User should have profiles in both organizations they joined
if (userProfiles.length !== 2) {
throw new Error(`Expected 2 profiles, but found ${userProfiles.length}`);
}
// Verify that all profiles belong to the test user
const userId = (await api.login(testUser)).record.id;
for (const profile of userProfiles) {
if (profile.user !== userId) {
throw new Error(
`Profile ${profile.id} does not belong to the test user`,
);
}
}
return userProfiles;
}, "User can list all their profiles across organizations");
await check(async () => {
const updatedUser = await api.updateUserActiveOrg(
testUser,
orgOwner1.orgId,
);
const book = await api.viewBook(testUser, orgOwner1.bookId);
if (book.id != orgOwner1.bookId) {
throw new Error("Should be able to see the correct book for the org");
}
const allBooks = await api.listBooks(testUser);
console.log(allBooks);
}, "User can list pricebooks in the first organization");
await check(async () => {
const updatedUser = await api.updateUserActiveOrg(
testUser,
orgOwner2.orgId,
);
const book = await api.viewBook(testUser, orgOwner2.bookId);
if (book.id != orgOwner2.bookId) {
throw new Error("Should be able to see the correct book for the org");
}
const allBooks = await api.listBooks(testUser);
console.log(allBooks);
}, "User can list pricebooks in the second organization");
await check(async () => {
const userProfiles = await api.listUserProfiles(testUser);
// Extract organization IDs from the profiles
const profileOrgIds = userProfiles.map((p) => p.org);
// Verify profiles exist for both organizations the user joined
if (!profileOrgIds.includes(orgOwner1.orgId)) {
throw new Error("User should have a profile in First Test Company");
}
if (!profileOrgIds.includes(orgOwner2.orgId)) {
throw new Error("User should have a profile in Second Test Company");
}
return userProfiles;
}, "User profiles include correct organizations");
await check(async () => {
const userProfiles = await api.listUserProfiles(testUser);
// Verify expanded data is included
for (const profile of userProfiles) {
if (!profile.expand || !profile.expand.user) {
throw new Error("Profile should include expanded user data");
}
if (profile.expand.user.name !== testUser.name) {
throw new Error("Expanded user data should match the test user");
}
}
return userProfiles;
}, "User profiles include properly expanded user data");
// Test edge case: Verify other users cannot see this user's profiles unless they're in the same org
await check(async () => {
// Create a third user who is not in any of the test organizations
const outsideUser = {
email: "outside@test.com",
password: "outsidepassword123",
name: "Outside User",
};
await api.register(outsideUser);
try {
const outsideUserProfiles = await api.listUserProfiles(outsideUser);
// Outside user should have no profiles since they haven't joined any organizations
if (outsideUserProfiles.length !== 0) {
throw new Error(
`Outside user should have 0 profiles, but found ${outsideUserProfiles.length}`,
);
}
return outsideUserProfiles;
} catch (error) {
// This is expected if the user has no profiles
return [];
}
}, "Users who haven't joined organizations have no profiles");
// Test security: Users cannot list profiles that don't belong to them
await check(async () => {
// Create a fourth user who joins only one organization
const limitedUser = {
email: "limited@test.com",
password: "limitedpassword123",
name: "Limited User",
};
await api.register(limitedUser);
await api.joinOrg(limitedUser, orgOwner1.orgId); // Only joins First Test Company
const limitedUserProfiles = await api.listUserProfiles(limitedUser);
// Limited user should only see their own profile (1 profile)
if (limitedUserProfiles.length !== 1) {
throw new Error(
`Limited user should only see 1 profile (their own), but found ${limitedUserProfiles.length}`,
);
}
// Verify the profile belongs to the limited user
const userId = (await api.login(limitedUser)).record.id;
if (limitedUserProfiles[0].user !== userId) {
throw new Error("Limited user should only see their own profile");
}
// Verify the profile is from the correct organization
if (limitedUserProfiles[0].org !== orgOwner1.orgId) {
throw new Error(
"Limited user's profile should be from First Test Company",
);
}
return limitedUserProfiles;
}, "Users can only list their own profiles, not other users' profiles");
// Test that users cannot access profiles from organizations they're not members of
await check(async () => {
// Create a user who joins a different organization entirely
const separateOrgOwner = {
email: "separate@company.com",
password: "separatepassword123",
name: "Separate Company Owner",
org: "Separate Test Company",
orgId: "",
};
const separateUser = {
email: "separate.user@company.com",
password: "separateuserpassword123",
name: "Separate Company User",
};
await api.register(separateOrgOwner);
await api.register(separateUser);
const separateOrgResponse = await api.createOrg(
separateOrgOwner,
separateOrgOwner.org,
);
separateOrgOwner.orgId = separateOrgResponse.id;
await api.joinOrg(separateUser, separateOrgOwner.orgId);
// Separate user should only see their own profile from their organization
const separateUserProfiles = await api.listUserProfiles(separateUser);
if (separateUserProfiles.length !== 1) {
throw new Error(
`Separate user should only see 1 profile, but found ${separateUserProfiles.length}`,
);
}
// Verify it's their own profile from their organization
const userId = (await api.login(separateUser)).record.id;
if (separateUserProfiles[0].user !== userId) {
throw new Error("Separate user should only see their own profile");
}
if (separateUserProfiles[0].org !== separateOrgOwner.orgId) {
throw new Error(
"Separate user's profile should be from Separate Test Company",
);
}
return separateUserProfiles;
}, "Users cannot access profiles from organizations they don't belong to");
}