test(qa): qualify bounded revision timeline and notification volume
This commit is contained in:
@@ -6,10 +6,12 @@ The final separate Phase9 diagnostic lane completed **18/18**, zero failures/ski
|
||||
|
||||
Fresh real preview and native exported PDF bytes from the retained corpus passed **16/16 fixtures /36 pages** under the unchanged oracle: exact page count/membership, physical dimensions ±0.5mm, marker positions ±2mm, font sizes ±0.5pt and original RGB tolerance. Nine defaults, long EN/NB, 8mm Letter, 28mm roomy, grid/bubbles, long sidebar and unbreakable boundary fixtures are included. No renderer geometry or tolerance changed.
|
||||
|
||||
[Portable oracle results](evidence/fidelity-results.json) retain all measurements. All36 page thumbnails were actually inspected in [sheet1](evidence/pdf-sheet-1.png), [sheet2](evidence/pdf-sheet-2.png) and [sheet3](evidence/pdf-sheet-3.png). Inspection found no visible clipping/overlap/blank output in those thumbnails. Several last pages contain little content; that is recorded layout behavior, not missing text according to the marker oracle. Thumbnail inspection is not print/readability, photo/crop, every glyph or intended Linux-font acceptance. Full-size spot checks and the final whole-suite successor are recorded separately when completed.
|
||||
[Portable oracle results](evidence/fidelity-results.json) retain all measurements. The final frozen ordinary run's native corpus producer passed; the unchanged oracle again passed16/16 over its fresh36 pages. All36 regenerated page thumbnails were actually inspected in [sheet1](evidence/pdf-sheet-1.png), [sheet2](evidence/pdf-sheet-2.png) and [sheet3](evidence/pdf-sheet-3.png), plus the full-size preview/PDF `unbreakable-and-boundary/comparison-4.png`. Inspection found no visible clipping/overlap/blank output in those views. Several last pages contain little content; that is recorded layout behavior, not missing text according to the marker oracle. Thumbnail/spot inspection is not print/readability, photo/crop, every glyph or intended Linux-font acceptance. Whole-suite completion remains a separate gate.
|
||||
|
||||
Existing browser fixtures also cover hidden preview mounting, image-layout readiness, dirty edits, stale CV revisions, preview transport recovery, canonical versus variant overrides, frozen submitted packages/archived binaries and missing optional sections. Their full-suite outcomes belong to final regression. Native PDF admission uses inert child processes and is not added to fidelity counts.
|
||||
|
||||
The separate Settings corpus passed the same unchanged oracle over **8 fixtures /24 pages**: EN/NB × light/dark, before and after actual document-control changes. All24 page thumbnails were inspected in [Settings sheet1](evidence/settings-pdf-sheet-1.png) and [sheet2](evidence/settings-pdf-sheet-2.png); a full-size light-EN before preview/PDF comparison was also inspected. No visible clipping/overlap/blank output was found in those views. [Measured results](evidence/settings-fidelity-results.json) retain the before/after page dimensions, marker/font/colour agreement. These are separate24 pages, not extra fixtures in the16/36 baseline corpus, and do not close human/Linux/print requirements.
|
||||
|
||||
## Sustained local workload
|
||||
|
||||
[Raw finite measurements](performance.json): **65.303s**, two paced readers, **240 successful reads**, 250 owned applications, eight Career roles with48 distinct long bullets, and20 native navigations across Dashboard/Table/Board/long CV. The same saved canonical profile and250-job membership remained intact afterward. CV readiness waits for settled pagination and verifies the final bullet's page membership and multiple rendered pages before recording timing.
|
||||
@@ -19,3 +21,5 @@ API latency across these reads: **p50 23ms /p95 76ms /max115ms**. Native ready t
|
||||
CDP CPU/task/heap/DOM metrics are retained for every navigation. They are observations, not leak diagnosis or attributed backend resource profiling. Two paced readers for a minute do not establish capacity/SLA, sustained drag frames, physical mobile performance, long-term memory stability, MariaDB/Linux behavior or real model inference throughput. No production load was generated.
|
||||
|
||||
Retained ordinary fixtures extend edge-case coverage to no-data,1,000-job Board membership/filtering, long timelines/profiles, revision conflicts, stale sessions, HTTP/network failures, missing/foreign references and recoverable drafts. Remaining worker durability, creation idempotency and high RD21 stale writes are explicit risks. No synthetic data was loaded into production.
|
||||
|
||||
A separately isolated [volume supplement](volume/README.md) adds3 passing native API cases:100 CV revisions plus additive restore101,100 long manual timeline messages with exact membership and foreign-owner denial, and100 cancellation notices with bounded listing/read/dismissal counts. Its source hashes and results are separate from the825-input ordinary-run snapshot. This establishes finite persisted membership/count/revision behavior, not large-history browser capacity.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@@ -1,5 +1,7 @@
|
||||
[
|
||||
{
|
||||
"id": "ddd864604af8a6a0198f-360c04fa6be46bbcba6d",
|
||||
"project": "chromium",
|
||||
"file": "accessibility.spec.ts",
|
||||
"title": "P9 settled badge drawer and editor triage en light",
|
||||
"status": "expected",
|
||||
@@ -21,6 +23,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ddd864604af8a6a0198f-d5c781053552377067ae",
|
||||
"project": "chromium",
|
||||
"file": "accessibility.spec.ts",
|
||||
"title": "P9 settled badge drawer and editor triage en dark",
|
||||
"status": "expected",
|
||||
@@ -42,6 +46,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ddd864604af8a6a0198f-2e3bdd1a8283956a0e35",
|
||||
"project": "chromium",
|
||||
"file": "accessibility.spec.ts",
|
||||
"title": "P9 settled badge drawer and editor triage nb light",
|
||||
"status": "expected",
|
||||
@@ -63,6 +69,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ddd864604af8a6a0198f-864eae11bccec12247a6",
|
||||
"project": "chromium",
|
||||
"file": "accessibility.spec.ts",
|
||||
"title": "P9 settled badge drawer and editor triage nb dark",
|
||||
"status": "expected",
|
||||
@@ -84,6 +92,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1aed2e881af2e0d7a227-8579037087f487b9f8d8",
|
||||
"project": "chromium",
|
||||
"file": "data.spec.ts",
|
||||
"title": "P9 characterize retained RD21 stale writes separately from CV revision safety",
|
||||
"status": "expected",
|
||||
@@ -105,6 +115,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7066e7f2e0ac7d296410-f9c3cfbba3d3cb3cd4f7",
|
||||
"project": "chromium",
|
||||
"file": "matrix.spec.ts",
|
||||
"title": "P9 all 33 routes 320 en light",
|
||||
"status": "expected",
|
||||
@@ -291,6 +303,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7066e7f2e0ac7d296410-3a13c24159dbd785ebda",
|
||||
"project": "chromium",
|
||||
"file": "matrix.spec.ts",
|
||||
"title": "P9 all 33 routes 320 en dark",
|
||||
"status": "expected",
|
||||
@@ -477,6 +491,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7066e7f2e0ac7d296410-6d49111c17a1dff08c8e",
|
||||
"project": "chromium",
|
||||
"file": "matrix.spec.ts",
|
||||
"title": "P9 all 33 routes 320 nb light",
|
||||
"status": "expected",
|
||||
@@ -663,6 +679,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7066e7f2e0ac7d296410-5fd0b9872d4828bde53b",
|
||||
"project": "chromium",
|
||||
"file": "matrix.spec.ts",
|
||||
"title": "P9 all 33 routes 320 nb dark",
|
||||
"status": "expected",
|
||||
@@ -849,6 +867,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7066e7f2e0ac7d296410-0baa4f01c2d84e97be11",
|
||||
"project": "chromium",
|
||||
"file": "matrix.spec.ts",
|
||||
"title": "P9 all 33 routes 768 en light",
|
||||
"status": "expected",
|
||||
@@ -1035,6 +1055,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7066e7f2e0ac7d296410-f2247ee0ab8d5b198b4f",
|
||||
"project": "chromium",
|
||||
"file": "matrix.spec.ts",
|
||||
"title": "P9 all 33 routes 768 en dark",
|
||||
"status": "expected",
|
||||
@@ -1221,6 +1243,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7066e7f2e0ac7d296410-f1032e36a96fbb1dcefa",
|
||||
"project": "chromium",
|
||||
"file": "matrix.spec.ts",
|
||||
"title": "P9 all 33 routes 768 nb light",
|
||||
"status": "expected",
|
||||
@@ -1407,6 +1431,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7066e7f2e0ac7d296410-06956c48e2db0078d906",
|
||||
"project": "chromium",
|
||||
"file": "matrix.spec.ts",
|
||||
"title": "P9 all 33 routes 768 nb dark",
|
||||
"status": "expected",
|
||||
@@ -1593,6 +1619,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7066e7f2e0ac7d296410-d810e2662b4adde1defd",
|
||||
"project": "chromium",
|
||||
"file": "matrix.spec.ts",
|
||||
"title": "P9 all 33 routes 1440 en light",
|
||||
"status": "expected",
|
||||
@@ -1779,6 +1807,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7066e7f2e0ac7d296410-e5e4f4ee9e4f4fb18acd",
|
||||
"project": "chromium",
|
||||
"file": "matrix.spec.ts",
|
||||
"title": "P9 all 33 routes 1440 en dark",
|
||||
"status": "expected",
|
||||
@@ -1965,6 +1995,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7066e7f2e0ac7d296410-d71823335925949fbcf8",
|
||||
"project": "chromium",
|
||||
"file": "matrix.spec.ts",
|
||||
"title": "P9 all 33 routes 1440 nb light",
|
||||
"status": "expected",
|
||||
@@ -2151,6 +2183,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7066e7f2e0ac7d296410-cdfb026d92e493311855",
|
||||
"project": "chromium",
|
||||
"file": "matrix.spec.ts",
|
||||
"title": "P9 all 33 routes 1440 nb dark",
|
||||
"status": "expected",
|
||||
@@ -2337,6 +2371,8 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dfb76b6411378a2f08ea-e69f55f4f639e0fe64e1",
|
||||
"project": "chromium",
|
||||
"file": "sustained.spec.ts",
|
||||
"title": "P9 bounded sustained reads with 250 jobs and long CV while using native workspaces",
|
||||
"status": "expected",
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
[
|
||||
{
|
||||
"id": "30685a6c0e3dbeec97fc-41d7ff2d700986d6de49",
|
||||
"project": "chromium",
|
||||
"file": "volume.spec.ts",
|
||||
"title": "P9 volume: 100 CV revisions retain exact history, stale rejection and additive restore",
|
||||
"status": "expected",
|
||||
"results": [
|
||||
{
|
||||
"status": "passed",
|
||||
"retry": 0,
|
||||
"durationMs": 6435,
|
||||
"errors": [],
|
||||
"attachments": [
|
||||
{
|
||||
"name": "phase9-volume-cv",
|
||||
"contentType": "application/json",
|
||||
"bytes": 183,
|
||||
"sha256": "06ada1842930140b730f802849b79a75f3cab67cbc7b42f514224940b687fb53"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "30685a6c0e3dbeec97fc-38eecc55ba78ad0862e8",
|
||||
"project": "chromium",
|
||||
"file": "volume.spec.ts",
|
||||
"title": "P9 volume: 100 manual timeline messages retain exact membership and foreign-owner denial",
|
||||
"status": "expected",
|
||||
"results": [
|
||||
{
|
||||
"status": "passed",
|
||||
"retry": 0,
|
||||
"durationMs": 5520,
|
||||
"errors": [],
|
||||
"attachments": [
|
||||
{
|
||||
"name": "phase9-volume-timeline",
|
||||
"contentType": "application/json",
|
||||
"bytes": 173,
|
||||
"sha256": "c6cbc352b555858d0b7b7133e442381b417be48fe54c204b2f106afe6b007f85"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "30685a6c0e3dbeec97fc-b9a8d42713cfaef64e77",
|
||||
"project": "chromium",
|
||||
"file": "volume.spec.ts",
|
||||
"title": "P9 volume: 100 native cancellation notices preserve bounded lists, unread counts and ownership",
|
||||
"status": "expected",
|
||||
"results": [
|
||||
{
|
||||
"status": "passed",
|
||||
"retry": 0,
|
||||
"durationMs": 51407,
|
||||
"errors": [],
|
||||
"attachments": [
|
||||
{
|
||||
"name": "phase9-volume-notifications",
|
||||
"contentType": "application/json",
|
||||
"bytes": 253,
|
||||
"sha256": "66cebc8fb3824f74e7452f25482357f27695237b9f0f7ec63f8d090986aa05ba"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,33 @@
|
||||
[
|
||||
{
|
||||
"name": "phase9-volume-cv",
|
||||
"observation": {
|
||||
"savedRevisions": 100,
|
||||
"finalVersions": 101,
|
||||
"staleStatus": 409,
|
||||
"canonicalUnchanged": true,
|
||||
"limits": "Native isolated SQLite API; no100-row history UI or MariaDB/long-term capacity claim."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "phase9-volume-timeline",
|
||||
"observation": {
|
||||
"messages": 100,
|
||||
"exactMembership": true,
|
||||
"foreignRead": 404,
|
||||
"limits": "Native persisted manual correspondence; no external mail delivery or100-message browser rendering claim."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "phase9-volume-notifications",
|
||||
"observation": {
|
||||
"notices": 100,
|
||||
"defaultList": 25,
|
||||
"maxList": 100,
|
||||
"repeatedReadCount": 99,
|
||||
"afterUnreadDismiss": 98,
|
||||
"foreignMutation": 404,
|
||||
"limits": "Existing guarded inert operation seed + native cancel/read/dismiss. No enabled worker/provider/model or100-item bell UI claim."
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,15 @@
|
||||
# Bounded volume supplement
|
||||
|
||||
**3/3 passed**, zero failures/skips/flaky/retries, `volume-qualified`, exit0 at20:46:29UTC on22October. Full TypeScript checking also passed. The initial runtime pass used an incorrect type-only import; that import was corrected and the complete three-case successor passed on the final source. Neither run changes product code.
|
||||
|
||||
| Scenario | Actual native result | Limit |
|
||||
|---|---|---|
|
||||
| 100 CV revisions | Exact descending1–100 membership; stale99 save rejected409 without appending history; restoring revision2 appends101 with its original headline; canonical Career readback unchanged | Isolated SQLite API. No100-row history UI, MariaDB, long-term growth or real model claim |
|
||||
| 100 long manual correspondence entries | All100 persisted IDs returned exactly; long content retained; second owner's job read404 | Manual timeline records, no external email delivery or100-message browser usability acceptance |
|
||||
| 100 notifications | Existing guarded one-insert inert-operation helper + actual native cancellation generates100 distinct notices; default25/max100 list, limit101 rejected400; repeated Read counts99 once, unread Dismiss counts98, foreign mutation404 | No enabled-worker/provider/inference proof or100-item bell UI acceptance |
|
||||
|
||||
The separate config reuses the existing API fixture on **localhost5304**, with a unique `jobtracker-e2e-*` direct child of system temporary storage and reuse disabled. It does not share the ordinary browser database or start another frontend. The existing helper verifies actual temp path, schema and exact owner before permitting one inert operation insert; all notification creation/read/dismissal goes through native product APIs. No production data or new seeding framework is used.
|
||||
|
||||
[Portable observations](../volume-results.json), [cases](../volume-cases.json) and [validation](../../validation.json) retain outcomes. The two source hashes are separately recorded as `supplementalSources`; the original825-input browser/product manifest remains unchanged. This supplement is additional to the18-case diagnostics and the362-case ordinary suite, never a replacement for either.
|
||||
|
||||
From `job-tracker-ui`, use the existing pinned DOTNET environment and run `rtk proxy node node_modules/@playwright/test/cli.js test --config=../docs/redesign/phase-9/p9-05/volume/playwright.config.ts --retries=0 --reporter=list,json`, with its own `PLAYWRIGHT_JSON_OUTPUT_NAME`.
|
||||
@@ -0,0 +1,12 @@
|
||||
import { defineConfig } from '../../../../../job-tracker-ui/node_modules/@playwright/test';
|
||||
import base from '../../../../../job-tracker-ui/playwright.config';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
|
||||
const server = Array.isArray(base.webServer) ? base.webServer[0] : base.webServer!;
|
||||
const dataRoot = path.join(os.tmpdir(), `jobtracker-e2e-p9-volume-${process.pid}-${Date.now().toString(36)}`);
|
||||
export default defineConfig({ ...base, testDir: __dirname, testMatch: '*.spec.ts', retries: 0,
|
||||
metadata: { isolatedDataRoot: dataRoot },
|
||||
webServer: { ...server, cwd: path.resolve(__dirname, '../../../../../job-tracker-ui'),
|
||||
command: server.command.replaceAll('5302', '5304'), url: 'http://localhost:5304/health',
|
||||
reuseExistingServer: false, env: { ...server.env, Data__Root: dataRoot } } });
|
||||
@@ -0,0 +1,99 @@
|
||||
import { test, expect, type APIRequestContext, type PlaywrightWorkerArgs } from '../../../../../job-tracker-ui/node_modules/@playwright/test';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import path from 'node:path';
|
||||
|
||||
const base = 'http://localhost:5304/api';
|
||||
const password = 'E2ePassword123!';
|
||||
async function login(api: APIRequestContext, email: string) {
|
||||
expect((await api.post(`${base}/auth/login`, { data: { email, password } })).status()).toBe(200);
|
||||
const me = await (await api.get(`${base}/auth/me`)).json();
|
||||
return { 'X-CSRF-TOKEN': (await api.storageState()).cookies.find(cookie => cookie.name === 'XSRF-TOKEN')!.value, 'X-Session-Context': me.sessionContext as string };
|
||||
}
|
||||
async function owner(playwright: PlaywrightWorkerArgs['playwright']) {
|
||||
const admin = await playwright.request.newContext();
|
||||
const api = await playwright.request.newContext();
|
||||
try {
|
||||
const headers = await login(admin, 'e2e@example.test');
|
||||
const email = `p9-volume-${Date.now()}@example.test`;
|
||||
const response = await admin.post(`${base}/users`, { headers, data: { email, password, roles: [] } });
|
||||
expect(response.status()).toBe(200);
|
||||
return { api, email, id: (await response.json()).id as string, headers: await login(api, email) };
|
||||
} finally { await admin.dispose(); }
|
||||
}
|
||||
|
||||
test('P9 volume: 100 CV revisions retain exact history, stale rejection and additive restore', async ({ playwright }, info) => {
|
||||
test.setTimeout(180000);
|
||||
const account = await owner(playwright);
|
||||
try {
|
||||
const { api, headers } = account;
|
||||
expect((await api.put(`${base}/career/profile`, { headers, data: { profile: { version: '1', contact: { fullName: 'Volume fixture' }, summary: ['Canonical unchanged'] } } })).status()).toBe(200);
|
||||
const source = await (await api.get(`${base}/career/profile`)).json();
|
||||
const created = await api.post(`${base}/cv/variants`, { headers, data: { name: 'Volume CV', settings: { themeId: 'minimal' } } });
|
||||
expect(created.status()).toBe(200); let cv = await created.json(); const id = cv.id;
|
||||
for (let version = 2; version <= 100; version++) {
|
||||
const response = await api.put(`${base}/cv/variants/${id}`, { headers, data: { expectedVersion: cv.version, name: cv.name, settings: { ...cv.settings, headline: `Revision ${version}` }, source: 'manual' } });
|
||||
expect(response.status()).toBe(200); cv = await response.json(); expect(cv.version).toBe(version);
|
||||
}
|
||||
const history = await (await api.get(`${base}/cv/variants/${id}/versions`)).json();
|
||||
expect(history.map((row: { version: number }) => row.version)).toEqual(Array.from({ length: 100 }, (_, index) => 100 - index));
|
||||
expect((await api.put(`${base}/cv/variants/${id}`, { headers, data: { expectedVersion: 99, settings: cv.settings } })).status()).toBe(409);
|
||||
expect(await (await api.get(`${base}/cv/variants/${id}/versions`)).json()).toEqual(history);
|
||||
const restored = await api.post(`${base}/cv/variants/${id}/versions/2/restore`, { headers, data: { expectedVersion: 100 } });
|
||||
expect(restored.status()).toBe(200); expect(await restored.json()).toMatchObject({ version: 101, settings: { headline: 'Revision 2' } });
|
||||
expect((await (await api.get(`${base}/cv/variants/${id}/versions`)).json()).length).toBe(101);
|
||||
expect(await (await api.get(`${base}/career/profile`)).json()).toEqual(source);
|
||||
await info.attach('phase9-volume-cv', { body: JSON.stringify({ savedRevisions: 100, finalVersions: 101, staleStatus: 409, canonicalUnchanged: true, limits: 'Native isolated SQLite API; no100-row history UI or MariaDB/long-term capacity claim.' }), contentType: 'application/json' });
|
||||
} finally { await account.api.dispose(); }
|
||||
});
|
||||
|
||||
test('P9 volume: 100 manual timeline messages retain exact membership and foreign-owner denial', async ({ playwright }, info) => {
|
||||
test.setTimeout(180000);
|
||||
const account = await owner(playwright); const other = await owner(playwright);
|
||||
try {
|
||||
const { api, headers } = account;
|
||||
const company = await api.post(`${base}/companies`, { headers, data: { name: 'Synthetic volume timeline' } });
|
||||
expect(company.status()).toBe(201);
|
||||
const job = await api.post(`${base}/jobapplications`, { headers, data: { companyId: (await company.json()).id, jobTitle: 'Volume timeline fixture', status: 'Saved' } });
|
||||
expect(job.status()).toBe(201); const jobId = (await job.json()).id;
|
||||
const ids: number[] = [];
|
||||
for (let index = 0; index < 100; index++) {
|
||||
const response = await api.post(`${base}/correspondence`, { headers, data: { jobApplicationId: jobId, from: 'Synthetic manual note', content: `Timeline evidence ${index} ${'Documented progress. '.repeat(20)}`, channel: 'manual', direction: 'incoming' } });
|
||||
expect(response.status()).toBe(201); ids.push((await response.json()).id);
|
||||
}
|
||||
const response = await api.get(`${base}/correspondence/${jobId}`); expect(response.status()).toBe(200);
|
||||
const rows = await response.json(); expect(rows.map((row: { id: number }) => row.id).sort((a: number, b: number) => a - b)).toEqual([...ids].sort((a, b) => a - b));
|
||||
expect(rows.every((row: { content: string }) => row.content.startsWith('Timeline evidence '))).toBe(true);
|
||||
expect((await other.api.get(`${base}/correspondence/${jobId}`)).status()).toBe(404);
|
||||
await info.attach('phase9-volume-timeline', { body: JSON.stringify({ messages: rows.length, exactMembership: true, foreignRead: 404, limits: 'Native persisted manual correspondence; no external mail delivery or100-message browser rendering claim.' }), contentType: 'application/json' });
|
||||
} finally { await account.api.dispose(); await other.api.dispose(); }
|
||||
});
|
||||
|
||||
test('P9 volume: 100 native cancellation notices preserve bounded lists, unread counts and ownership', async ({ playwright }, info) => {
|
||||
test.setTimeout(300000);
|
||||
const account = await owner(playwright); const other = await owner(playwright);
|
||||
try {
|
||||
const dataRoot = info.config.metadata.isolatedDataRoot;
|
||||
expect(typeof dataRoot).toBe('string'); expect(process.env.PLAYWRIGHT_REUSE_SERVERS).not.toBe('true');
|
||||
for (let index = 0; index < 100; index++) {
|
||||
// Existing helper admits one inert insert only after temp-path/schema/owner verification.
|
||||
const seeded = spawnSync(process.env.PLAYWRIGHT_PYTHON_PATH || 'python', [path.resolve('e2e/helpers/seed-operation.py')], { input: JSON.stringify({ dataRoot, ownerId: account.id, ownerEmail: account.email }), encoding: 'utf8', timeout: 15000 });
|
||||
expect(seeded.status, seeded.stderr).toBe(0);
|
||||
const id = JSON.parse(seeded.stdout).operationId;
|
||||
expect((await account.api.post(`${base}/operations/${id}/cancel`, { headers: account.headers })).status()).toBe(200);
|
||||
}
|
||||
const list = await account.api.get(`${base}/notifications?limit=100`); expect(list.status()).toBe(200);
|
||||
const rows = await list.json(); expect(rows.length).toBe(100); expect(new Set(rows.map((row: { id: string }) => row.id)).size).toBe(100);
|
||||
expect((await (await account.api.get(`${base}/notifications`)).json()).length).toBe(25);
|
||||
expect(await (await account.api.get(`${base}/notifications/unread-count`)).json()).toEqual({ count: 100 });
|
||||
expect((await account.api.get(`${base}/notifications?limit=101`)).status()).toBe(400);
|
||||
expect(await (await other.api.get(`${base}/notifications`)).json()).toEqual([]);
|
||||
expect((await other.api.post(`${base}/notifications/${rows[0].id}/read`, { headers: other.headers })).status()).toBe(404);
|
||||
expect((await account.api.post(`${base}/notifications/${rows[0].id}/read`, { headers: account.headers })).status()).toBe(204);
|
||||
expect((await account.api.post(`${base}/notifications/${rows[0].id}/read`, { headers: account.headers })).status()).toBe(204);
|
||||
expect(await (await account.api.get(`${base}/notifications/unread-count`)).json()).toEqual({ count: 99 });
|
||||
expect((await account.api.delete(`${base}/notifications/${rows[1].id}`, { headers: account.headers })).status()).toBe(204);
|
||||
expect(await (await account.api.get(`${base}/notifications/unread-count`)).json()).toEqual({ count: 98 });
|
||||
expect((await (await account.api.get(`${base}/notifications?limit=100`)).json()).length).toBe(99);
|
||||
await info.attach('phase9-volume-notifications', { body: JSON.stringify({ notices: 100, defaultList: 25, maxList: 100, repeatedReadCount: 99, afterUnreadDismiss: 98, foreignMutation: 404, limits: 'Existing guarded inert operation seed + native cancel/read/dismiss. No enabled worker/provider/model or100-item bell UI claim.' }), contentType: 'application/json' });
|
||||
} finally { await account.api.dispose(); await other.api.dispose(); }
|
||||
});
|
||||
Reference in New Issue
Block a user