Files
iptvnator/apps/electron-backend/src/app/workers/epg-response-utils.spec.ts
T
4gray 36a7ce9f3d fix(epg): decode compressed XMLTV responses
Decode HTTP Content-Encoding for EPG XMLTV streams before SAX parsing and harden gzip payload handling for mislabelled providers.

Validation:
- pnpm nx test electron-backend --testFile=epg-stream-decoder.spec.ts
- pnpm nx test electron-backend --testFile=epg-response-utils.spec.ts
- pnpm nx test electron-backend
- pnpm nx lint electron-backend
- pnpm nx run electron-backend:build-worker
2026-06-21 09:34:37 +02:00

119 lines
3.6 KiB
TypeScript

import {
getEpgResponseContentEncoding,
shouldGunzipEpgResponse,
} from './epg-response-utils';
describe('shouldGunzipEpgResponse', () => {
it('returns true for original .gz URLs', () => {
expect(
shouldGunzipEpgResponse('https://example.com/guide.xml.gz', {
headers: new Headers(),
url: 'https://example.com/guide.xml.gz',
})
).toBe(true);
});
it('returns true when a redirect lands on a .gz URL', () => {
expect(
shouldGunzipEpgResponse('http://epg.vcboy.com', {
headers: new Headers(),
url: 'http://iptv-worker.sapireli.workers.dev/epg.xml.gz',
})
).toBe(true);
});
it('returns false for gzip content-encoding without a gzip payload', () => {
expect(
shouldGunzipEpgResponse('https://example.com/guide', {
headers: new Headers([['content-encoding', 'gzip']]),
url: 'https://example.com/guide',
})
).toBe(false);
});
it('returns true for gzip mime types', () => {
expect(
shouldGunzipEpgResponse('https://example.com/guide', {
headers: new Headers([['content-type', 'application/gzip']]),
url: 'https://example.com/guide',
})
).toBe(true);
});
it('supports plain Axios response header objects', () => {
expect(
shouldGunzipEpgResponse('https://example.com/guide', {
headers: {
'Content-Type': 'application/gzip',
},
url: 'https://example.com/guide',
})
).toBe(true);
});
it('returns true when content-disposition advertises a .gz filename', () => {
expect(
shouldGunzipEpgResponse('https://example.com/guide', {
headers: new Headers([
[
'content-disposition',
'attachment; filename="guide.xml.gz"',
],
]),
url: 'https://example.com/guide',
})
).toBe(true);
});
it('returns false for plain XML responses', () => {
expect(
shouldGunzipEpgResponse('https://example.com/guide.xml', {
headers: new Headers([['content-type', 'application/xml']]),
url: 'https://example.com/guide.xml',
})
).toBe(false);
});
});
describe('getEpgResponseContentEncoding', () => {
it('detects Brotli transfer encoding from response headers', () => {
expect(
getEpgResponseContentEncoding({
'content-encoding': 'br',
})
).toBe('br');
});
it('detects gzip transfer encoding from response headers', () => {
expect(
getEpgResponseContentEncoding(
new Headers([['content-encoding', 'gzip']])
)
).toBe('gzip');
});
it('detects deflate transfer encoding from response headers', () => {
expect(
getEpgResponseContentEncoding({
'Content-Encoding': 'deflate',
})
).toBe('deflate');
});
it('ignores unsupported transfer encodings', () => {
expect(
getEpgResponseContentEncoding({
'content-encoding': 'zstd',
})
).toBe(null);
});
it('returns the outermost supported transfer encoding first', () => {
expect(
getEpgResponseContentEncoding({
'content-encoding': 'gzip, br',
})
).toBe('br');
});
});