mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-24 04:33:17 +00:00
Keep late browser refusal terminal during context cleanup
A denial arriving as the browser closes must leave a failed bounded receipt and suppress recovery authentication. Exercise real late HTTP401/403 responses alongside the earlier navigation and API controls. Contract-Neutral: Enforces the existing all-response refusal contract through final context disposal; no new acceptance route or permission. Change-source: pulse-maintainer
This commit is contained in:
@@ -490,11 +490,12 @@ def verify_browser_recovery_controls(controls_path: Path) -> None:
|
||||
fixed = {(kind, "upgrade") for kind in ("success", "anonymous-probes", "restart-gap",
|
||||
"wrong-preview", "silent-stream", "stuck-modal")}
|
||||
fixed.add(("success", "recovery"))
|
||||
fixed.update((f"cleanup-{status}", "upgrade") for status in (401, 403))
|
||||
for endpoint in ("navigation", "authenticated-navigation", "login", "initial-version",
|
||||
"authenticated-api", "check", "apply", "stream", "version", "health", "read"):
|
||||
fixed.update((f"{endpoint}-{status}", "upgrade") for status in (401, 403))
|
||||
results = controls["results"]
|
||||
assert len(results) == len(fixed) == 29
|
||||
assert len(results) == len(fixed) == 31
|
||||
assert {(row["fixture"], row["mode"]) for row in results} == fixed
|
||||
source = HARNESS_PATH.read_text(encoding="utf-8")
|
||||
function = "run_browser_journey() {" + source.split("run_browser_journey() {", 1)[1].split("\n}\n", 1)[0] + "\n}"
|
||||
|
||||
@@ -243,8 +243,13 @@ async function runJourney(browser, options) {
|
||||
throw error;
|
||||
} finally {
|
||||
await closeContext();
|
||||
if (record.access_refused) {
|
||||
record.status = 'failed';
|
||||
record.failure_type ||= 'AssertionOrOperationError';
|
||||
}
|
||||
if (options.outputDir) fs.writeFileSync(path.join(options.outputDir, `browser-${options.mode}.json`), JSON.stringify(record, null, 2) + '\n');
|
||||
assert.equal(record.context_closed, true, 'browser context cleanup failed');
|
||||
requireAccess(); // A response received during cleanup cannot become a pass.
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ function fixture(kind) {
|
||||
if (refuse('authenticated-api')) return;
|
||||
return json(200, {});
|
||||
}
|
||||
if (url.pathname === '/api/cleanup-control') { refuse('cleanup'); return; }
|
||||
if (url.pathname === '/api/updates/check') {
|
||||
if (refuse('check')) return;
|
||||
return json(200, { latestVersion: kind === 'wrong-preview' ? '6.5.0-rc.1' : to.slice(1), downloadUrl });
|
||||
@@ -143,6 +144,7 @@ async function runControls(outputDir) {
|
||||
['anonymous-probes', 'upgrade', true, 1, 1], ['restart-gap', 'upgrade', true, 1, 1],
|
||||
['wrong-preview', 'upgrade', false, 0, 1], ['silent-stream', 'upgrade', false, 1, 1],
|
||||
['stuck-modal', 'upgrade', false, 1, 1],
|
||||
['cleanup-401', 'upgrade', false, 1, 1], ['cleanup-403', 'upgrade', false, 1, 1],
|
||||
];
|
||||
for (const endpoint of ['navigation', 'authenticated-navigation', 'login', 'initial-version',
|
||||
'authenticated-api', 'check', 'apply', 'stream', 'version', 'health', 'read']) {
|
||||
@@ -158,7 +160,22 @@ async function runControls(outputDir) {
|
||||
const folder = path.resolve(outputDir, `${kind}-${mode}`); fs.mkdirSync(folder, { recursive: true });
|
||||
let passed = false;
|
||||
try {
|
||||
await runJourney(browser, { origin: `http://127.0.0.1:${target.server.address().port}`, mode, from, to,
|
||||
// A real denied HTTP response deliberately arrives during cleanup,
|
||||
// after the main checks. The observer must not leave a passed receipt.
|
||||
// Only this fixture delays context disposal; no response is fabricated.
|
||||
const testBrowser = kind.startsWith('cleanup-') ? {
|
||||
version: () => browser.version(),
|
||||
newContext: async options => {
|
||||
const context = await browser.newContext(options);
|
||||
const close = context.close.bind(context);
|
||||
context.close = async () => {
|
||||
await context.pages()[0].evaluate(() => fetch('/api/cleanup-control'));
|
||||
await close();
|
||||
};
|
||||
return context;
|
||||
},
|
||||
} : browser;
|
||||
await runJourney(testBrowser, { origin: `http://127.0.0.1:${target.server.address().port}`, mode, from, to,
|
||||
expected: mode === 'upgrade' ? to : from, auth: { username: 'synthetic', password: 'synthetic' },
|
||||
timeout: 1500, readinessTimeout: 3000, outputDir: folder });
|
||||
passed = true;
|
||||
|
||||
Reference in New Issue
Block a user