test(stalker): replay catalog and playback auth

This commit is contained in:
4gray committed 2026-07-27 11:45:48 +02:00
1 parent 829550e2c9
commit 85bee2dbb4
5 files changed
+920 -10

No files matched your search

@@ -59,6 +59,181 @@ test.describe('Electron Stalker authenticated sessions', () => {
});
});
test('@stalker @electron keeps authenticated catalog and playback identity in main', async ({
dataDir,
}) => {
const replay = await withStalkerReplayRun(
'e2e/full-session-catalog-playback',
async (run) => {
const app = await launchElectronApp(dataDir);
try {
const { ready } = await openStatus2Session(
app.mainWindow,
run,
'e2e-catalog-playback'
);
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 catalog = await app.mainWindow.evaluate(
async ({ leaseRef }) => {
const request =
window.electron?.stalkerSessionRequest;
if (!request) {
throw new Error(
'stalker-session-request-unavailable'
);
}
return request({
leaseRef,
operation: 'get-ordered-list',
parameters: {
category: '*',
contentType: 'itv',
page: 1,
},
});
},
{ leaseRef: ready.leaseRef }
);
if (
catalog.kind !== 'success' ||
catalog.operation !== 'get-ordered-list'
) {
throw new Error(
`expected-catalog-success:${JSON.stringify(catalog)}`
);
}
expect(catalog).toMatchObject({
kind: 'success',
operation: 'get-ordered-list',
payload: {
hasMore: false,
items: [
{
contentType: 'itv',
id: 'channel-1',
title: 'Synthetic Channel',
},
],
page: 1,
pageSize: 1,
total: 1,
totalPages: 1,
},
});
const command = catalog.payload.items[0]?.command;
if (!command) {
throw new Error('expected-catalog-command');
}
const link = await app.mainWindow.evaluate(
async ({ command, leaseRef }) => {
const request =
window.electron?.stalkerSessionRequest;
if (!request) {
throw new Error(
'stalker-session-request-unavailable'
);
}
return request({
leaseRef,
operation: 'create-link',
parameters: {
command,
contentType: 'itv',
},
});
},
{
command,
leaseRef: ready.leaseRef,
}
);
expect(link).toMatchObject({
kind: 'success',
operation: 'create-link',
payload: {
streamUrl: new URL(
'/stream/ready.m3u8',
requiredOrigin(run, 'portal')
).toString(),
},
});
if (
link.kind !== 'success' ||
link.operation !== 'create-link'
) {
throw new Error(
`expected-create-link-success:${JSON.stringify(link)}`
);
}
expect(Object.keys(link.payload).sort()).toEqual([
'playbackContextRef',
'streamUrl',
]);
expect(link.payload.playbackContextRef).toEqual(
expect.any(String)
);
expect(link.payload.playbackContextRef).not.toHaveLength(0);
expect(
JSON.stringify({ catalog, committed, link, ready })
).not.toMatch(/authorization|bearer|cookie|token/i);
} finally {
await closeElectronApp(app);
}
}
);
expect(replay.finalization).toMatchObject({
ledger: {
mismatchCounts: {},
operationCounts: {
'anonymous-probe': 1,
'catalog-page': 1,
'create-link': 1,
'do-auth': 1,
handshake: 1,
'profile-first': 1,
'profile-second': 1,
},
terminalState: 'complete',
},
ok: true,
});
});
test('@stalker @electron keeps concurrent authenticated attempts isolated by replay run', async ({
dataDir,
}) => {
@@ -160,7 +335,9 @@ async function openStatus2Session(
}
);
if (challenge.kind !== 'credentials-required') {
throw new Error('expected-credentials-challenge');
throw new Error(
`expected-credentials-challenge:${JSON.stringify(challenge)}`
);
}
const ready = await page.evaluate(
@@ -198,10 +198,6 @@
"exact": {
"action": "get_profile",
"auth_second_step": "0",
"random": {
"kind": "ref",
"symbol": "challenge"
},
"type": "stb"
},
"present": []
@@ -356,10 +352,6 @@
"exact": {
"action": "get_profile",
"auth_second_step": "1",
"random": {
"kind": "ref",
"symbol": "challenge"
},
"type": "stb"
},
"present": []
@@ -0,0 +1,740 @@
{
"description": "Synthetic full session rotates server cookies across credential authentication, catalog paging, and playback link creation.",
"entry": {
"origin": "portal",
"path": "/c/"
},
"expectedEndpoint": {
"origin": "portal",
"path": "/portal.php"
},
"failOnUnexpectedRequest": true,
"initialState": "start",
"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": "handshake",
"method": "GET",
"operation": "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": {
"accept-language": "en-US",
"user-agent": "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG250",
"x-user-agent": "Model: MAG250"
},
"present": [
"referer"
]
},
"query": {
"absent": [
"password",
"token",
"username"
],
"exact": {
"JsHttpRequest": "1-xml",
"action": "handshake",
"type": "stb"
},
"present": []
}
},
"response": {
"body": {
"kind": "json",
"value": {
"js": {
"random": {
"kind": "ref",
"symbol": "challenge"
},
"token": {
"kind": "ref",
"symbol": "session-token"
}
}
}
},
"headers": {
"content-type": [
"application/json"
],
"set-cookie": [
{
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "session="
},
{
"kind": "ref",
"symbol": "session-cookie-1"
},
{
"kind": "literal",
"value": "; Path=/; HttpOnly"
}
]
}
]
},
"status": 200
}
},
{
"cardinality": {
"max": 1,
"min": 1
},
"id": "profile-first",
"method": "GET",
"operation": "profile-first",
"origin": "portal",
"path": "/portal.php",
"request": {
"body": {
"kind": "absent"
},
"cookies": {
"absent": [],
"attributes": {},
"exact": {
"mac": {
"kind": "ref",
"symbol": "mac"
},
"session": {
"kind": "ref",
"symbol": "session-cookie-1"
},
"stb_lang": "en",
"timezone": "UTC"
},
"present": []
},
"headers": {
"absent": [],
"exact": {
"accept-language": "en-US",
"authorization": {
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "Bearer "
},
{
"kind": "ref",
"symbol": "session-token"
}
]
},
"user-agent": "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG250",
"x-user-agent": "Model: MAG250"
},
"present": [
"referer"
]
},
"query": {
"absent": [
"device_id",
"device_id2",
"password",
"signature",
"signature2",
"sn",
"username"
],
"exact": {
"JsHttpRequest": "1-xml",
"action": "get_profile",
"auth_second_step": "0",
"client_type": "STB",
"hd": "1",
"language": "en",
"not_valid_token": "0",
"stb_type": "MAG250",
"timezone": "UTC",
"type": "stb"
},
"present": [
"metrics"
]
}
},
"response": {
"body": {
"kind": "json",
"value": {
"js": {
"status": 2
}
}
},
"headers": {
"content-type": [
"application/json"
]
},
"status": 200
}
},
{
"cardinality": {
"max": 1,
"min": 1
},
"id": "do-auth",
"method": "GET",
"operation": "do-auth",
"origin": "portal",
"path": "/portal.php",
"request": {
"body": {
"kind": "absent"
},
"cookies": {
"absent": [],
"attributes": {},
"exact": {
"mac": {
"kind": "ref",
"symbol": "mac"
},
"session": {
"kind": "ref",
"symbol": "session-cookie-1"
},
"stb_lang": "en",
"timezone": "UTC"
},
"present": []
},
"headers": {
"absent": [],
"exact": {
"accept-language": "en-US",
"authorization": {
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "Bearer "
},
{
"kind": "ref",
"symbol": "session-token"
}
]
},
"user-agent": "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG250",
"x-user-agent": "Model: MAG250"
},
"present": [
"referer"
]
},
"query": {
"absent": [],
"exact": {
"JsHttpRequest": "1-xml",
"action": "do_auth",
"login": {
"kind": "ref",
"symbol": "username"
},
"password": {
"kind": "ref",
"symbol": "password"
},
"type": "stb"
},
"present": []
}
},
"response": {
"body": {
"kind": "json",
"value": {
"js": true
}
},
"headers": {
"content-type": [
"application/json"
],
"set-cookie": [
{
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "session="
},
{
"kind": "ref",
"symbol": "session-cookie-2"
},
{
"kind": "literal",
"value": "; Path=/; HttpOnly"
}
]
}
]
},
"status": 200
}
},
{
"cardinality": {
"max": 1,
"min": 1
},
"id": "profile-second",
"method": "GET",
"operation": "profile-second",
"origin": "portal",
"path": "/portal.php",
"request": {
"body": {
"kind": "absent"
},
"cookies": {
"absent": [],
"attributes": {},
"exact": {
"mac": {
"kind": "ref",
"symbol": "mac"
},
"session": {
"kind": "ref",
"symbol": "session-cookie-2"
},
"stb_lang": "en",
"timezone": "UTC"
},
"present": []
},
"headers": {
"absent": [],
"exact": {
"accept-language": "en-US",
"authorization": {
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "Bearer "
},
{
"kind": "ref",
"symbol": "session-token"
}
]
},
"user-agent": "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG250",
"x-user-agent": "Model: MAG250"
},
"present": [
"referer"
]
},
"query": {
"absent": [
"device_id",
"device_id2",
"password",
"signature",
"signature2",
"sn",
"username"
],
"exact": {
"JsHttpRequest": "1-xml",
"action": "get_profile",
"auth_second_step": "1",
"client_type": "STB",
"hd": "1",
"language": "en",
"not_valid_token": "0",
"stb_type": "MAG250",
"timezone": "UTC",
"type": "stb"
},
"present": [
"metrics"
]
}
},
"response": {
"body": {
"kind": "json",
"value": {
"js": {
"status": 0
}
}
},
"headers": {
"content-type": [
"application/json"
]
},
"status": 200
}
},
{
"cardinality": {
"max": 1,
"min": 1
},
"id": "catalog-page",
"method": "GET",
"operation": "catalog-page",
"origin": "portal",
"path": "/portal.php",
"request": {
"body": {
"kind": "absent"
},
"cookies": {
"absent": [],
"attributes": {},
"exact": {
"mac": {
"kind": "ref",
"symbol": "mac"
},
"session": {
"kind": "ref",
"symbol": "session-cookie-2"
},
"stb_lang": "en",
"timezone": "UTC"
},
"present": []
},
"headers": {
"absent": [],
"exact": {
"accept-language": "en-US",
"authorization": {
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "Bearer "
},
{
"kind": "ref",
"symbol": "session-token"
}
]
},
"user-agent": "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG250",
"x-user-agent": "Model: MAG250"
},
"present": [
"referer"
]
},
"query": {
"absent": [
"mac",
"password",
"token",
"username"
],
"exact": {
"JsHttpRequest": "1-xml",
"action": "get_ordered_list",
"category": "*",
"p": "1",
"type": "itv"
},
"present": []
}
},
"response": {
"body": {
"kind": "json",
"value": {
"js": {
"cur_page": 1,
"data": [
{
"cmd": "ffmpeg /stream/source.m3u8",
"id": "channel-1",
"name": "Synthetic Channel",
"number": 7,
"title": "Synthetic Channel"
}
],
"max_page_items": 1,
"total_items": 1,
"total_pages": 1
}
}
},
"headers": {
"content-type": [
"application/json"
],
"set-cookie": [
{
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "session="
},
{
"kind": "ref",
"symbol": "session-cookie-3"
},
{
"kind": "literal",
"value": "; Path=/; HttpOnly"
}
]
}
]
},
"status": 200
}
},
{
"cardinality": {
"max": 1,
"min": 1
},
"id": "create-link",
"method": "GET",
"operation": "create-link",
"origin": "portal",
"path": "/portal.php",
"request": {
"body": {
"kind": "absent"
},
"cookies": {
"absent": [],
"attributes": {},
"exact": {
"mac": {
"kind": "ref",
"symbol": "mac"
},
"session": {
"kind": "ref",
"symbol": "session-cookie-3"
},
"stb_lang": "en",
"timezone": "UTC"
},
"present": []
},
"headers": {
"absent": [],
"exact": {
"accept-language": "en-US",
"authorization": {
"kind": "parts",
"parts": [
{
"kind": "literal",
"value": "Bearer "
},
{
"kind": "ref",
"symbol": "session-token"
}
]
},
"user-agent": "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG250",
"x-user-agent": "Model: MAG250"
},
"present": [
"referer"
]
},
"query": {
"absent": [
"mac",
"password",
"token",
"username"
],
"exact": {
"JsHttpRequest": "1-xml",
"action": "create_link",
"cmd": "ffmpeg /stream/source.m3u8",
"disable_ad": "0",
"download": "0",
"type": "itv"
},
"present": []
}
},
"response": {
"body": {
"kind": "json",
"value": {
"js": {
"cmd": "/stream/ready.m3u8"
}
}
},
"headers": {
"content-type": [
"application/json"
]
},
"status": 200
}
}
],
"mode": "ordered",
"name": "full-session-catalog-playback",
"nextState": "complete",
"state": "start"
}
],
"scenarioId": "e2e-full-session-catalog-playback",
"schemaVersion": 1,
"symbols": [
{
"kind": "generate",
"symbol": "mac",
"valueKind": "mac"
},
{
"kind": "generate",
"symbol": "username",
"valueKind": "credential"
},
{
"kind": "generate",
"symbol": "password",
"valueKind": "credential"
},
{
"kind": "generate",
"symbol": "session-token",
"valueKind": "token"
},
{
"kind": "generate",
"symbol": "challenge",
"valueKind": "random"
},
{
"kind": "generate",
"symbol": "session-cookie-1",
"valueKind": "cookie"
},
{
"kind": "generate",
"symbol": "session-cookie-2",
"valueKind": "cookie"
},
{
"kind": "generate",
"symbol": "session-cookie-3",
"valueKind": "cookie"
}
],
"terminalState": "complete"
}
@@ -534,7 +534,7 @@ describe('replay control-plane lifecycle and public response', () => {
),
},
inputs: {
mac: expect.stringMatching(/^02(?::[a-f0-9]{2}){5}$/),
mac: expect.stringMatching(/^02(?::[A-F0-9]{2}){5}$/),
password: expect.stringMatching(/^test-credential-/),
username: expect.stringMatching(/^test-credential-/),
},
@@ -36,6 +36,7 @@ const EXPECTED_SCENARIOS = [
'classifier-waf-403',
'cookies-managed-shadow',
'cookies-session-isolation',
'e2e-full-session-catalog-playback',
'redirect-auth-query',
'redirect-identity-pause',
'redirect-landing-approval',