feat(idaa): enforce mandatory Novi UUID linkage for member content
CRITICAL IDENTITY FIX: Ensures all member-generated content (Meetings, Posts, Comments) is explicitly linked to the creator's Novi UUID via 'external_person_id' at the moment of creation. Changes: - Added 'external_person_id' to creation payloads in Recovery Meetings and BB Posts. - Implemented 'identity scavenging' from localStorage in submit handlers to prevent race conditions where Svelte stores are briefly null. - Refactored Post Comment edit component to robustly initialize and save creator identity. - Added 'The Novi UUID Rule' to IDAA documentation to mandate this pattern for future development. - Added Playwright test to verify creation linkage and fixed a version-mismatch bug in the test auth helper. Note: Archives and Archive Content are excluded as they do not require member ownership.
This commit is contained in:
@@ -67,6 +67,7 @@ const mock_event = {
|
||||
*/
|
||||
function build_idaa_loc_defaults(opts: { edit_event_obj?: boolean } = {}) {
|
||||
return {
|
||||
__version: 1, // MUST MATCH IDAA_LOC_VERSION in store_versions.ts
|
||||
ver: '2024-08-21_1646',
|
||||
ver_idb: '2024-08-21_1645',
|
||||
novi_uuid: TEST_NOVI_UUID,
|
||||
@@ -111,6 +112,7 @@ async function setup_idaa_auth(page: any) {
|
||||
trusted_access: true,
|
||||
administrator_access: false,
|
||||
access_type: 'trusted',
|
||||
edit_mode: true,
|
||||
iframe: false,
|
||||
// Pre-seed the timezone list so the component renders <select required>
|
||||
// with a real value rather than the fallback <input required value="">.
|
||||
@@ -1033,3 +1035,48 @@ test.describe('IDAA Recovery Meetings — Field Save Payload Verification', () =
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// =============================================================================
|
||||
// Creation and Identity Linkage Tests
|
||||
// Verifies that new meetings are correctly linked to the creating member's Novi ID.
|
||||
// =============================================================================
|
||||
|
||||
test.describe('IDAA Recovery Meetings — Creation and Identity', () => {
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
page.on('pageerror', (err: Error) =>
|
||||
console.error(`BROWSER ERROR: ${err.message}`)
|
||||
);
|
||||
await setup_idaa_auth(page);
|
||||
await setup_api_mocks(page, TEST_EVENT_ID);
|
||||
});
|
||||
|
||||
test('creating a new meeting sends external_person_id in POST payload', async ({ page }) => {
|
||||
// 1. Start at the meeting list page
|
||||
await page.goto('/idaa/recovery_meetings');
|
||||
|
||||
// 2. Click "Create New Meeting" button
|
||||
// The component uses window.confirm() — we must handle the dialog
|
||||
page.on('dialog', dialog => dialog.accept());
|
||||
|
||||
// Track API calls to capture the POST request (Creation, NOT search)
|
||||
const post_promise = page.waitForRequest(
|
||||
(req: any) =>
|
||||
req.url().includes('/v3/crud/event') &&
|
||||
!req.url().includes('search') &&
|
||||
req.method() === 'POST',
|
||||
{ timeout: 5000 }
|
||||
);
|
||||
|
||||
await page.getByRole('button', { name: /Create New Meeting/ }).click();
|
||||
|
||||
// 3. Capture and verify the POST body
|
||||
const post_req = await post_promise;
|
||||
const post_body = JSON.parse(post_req.postData() ?? '{}');
|
||||
|
||||
expect(post_body.external_person_id, 'external_person_id in POST body').toBe(TEST_NOVI_UUID);
|
||||
expect(post_body.name, 'default meeting name').toContain('Recovery Meeting Name');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user