fix(rd21): retain draft revisions and qualify conflict recovery

This commit is contained in:
cesnimda
2026-10-23 12:34:34 +02:00
parent 12f342c09c
commit 05a738693e
53 changed files with 813 additions and 192 deletions
+4 -4
View File
@@ -23,7 +23,7 @@ const test = base.extend<{ accounts: { a: Account; b: Account } }>({
expect((await admin.api.post(`${apiUrl}/users`, { headers: admin.headers, data: { email, password, roles: [] } })).status()).toBe(200);
const account = await signIn(playwright, email);
accounts.push(account);
expect((await account.api.put(`${apiUrl}/career/profile`, { headers: account.headers, data: profile(`Private ${name.toUpperCase()}`) })).status()).toBe(200);
expect((await account.api.put(`${apiUrl}/career/profile`, { headers: account.headers, data: { ...profile(`Private ${name.toUpperCase()}`), expectedVersion: (await (await account.api.get(`${apiUrl}/career/profile`)).json()).revision } })).status()).toBe(200);
}
await provide({ a: accounts[0], b: accounts[1] });
} finally {
@@ -52,7 +52,7 @@ test("retained profile request from A cannot write using B cookies and fresh B C
expect((await browserSession.api.post(`${apiUrl}/auth/login`, { data: { email: b.email, password } })).status()).toBe(200);
const csrfB = (await browserSession.api.storageState()).cookies.find(cookie => cookie.name === "XSRF-TOKEN")!.value;
const response = await browserSession.api.put(`${apiUrl}/career/profile`, {
headers: { "X-CSRF-TOKEN": csrfB, "X-Session-Context": staleContext }, data: retained,
headers: { "X-CSRF-TOKEN": csrfB, "X-Session-Context": staleContext }, data: { ...retained, expectedVersion: retained.revision },
});
const persisted = { status: response.status(), a: await storedName(a), b: await storedName(b) };
await info.attach("retained-request-persistence", { body: JSON.stringify(persisted, null, 2), contentType: "application/json" });
@@ -66,7 +66,7 @@ test("missing and forged session contexts fail closed while same-account profile
for (const sessionContext of [undefined, a.sessionContext, a.id, "crafted-session"]) {
const response = await b.api.put(`${apiUrl}/career/profile`, {
headers: { "X-CSRF-TOKEN": b.headers["X-CSRF-TOKEN"], ...(sessionContext ? { "X-Session-Context": sessionContext } : {}) },
data: { ...profile("forged"), userId: a.id, ownerUserId: a.id, accountId: a.id },
data: { expectedVersion: (await (await b.api.get(`${apiUrl}/career/profile`)).json()).revision, ...profile("forged"), userId: a.id, ownerUserId: a.id, accountId: a.id },
});
expect(response.status()).toBe(sessionContext ? 409 : 428);
}
@@ -76,7 +76,7 @@ test("missing and forged session contexts fail closed while same-account profile
const singletonRead = await b.api.get(`${apiUrl}/career/profile?ownerUserId=${a.id}`);
expect((await singletonRead.json()).profile.contact.fullName).toBe("Private B");
const saved = await b.api.put(`${apiUrl}/career/profile`, {
headers: b.headers, data: { ...profile("Legitimate B"), userId: a.id, ownerUserId: a.id, roles: ["Admin"], plan: "pro" },
headers: b.headers, data: { expectedVersion: (await (await b.api.get(`${apiUrl}/career/profile`)).json()).revision, ...profile("Legitimate B"), userId: a.id, ownerUserId: a.id, roles: ["Admin"], plan: "pro" },
});
expect(saved.status()).toBe(200); // Over-posted ownership/security fields are not DTO properties.
expect(await storedName(a)).toBe("Private A");