Files
iptvnator/libs/shared/interfaces/src/lib/stalker-request-identity.util.spec.ts
T
4grayandClaude Fable 5 65f81b7110 fix(pwa): bring the Stalker transport to parity with Electron (#1348)
* fix(pwa): bring the Stalker transport to parity with Electron

The self-hosted PWA's /stalker proxy now derives its portal requests from
the same shared identity and URL builders as the Electron main process:
MAG User-Agent/X-User-Agent, full STB cookie (mac + stb_lang + timezone +
serial-derived __cfduid), SN header, JsHttpRequest=1-xml defaulting, and
the sn-only-on-get_profile rule. macAddress/token/serialNumber are control
params consumed into headers and never echoed into the portal's query
string (handshake keeps its candidate token — protocol content). The
stalker-mock-server /stalker route mirrors the new contract through the
same shared builder.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(stalker): forward the full identity header set in the mock /stalker mirror

Greptile review: the synthetic portal request kept only the cookie and
Authorization from the generated identity, so mock handlers could never
validate the SN/MAG-UA/Accept/Language/Connection headers the real proxy
sends. Forward the complete set, lowercased the way Express normalizes
incoming headers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 14:51:36 +02:00

94 lines
3.3 KiB
TypeScript

import { LEGACY_DEFAULT_STALKER_SERIAL } from './stalker-identity.utils';
import { buildStalkerIdentityRequestContext } from './stalker-request-identity.util';
describe('buildStalkerIdentityRequestContext', () => {
const macAddress = '00:1A:79:AA:BB:CC';
it('does not add SN header or force get_profile SN fields when serial is absent', () => {
const context = buildStalkerIdentityRequestContext({
macAddress,
params: {
type: 'stb',
action: 'get_profile',
sn: 'STALE-SN',
metrics: JSON.stringify({
mac: macAddress,
sn: 'STALE-SN',
}),
},
});
expect(context.effectiveSerialNumber).toBeUndefined();
expect(context.headers).not.toHaveProperty('SN');
expect(context.cookieString).not.toContain('__cfduid=');
expect(context.requestParams).not.toHaveProperty('sn');
expect(
JSON.parse(String(context.requestParams['metrics']))
).not.toHaveProperty('sn');
});
it('preserves a provided serial exactly in headers, params, and metrics', () => {
const context = buildStalkerIdentityRequestContext({
macAddress,
serialNumber: 'CustomSn123',
params: {
type: 'stb',
action: 'get_profile',
metrics: JSON.stringify({
mac: macAddress,
}),
},
});
expect(context.effectiveSerialNumber).toBe('CustomSn123');
expect(context.headers['SN']).toBe('CustomSn123');
expect(context.cookieString).toContain('__cfduid=');
expect(
context.cookieString.match(/__cfduid=([^;]+)/)?.[1]
).toHaveLength(32);
expect(context.requestParams['sn']).toBe('CustomSn123');
expect(JSON.parse(String(context.requestParams['metrics']))).toEqual(
expect.objectContaining({
sn: 'CustomSn123',
})
);
});
it('treats the legacy default serial as absent', () => {
const context = buildStalkerIdentityRequestContext({
macAddress,
serialNumber: LEGACY_DEFAULT_STALKER_SERIAL,
params: {
type: 'stb',
action: 'get_profile',
sn: LEGACY_DEFAULT_STALKER_SERIAL,
metrics: JSON.stringify({
mac: macAddress,
sn: LEGACY_DEFAULT_STALKER_SERIAL,
}),
},
});
expect(context.effectiveSerialNumber).toBeUndefined();
expect(context.headers).not.toHaveProperty('SN');
expect(context.cookieString).not.toContain('__cfduid=');
expect(context.requestParams).not.toHaveProperty('sn');
expect(
JSON.parse(String(context.requestParams['metrics']))
).not.toHaveProperty('sn');
});
it('removes stale SN params from non-profile requests', () => {
const context = buildStalkerIdentityRequestContext({
macAddress,
params: {
type: 'itv',
action: 'get_ordered_list',
sn: 'STALE-SN',
},
});
expect(context.requestParams).not.toHaveProperty('sn');
});
});