:constructor: Add more fixes

This commit is contained in:
Andrey Antukh 2026-06-24 13:54:40 +02:00
parent 45a0937266
commit 03df00fa72
2 changed files with 98 additions and 8 deletions

View File

@ -535,6 +535,46 @@ export function createClient(baseUrl) {
};
}
/**
* Invite members to a team by email.
*
* @param {string} teamId - Team UUID
* @param {string[]} emails - Array of email addresses
* @param {string} role - Role for the invited members (e.g. "editor")
* @returns {object} Parsed response { status, body }
*/
function inviteTeamMembers(teamId, emails, role) {
const res = rpc("POST", "create-team-invitations", {
"team-id": teamId,
emails: emails,
role: role,
});
return {
status: res.status,
body: res.status === 200 ? res.json() : null,
raw: res,
};
}
/**
* Get an invitation token for a specific email.
*
* @param {string} teamId - Team UUID
* @param {string} email - Invited email address
* @returns {object} Parsed response { status, body }
*/
function getTeamInvitationToken(teamId, email) {
const res = rpc("GET", "get-team-invitation-token", {
"team-id": teamId,
email: email,
});
return {
status: res.status,
body: res.status === 200 ? res.json() : null,
raw: res,
};
}
/**
* Logout the current user.
*
@ -574,6 +614,8 @@ export function createClient(baseUrl) {
deleteFile,
deleteProject,
deleteTeam,
inviteTeamMembers,
getTeamInvitationToken,
logout,
};
}

View File

@ -151,11 +151,39 @@ export function setup() {
}
console.log(` Created ${users.length} demo profiles`);
// Login with first user to create project and files
// Login with first user to create shared team and files
const loginRes = client.login(users[0].email, users[0].password);
if (loginRes.status !== 200) fail("Login failed for setup");
const teamId = client.getTeams().body[0].id;
const projectId = client.createProject(teamId, "Concurrent Edit Project").body.id;
// Create a shared team so all VUs can access the same file.
// Each demo profile gets its own default team; without a shared
// team, VUs 2+ would get 404 on get-file.
const teamRes = client.createTeam("Concurrent Edit Team");
if (teamRes.status !== 200) fail("Failed to create shared team");
const sharedTeamId = teamRes.body.id;
console.log(` Shared team: ${sharedTeamId}`);
// Invite remaining users to the shared team and get acceptance tokens.
// The tokens are used by each VU via verify-token to join the team.
const invitationTokens = [];
for (let i = 1; i < TOTAL_VUS; i++) {
const invRes = client.inviteTeamMembers(sharedTeamId, [users[i].email], "editor");
if (invRes.status !== 200) {
console.error(` Invite user ${i}: status=${invRes.status}`);
}
const tokenRes = client.getTeamInvitationToken(sharedTeamId, users[i].email);
if (tokenRes.status === 200 && tokenRes.body) {
invitationTokens.push({ vuIndex: i, token: tokenRes.body });
}
}
if (invitationTokens.length > 0) {
console.log(` Got ${invitationTokens.length} invitation tokens`);
} else {
console.log(` All users auto-added (no tokens needed)`);
}
// Create project and files in the shared team
const projectId = client.createProject(sharedTeamId, "Concurrent Edit Project").body.id;
console.log(` Project: ${projectId}`);
// Build file/page assignments based on mode
@ -180,13 +208,14 @@ export function setup() {
const pageIds = [defaultPageId];
// Add remaining pages
// vern never changes on regular edits (only on snapshot restore),
// and each add-page increments revn by 1, so no need to re-fetch.
for (let i = 1; i < TOTAL_VUS; i++) {
const pageId = uuidv4();
const pageName = `Page ${i + 1}`;
const addRes = addPage(client, fileId, revn, vern, pageId, pageName);
if (addRes.status !== 200) fail(`Failed to add page ${i + 1}`);
revn = addRes.body.revn;
vern = addRes.body.vern;
revn++;
pageIds.push(pageId);
}
console.log(` Added ${pageIds.length} pages to file`);
@ -222,8 +251,7 @@ export function setup() {
const pageName = `Page ${p + 1}`;
const addRes = addPage(client, fileId, revn, vern, pageId, pageName);
if (addRes.status !== 200) fail(`Failed to add page ${p + 1} to file ${f + 1}`);
revn = addRes.body.revn;
vern = addRes.body.vern;
revn++;
pageIds.push(pageId);
}
console.log(` Added ${pageIds.length} pages to file ${f + 1}`);
@ -246,6 +274,7 @@ export function setup() {
editMode: EDIT_MODE,
users,
vuAssignments,
invitationTokens,
};
}
@ -253,10 +282,17 @@ export function setup() {
// Main VU Function — each VU edits its assigned page
// ---------------------------------------------------------------------------
// Track which VUs have accepted their invitation (once per VU, not per iteration)
const verifiedVus = {};
// ---------------------------------------------------------------------------
// Main VU Function — each VU edits its assigned page
// ---------------------------------------------------------------------------
export default function (data) {
const client = createClient(data.baseUrl);
// Pick user and assignment from pool
// Each VU uses its own demo profile (different users editing the same file)
const vuIndex = __VU - 1;
const user = data.users[vuIndex];
const assignment = data.vuAssignments[vuIndex];
@ -268,6 +304,18 @@ export default function (data) {
// Login
if (!assertOk(client.login(user.email, user.password), "login")) fail("login failed");
// Accept team invitation once per VU (not per iteration).
// In devenv the user may already be auto-added; 400 on already-accepted
// tokens is harmless — skip the token on subsequent iterations.
if (!verifiedVus[__VU]) {
const tokenEntry = data.invitationTokens.find((t) => t.vuIndex === vuIndex);
if (tokenEntry && tokenEntry.token) {
client.rpc("POST", "verify-token", tokenEntry.token);
}
verifiedVus[__VU] = true;
}
sleep(0.5);
// Edit loop