test(stalker): replay single-flight auth refresh

This commit is contained in:
4gray committed 2026-07-27 11:53:02 +02:00
1 parent 85bee2dbb4
commit 76101fb29b
4 files changed
+1094

No files matched your search

@@ -0,0 +1,199 @@
import type { Page } from '@playwright/test';
import type { StalkerSessionConnectionOutcome } from '@iptvnator/shared/interfaces';
import {
closeElectronApp,
expect,
launchElectronApp,
test,
} from './electron-test-fixtures';
import {
StalkerReplayRun,
withStalkerReplayRun,
} from './support/stalker-replay';
test.describe('Electron Stalker authenticated session refresh', () => {
test('@stalker @electron refreshes concurrent rejected catalog requests once', async ({
dataDir,
}) => {
const replay = await withStalkerReplayRun(
'e2e/concurrent-catalog-refresh',
async (run) => {
const app = await launchElectronApp(dataDir);
try {
const ready = await openProvisionalSession(
app.mainWindow,
run,
'e2e-concurrent-refresh'
);
if (
ready.kind !== 'ready' ||
ready.recipe !== 'full-session' ||
ready.connectionMode !== 'provisional'
) {
throw new Error(
`expected-full-session:${JSON.stringify(ready)}`
);
}
const committed = await app.mainWindow.evaluate(
async ({ attemptRef }) => {
const control =
window.electron?.stalkerSessionControl;
if (!control) {
throw new Error(
'stalker-session-control-unavailable'
);
}
return control({
action: 'commit',
attemptRef,
});
},
{ attemptRef: ready.attemptRef }
);
expect(committed).toMatchObject({
action: 'commit',
kind: 'success',
});
const outcomes = await app.mainWindow.evaluate(
async ({ leaseRef }) => {
const request =
window.electron?.stalkerSessionRequest;
if (!request) {
throw new Error(
'stalker-session-request-unavailable'
);
}
return Promise.all([
request({
leaseRef,
operation: 'get-ordered-list',
parameters: {
category: 'news',
contentType: 'itv',
page: 1,
},
}),
request({
leaseRef,
operation: 'get-ordered-list',
parameters: {
category: 'sports',
contentType: 'itv',
page: 2,
},
}),
]);
},
{ leaseRef: ready.leaseRef }
);
expect(outcomes).toMatchObject([
{
kind: 'success',
operation: 'get-ordered-list',
payload: {
items: [{ id: 'news-channel' }],
page: 1,
},
},
{
kind: 'success',
operation: 'get-ordered-list',
payload: {
items: [{ id: 'sports-channel' }],
page: 2,
},
},
]);
expect(
JSON.stringify({ committed, outcomes, ready })
).not.toMatch(/authorization|bearer|cookie|token/i);
} finally {
await closeElectronApp(app);
}
}
);
expect(replay.finalization).toMatchObject({
ledger: {
mismatchCounts: {},
operationCounts: {
'anonymous-probe': 1,
'initial-handshake': 1,
'initial-profile': 1,
'refresh-anonymous-probe': 1,
'refresh-handshake': 1,
'refresh-profile': 1,
'reject-news': 1,
'reject-sports': 1,
'retry-news': 1,
'retry-sports': 1,
},
terminalState: 'complete',
},
ok: true,
});
});
});
async function openProvisionalSession(
page: Page,
run: StalkerReplayRun,
playlistRef: string
): Promise<StalkerSessionConnectionOutcome> {
return page.evaluate(
async ({
learnedEndpointHint,
macAddress,
playlistRef: rendererPlaylistRef,
sourceUrl,
}) => {
const open = window.electron?.stalkerSessionOpen;
if (!open) {
throw new Error('stalker-session-open-unavailable');
}
return open({
descriptor: {
connectionMode: 'provisional',
learnedEndpointHint,
macAddress,
playlistRef: rendererPlaylistRef,
profilePreset: {
id: 'mag250-public-5_1-minimal-v1',
version: 1,
},
provisionalReason: 'import',
sourceUrl,
},
});
},
{
learnedEndpointHint: `${requiredOrigin(run, 'portal')}/portal.php`,
macAddress: requiredInput(run, 'mac'),
playlistRef,
sourceUrl: run.entryUrl,
}
);
}
function requiredInput(run: StalkerReplayRun, name: string): string {
const value = run.inputs[name];
if (!value) {
throw new Error(`stalker-replay-input-missing:${name}`);
}
return value;
}
function requiredOrigin(run: StalkerReplayRun, name: string): string {
const value = run.origins[name];
if (!value) {
throw new Error(`stalker-replay-origin-missing:${name}`);
}
return value;
}
@@ -246,6 +246,14 @@ test.describe('Electron Stalker authenticated sessions', () => {
const app = await launchElectronApp(dataDir);
try {
await app.mainWindow.evaluate(async () => {
const readPlaylist = window.electron
?.dbGetAppPlaylist;
if (!readPlaylist) {
throw new Error('playlist-read-unavailable');
}
await readPlaylist('e2e-stalker-warmup');
});
return await Promise.all([
openStatus2Session(
app.mainWindow,
@@ -0,0 +1,886 @@
{
"description": "Synthetic concurrent catalog token rejections share one refreshed session and reissue each operation exactly once.",
"entry": {
"origin": "portal",
"path": "/c/"
},
"expectedEndpoint": {
"origin": "portal",
"path": "/portal.php"
},
"failOnUnexpectedRequest": true,
"initialState": "initial",
"origins": {
"portal": {}
},
"phases": [
{
"expectations": [
{
"cardinality": {
"max": 1,
"min": 1
},
"id": "anonymous-probe",
"method": "GET",
"operation": "anonymous-probe",
"origin": "portal",
"path": "/c/",
"request": {
"body": {
"kind": "absent"
},
"cookies": {
"absent": [
"mac",
"session"
],
"attributes": {},
"exact": {},
"present": []
},
"headers": {
"absent": [
"authorization",
"cookie"
],
"exact": {},
"present": []
},
"query": {
"absent": [
"device_id",
"device_id2",
"mac",
"password",
"signature",
"signature2",
"sn",
"token",
"username"
],
"exact": {},
"present": []
}
},
"response": {
"body": {
"kind": "empty"
},
"headers": {},
"status": 204
}
},
{
"cardinality": {
"max": 1,
"min": 1
},
"id": "initial-handshake",
"method": "GET",
"operation": "initial-handshake",
"origin": "portal",
"path": "/portal.php",
"request": {
"body": {
"kind": "absent"
},
"cookies": {
"absent": [
"session"
],
"attributes": {},
"exact": {
"mac": {
"kind": "ref",
"symbol": "mac"
},
"stb_lang": "en",
"timezone": "UTC"
},
"present": []
},
"headers": {
"absent": [
"authorization"
],
"exact": {
"user-agent": "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG250",
"x-user-agent": "Model: MAG250"
},
"present": []
},
"query": {
"absent": [
"password",
"token",
"username"
],
"exact": {
"JsHttpRequest": "1-xml",
"action": "handshake",
"type": "stb"
},
"present": []
}
},
"response": {
"body": {
"kind": "json",
"value": {
"js": {
"token": {
"kind": "ref",
"symbol": "initial-token"
}
}
}
},
"headers": {
"content-type": [
"application/json"
],
"set-cookie": [
{
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "session="
},
{
"kind": "ref",
"symbol": "initial-cookie"
},
{
"kind": "literal",
"value": "; Path=/; HttpOnly"
}
]
}
]
},
"status": 200
}
},
{
"cardinality": {
"max": 1,
"min": 1
},
"id": "initial-profile",
"method": "GET",
"operation": "initial-profile",
"origin": "portal",
"path": "/portal.php",
"request": {
"body": {
"kind": "absent"
},
"cookies": {
"absent": [],
"attributes": {},
"exact": {
"mac": {
"kind": "ref",
"symbol": "mac"
},
"session": {
"kind": "ref",
"symbol": "initial-cookie"
}
},
"present": []
},
"headers": {
"absent": [],
"exact": {
"authorization": {
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "Bearer "
},
{
"kind": "ref",
"symbol": "initial-token"
}
]
}
},
"present": []
},
"query": {
"absent": [
"password",
"username"
],
"exact": {
"JsHttpRequest": "1-xml",
"action": "get_profile",
"auth_second_step": "0",
"type": "stb"
},
"present": [
"metrics"
]
}
},
"response": {
"body": {
"kind": "json",
"value": {
"js": {
"status": 0
}
}
},
"headers": {
"content-type": [
"application/json"
]
},
"status": 200
}
}
],
"mode": "ordered",
"name": "initial-session",
"nextState": "reject",
"state": "initial"
},
{
"barrier": {
"name": "both-catalog-requests-rejected",
"releaseWhenMatched": 2
},
"expectations": [
{
"cardinality": {
"max": 1,
"min": 1
},
"id": "reject-news",
"method": "GET",
"operation": "reject-news",
"origin": "portal",
"path": "/portal.php",
"request": {
"body": {
"kind": "absent"
},
"cookies": {
"absent": [],
"attributes": {},
"exact": {
"mac": {
"kind": "ref",
"symbol": "mac"
},
"session": {
"kind": "ref",
"symbol": "initial-cookie"
}
},
"present": []
},
"headers": {
"absent": [],
"exact": {
"authorization": {
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "Bearer "
},
{
"kind": "ref",
"symbol": "initial-token"
}
]
}
},
"present": []
},
"query": {
"absent": [
"mac",
"password",
"token",
"username"
],
"exact": {
"JsHttpRequest": "1-xml",
"action": "get_ordered_list",
"category": "news",
"p": "1",
"type": "itv"
},
"present": []
}
},
"response": {
"body": {
"kind": "json",
"value": {
"js": {
"error": "Authorization failed"
}
}
},
"headers": {
"content-type": [
"application/json"
]
},
"status": 401
}
},
{
"cardinality": {
"max": 1,
"min": 1
},
"id": "reject-sports",
"method": "GET",
"operation": "reject-sports",
"origin": "portal",
"path": "/portal.php",
"request": {
"body": {
"kind": "absent"
},
"cookies": {
"absent": [],
"attributes": {},
"exact": {
"mac": {
"kind": "ref",
"symbol": "mac"
},
"session": {
"kind": "ref",
"symbol": "initial-cookie"
}
},
"present": []
},
"headers": {
"absent": [],
"exact": {
"authorization": {
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "Bearer "
},
{
"kind": "ref",
"symbol": "initial-token"
}
]
}
},
"present": []
},
"query": {
"absent": [
"mac",
"password",
"token",
"username"
],
"exact": {
"JsHttpRequest": "1-xml",
"action": "get_ordered_list",
"category": "sports",
"p": "2",
"type": "itv"
},
"present": []
}
},
"response": {
"body": {
"kind": "json",
"value": {
"js": {
"error": "Authorization failed"
}
}
},
"headers": {
"content-type": [
"application/json"
]
},
"status": 401
}
}
],
"mode": "unordered",
"name": "concurrent-rejections",
"nextState": "refresh",
"state": "reject"
},
{
"expectations": [
{
"cardinality": {
"max": 1,
"min": 1
},
"id": "refresh-anonymous-probe",
"method": "GET",
"operation": "refresh-anonymous-probe",
"origin": "portal",
"path": "/c/",
"request": {
"body": {
"kind": "absent"
},
"cookies": {
"absent": [
"mac",
"session"
],
"attributes": {},
"exact": {},
"present": []
},
"headers": {
"absent": [
"authorization",
"cookie"
],
"exact": {},
"present": []
},
"query": {
"absent": [
"device_id",
"device_id2",
"mac",
"password",
"signature",
"signature2",
"sn",
"token",
"username"
],
"exact": {},
"present": []
}
},
"response": {
"body": {
"kind": "empty"
},
"headers": {},
"status": 204
}
},
{
"cardinality": {
"max": 1,
"min": 1
},
"id": "refresh-handshake",
"method": "GET",
"operation": "refresh-handshake",
"origin": "portal",
"path": "/portal.php",
"request": {
"body": {
"kind": "absent"
},
"cookies": {
"absent": [
"session"
],
"attributes": {},
"exact": {
"mac": {
"kind": "ref",
"symbol": "mac"
},
"stb_lang": "en",
"timezone": "UTC"
},
"present": []
},
"headers": {
"absent": [
"authorization"
],
"exact": {
"user-agent": "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG250",
"x-user-agent": "Model: MAG250"
},
"present": []
},
"query": {
"absent": [
"password",
"token",
"username"
],
"exact": {
"JsHttpRequest": "1-xml",
"action": "handshake",
"type": "stb"
},
"present": []
}
},
"response": {
"body": {
"kind": "json",
"value": {
"js": {
"token": {
"kind": "ref",
"symbol": "refresh-token"
}
}
}
},
"headers": {
"content-type": [
"application/json"
],
"set-cookie": [
{
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "session="
},
{
"kind": "ref",
"symbol": "refresh-cookie"
},
{
"kind": "literal",
"value": "; Path=/; HttpOnly"
}
]
}
]
},
"status": 200
}
},
{
"cardinality": {
"max": 1,
"min": 1
},
"id": "refresh-profile",
"method": "GET",
"operation": "refresh-profile",
"origin": "portal",
"path": "/portal.php",
"request": {
"body": {
"kind": "absent"
},
"cookies": {
"absent": [],
"attributes": {},
"exact": {
"mac": {
"kind": "ref",
"symbol": "mac"
},
"session": {
"kind": "ref",
"symbol": "refresh-cookie"
}
},
"present": []
},
"headers": {
"absent": [],
"exact": {
"authorization": {
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "Bearer "
},
{
"kind": "ref",
"symbol": "refresh-token"
}
]
}
},
"present": []
},
"query": {
"absent": [
"password",
"username"
],
"exact": {
"JsHttpRequest": "1-xml",
"action": "get_profile",
"auth_second_step": "0",
"type": "stb"
},
"present": [
"metrics"
]
}
},
"response": {
"body": {
"kind": "json",
"value": {
"js": {
"status": 0
}
}
},
"headers": {
"content-type": [
"application/json"
]
},
"status": 200
}
}
],
"mode": "ordered",
"name": "single-refresh",
"nextState": "retry",
"state": "refresh"
},
{
"expectations": [
{
"cardinality": {
"max": 1,
"min": 1
},
"id": "retry-news",
"method": "GET",
"operation": "retry-news",
"origin": "portal",
"path": "/portal.php",
"request": {
"body": {
"kind": "absent"
},
"cookies": {
"absent": [],
"attributes": {},
"exact": {
"mac": {
"kind": "ref",
"symbol": "mac"
},
"session": {
"kind": "ref",
"symbol": "refresh-cookie"
}
},
"present": []
},
"headers": {
"absent": [],
"exact": {
"authorization": {
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "Bearer "
},
{
"kind": "ref",
"symbol": "refresh-token"
}
]
}
},
"present": []
},
"query": {
"absent": [
"mac",
"password",
"token",
"username"
],
"exact": {
"JsHttpRequest": "1-xml",
"action": "get_ordered_list",
"category": "news",
"p": "1",
"type": "itv"
},
"present": []
}
},
"response": {
"body": {
"kind": "json",
"value": {
"js": {
"cur_page": 1,
"data": [
{
"id": "news-channel",
"title": "Synthetic News"
}
],
"max_page_items": 1,
"total_items": 1,
"total_pages": 1
}
}
},
"headers": {
"content-type": [
"application/json"
]
},
"status": 200
}
},
{
"cardinality": {
"max": 1,
"min": 1
},
"id": "retry-sports",
"method": "GET",
"operation": "retry-sports",
"origin": "portal",
"path": "/portal.php",
"request": {
"body": {
"kind": "absent"
},
"cookies": {
"absent": [],
"attributes": {},
"exact": {
"mac": {
"kind": "ref",
"symbol": "mac"
},
"session": {
"kind": "ref",
"symbol": "refresh-cookie"
}
},
"present": []
},
"headers": {
"absent": [],
"exact": {
"authorization": {
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "Bearer "
},
{
"kind": "ref",
"symbol": "refresh-token"
}
]
}
},
"present": []
},
"query": {
"absent": [
"mac",
"password",
"token",
"username"
],
"exact": {
"JsHttpRequest": "1-xml",
"action": "get_ordered_list",
"category": "sports",
"p": "2",
"type": "itv"
},
"present": []
}
},
"response": {
"body": {
"kind": "json",
"value": {
"js": {
"cur_page": 2,
"data": [
{
"id": "sports-channel",
"title": "Synthetic Sports"
}
],
"max_page_items": 1,
"total_items": 2,
"total_pages": 2
}
}
},
"headers": {
"content-type": [
"application/json"
]
},
"status": 200
}
}
],
"mode": "unordered",
"name": "retried-catalog-requests",
"nextState": "complete",
"state": "retry"
}
],
"scenarioId": "e2e-concurrent-catalog-refresh",
"schemaVersion": 1,
"symbols": [
{
"kind": "generate",
"symbol": "mac",
"valueKind": "mac"
},
{
"kind": "generate",
"symbol": "initial-token",
"valueKind": "token"
},
{
"kind": "generate",
"symbol": "refresh-token",
"valueKind": "token"
},
{
"kind": "generate",
"symbol": "initial-cookie",
"valueKind": "cookie"
},
{
"kind": "generate",
"symbol": "refresh-cookie",
"valueKind": "cookie"
}
],
"terminalState": "complete"
}
@@ -36,6 +36,7 @@ const EXPECTED_SCENARIOS = [
'classifier-waf-403',
'cookies-managed-shadow',
'cookies-session-isolation',
'e2e-concurrent-catalog-refresh',
'e2e-full-session-catalog-playback',
'redirect-auth-query',
'redirect-identity-pause',