test(streaming): cover profile 409 recovery and stale response bodies (#7299)
This commit is contained in:
1 parent
99f22391d7
commit
8396520450
2 files changed
+36
-4
No files matched your search
@@ -67,8 +67,10 @@ The browser closes its persistent per-session SSE while hidden and uses
|
|||||||
subject to browser timer throttling. An active stream can be attached through
|
subject to browser timer throttling. An active stream can be attached through
|
||||||
the existing replay path; successful attachment stops the poll.
|
the existing replay path; successful attachment stops the poll.
|
||||||
|
|
||||||
HTTP `404` is ambiguous: the profile-visibility guard can return it for a live
|
HTTP `404` is ambiguous: older profile-visibility guards and the legacy
|
||||||
session after another tab changes the browser-wide active-profile cookie.
|
unknown-profile path can return it for a live session. Current master returns
|
||||||
|
`409 session_profile_mismatch` for a known foreign profile; that response
|
||||||
|
remains retryable when another tab changes the browser-wide profile cookie.
|
||||||
A single `404` therefore keeps polling and retains the hidden-resume owner.
|
A single `404` therefore keeps polling and retains the hidden-resume owner.
|
||||||
After three consecutive `404` responses, the poll pauses to bound repeated
|
After three consecutive `404` responses, the poll pauses to bound repeated
|
||||||
missing-session requests, but retains that owner so returning to the visible
|
missing-session requests, but retains that owner so returning to the visible
|
||||||
|
|||||||
@@ -358,6 +358,24 @@ def hidden_poll_results():
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function runLateJson() {
|
||||||
|
reset();
|
||||||
|
let resolveBody;
|
||||||
|
globalThis.fetch = () => {
|
||||||
|
fetchCalls++;
|
||||||
|
if (fetchCalls === 1) return Promise.resolve({ok: true, status: 200,
|
||||||
|
json: () => new Promise(resolve => { resolveBody = resolve; })});
|
||||||
|
return Promise.resolve(response(200));
|
||||||
|
};
|
||||||
|
_sessionStreamHiddenSid = 'session-a';
|
||||||
|
_startHiddenActiveStreamPoll('session-a');
|
||||||
|
await settle();
|
||||||
|
_startHiddenActiveStreamPoll('session-a');
|
||||||
|
resolveBody({active_stream_id: 'old-stream'});
|
||||||
|
await settle();
|
||||||
|
return {attachCalls, running: intervalFn !== null};
|
||||||
|
}
|
||||||
|
|
||||||
async function runSequence(statuses) {
|
async function runSequence(statuses) {
|
||||||
reset();
|
reset();
|
||||||
const states = [];
|
const states = [];
|
||||||
@@ -385,6 +403,10 @@ def hidden_poll_results():
|
|||||||
missing404: await runStatus(404),
|
missing404: await runStatus(404),
|
||||||
missing410: await runStatus(410),
|
missing410: await runStatus(410),
|
||||||
server500: await runStatus(500),
|
server500: await runStatus(500),
|
||||||
|
profile409: await runStatus(409),
|
||||||
|
auth401: await runStatus(401),
|
||||||
|
forbidden403: await runStatus(403),
|
||||||
|
rate429: await runStatus(429),
|
||||||
offline: await runStatus(0, true),
|
offline: await runStatus(0, true),
|
||||||
idle200: await runStatus(200),
|
idle200: await runStatus(200),
|
||||||
stale404: await runStaleResponse(404),
|
stale404: await runStaleResponse(404),
|
||||||
@@ -394,9 +416,12 @@ def hidden_poll_results():
|
|||||||
visible410: await runVisibilityRecovery(410),
|
visible410: await runVisibilityRecovery(410),
|
||||||
repeated404: await runSequence([404, 404, 404, 404]),
|
repeated404: await runSequence([404, 404, 404, 404]),
|
||||||
recoveredProfile: await runSequence([404, 'active']),
|
recoveredProfile: await runSequence([404, 'active']),
|
||||||
|
recoveredKnownProfile: await runSequence([409, 'active']),
|
||||||
reset200: await runSequence([404, 404, 200, 404, 404, 404]),
|
reset200: await runSequence([404, 404, 200, 404, 404, 404]),
|
||||||
reset500: await runSequence([404, 404, 500, 404, 404, 404]),
|
reset500: await runSequence([404, 404, 500, 404, 404, 404]),
|
||||||
resetOffline: await runSequence([404, 404, -1, 404, 404, 404]),
|
resetOffline: await runSequence([404, 404, -1, 404, 404, 404]),
|
||||||
|
reset409: await runSequence([404, 404, 409, 404, 404, 404]),
|
||||||
|
lateJson: await runLateJson(),
|
||||||
};
|
};
|
||||||
process.stdout.write(JSON.stringify(result));
|
process.stdout.write(JSON.stringify(result));
|
||||||
})().catch(error => {
|
})().catch(error => {
|
||||||
@@ -423,6 +448,7 @@ def test_hidden_poll_transient_404_preserves_visibility_recovery(hidden_poll_res
|
|||||||
"fetchCalls": 2, "running": True, "pollSid": "session-a", "hiddenSid": "session-a",
|
"fetchCalls": 2, "running": True, "pollSid": "session-a", "hiddenSid": "session-a",
|
||||||
}
|
}
|
||||||
assert result["recoveredProfile"]["attachCalls"] == 1
|
assert result["recoveredProfile"]["attachCalls"] == 1
|
||||||
|
assert result["recoveredKnownProfile"]["attachCalls"] == 1
|
||||||
|
|
||||||
|
|
||||||
def test_hidden_poll_repeated_404_is_bounded_but_can_resume(hidden_poll_results):
|
def test_hidden_poll_repeated_404_is_bounded_but_can_resume(hidden_poll_results):
|
||||||
@@ -433,7 +459,7 @@ def test_hidden_poll_repeated_404_is_bounded_but_can_resume(hidden_poll_results)
|
|||||||
assert result["eventSourceCalls"] == 1
|
assert result["eventSourceCalls"] == 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("key", ["reset200", "reset500", "resetOffline"])
|
@pytest.mark.parametrize("key", ["reset200", "reset500", "resetOffline", "reset409"])
|
||||||
def test_hidden_poll_404_budget_requires_consecutive_responses(hidden_poll_results, key):
|
def test_hidden_poll_404_budget_requires_consecutive_responses(hidden_poll_results, key):
|
||||||
result = hidden_poll_results[key]
|
result = hidden_poll_results[key]
|
||||||
assert [s["running"] for s in result["states"]] == [True] * 5 + [False]
|
assert [s["running"] for s in result["states"]] == [True] * 5 + [False]
|
||||||
@@ -458,7 +484,7 @@ def test_hidden_poll_410_stops_and_clears_resume_owner(hidden_poll_results):
|
|||||||
|
|
||||||
def test_hidden_poll_transient_failures_and_idle_remain_retryable(hidden_poll_results):
|
def test_hidden_poll_transient_failures_and_idle_remain_retryable(hidden_poll_results):
|
||||||
result = hidden_poll_results
|
result = hidden_poll_results
|
||||||
for key in ("server500", "offline", "idle200"):
|
for key in ("server500", "offline", "idle200", "profile409", "auth401", "forbidden403", "rate429"):
|
||||||
assert result[key]["fetchCalls"] == 2
|
assert result[key]["fetchCalls"] == 2
|
||||||
assert result[key]["running"] is True
|
assert result[key]["running"] is True
|
||||||
assert result[key]["pollSid"] == "session-a"
|
assert result[key]["pollSid"] == "session-a"
|
||||||
@@ -474,3 +500,7 @@ def test_hidden_poll_stale_response_and_tick_cannot_stop_replacement(hidden_poll
|
|||||||
"pollSid": sid,
|
"pollSid": sid,
|
||||||
"hiddenSid": sid,
|
"hiddenSid": sid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_hidden_poll_late_json_cannot_attach_into_replacement(hidden_poll_results):
|
||||||
|
assert hidden_poll_results["lateJson"] == {"attachCalls": 0, "running": True}
|
||||||
Reference in new issue
Block a user