Files
goon_plugin/__tests__/data_flow.test.mjs
T
kfj001 4357ce4094
CI / test (pull_request) Failing after 20s
Fixes issue with pornhub. Removes references.
2026-08-30 04:48:23 +00:00

220 lines
8.3 KiB
JavaScript

import { describe, it } from './runner.mjs';
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
const schema = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '../src/viewer/schema.json'), 'utf8'));
const ajv = new Ajv({ strict: false });
addFormats(ajv);
const validate = ajv.compile(schema);
describe('Data flow: popup -> storage -> viewer', () => {
it('should pass a scraped HLS entry through schema validation', () => {
// Simulate popup scraping an HLS stream
const scrapedEntry = {
url: 'https://stream.example.com/watch',
src: 'https://cdn.example.com/live/playlist.m3u8',
poster: 'https://cdn.example.com/live/thumb.jpg',
tab: 'Live Broadcast',
hlsManifest: 'https://cdn.example.com/live/playlist.m3u8',
mediaType: 'hls',
};
// Validate against schema (as storage layer would)
const valid = validate(scrapedEntry);
if (!valid) {
throw new Error(`Scraped HLS entry fails schema: ${JSON.stringify(validate.errors)}`);
}
// Verify the viewer would detect it as HLS
const isHls = (entry) => typeof entry === 'object' && entry.mediaType === 'hls';
if (!isHls(scrapedEntry)) {
throw new Error('Scraped HLS entry not detected as HLS by viewer');
}
// Verify the viewer would resolve the manifest
const resolveHlsManifest = (entry) => {
if (typeof entry === 'object' && entry.hlsManifest) return entry.hlsManifest;
return null;
};
const manifest = resolveHlsManifest(scrapedEntry);
if (manifest !== 'https://cdn.example.com/live/playlist.m3u8') {
throw new Error(`Manifest URL wrong: ${manifest}`);
}
});
it('should pass a scraped direct entry through schema validation', () => {
const scrapedEntry = {
url: 'https://video.example.com/watch',
src: 'https://cdn.example.com/video.mp4',
poster: 'https://cdn.example.com/thumb.jpg',
tab: 'Video Page',
};
const valid = validate(scrapedEntry);
if (!valid) {
throw new Error(`Scraped direct entry fails schema: ${JSON.stringify(validate.errors)}`);
}
const isHls = (entry) => typeof entry === 'object' && entry.mediaType === 'hls';
if (isHls(scrapedEntry)) {
throw new Error('Direct entry falsely detected as HLS');
}
const resolveSrc = (entry) => {
if (typeof entry === 'string') return entry;
return entry.src || entry.url || '';
};
const src = resolveSrc(scrapedEntry);
if (src !== 'https://cdn.example.com/video.mp4') {
throw new Error(`Src URL wrong: ${src}`);
}
});
it('should survive a round-trip: storage serialization -> deserialization', () => {
const originalEntry = {
url: 'https://example.com/stream',
src: 'https://cdn.example.com/stream.m3u8',
poster: 'https://example.com/thumb.jpg',
tab: 'HLS Stream',
hlsManifest: 'https://cdn.example.com/stream.m3u8',
mediaType: 'hls',
};
// Simulate storage round-trip (JSON serialization)
const serialized = JSON.stringify(originalEntry);
const deserialized = JSON.parse(serialized);
// Validate after deserialization
const valid = validate(deserialized);
if (!valid) {
throw new Error(`Round-tripped entry fails schema: ${JSON.stringify(validate.errors)}`);
}
// Verify fields are intact
if (deserialized.mediaType !== 'hls') {
throw new Error('mediaType lost during round-trip');
}
if (deserialized.hlsManifest !== 'https://cdn.example.com/stream.m3u8') {
throw new Error('hlsManifest lost during round-trip');
}
// Verify viewer still detects it correctly
const isHls = (entry) => typeof entry === 'object' && entry.mediaType === 'hls';
if (!isHls(deserialized)) {
throw new Error('Round-tripped entry not detected as HLS');
}
});
it('should handle batch entries from popup -> storage -> viewer pipeline', () => {
// Simulate popup storing multiple entries
const popupEntries = [
{ url: 'https://example.com/1', src: 'https://cdn.example.com/1.mp4', tab: 'Tab 1' },
{
url: 'https://example.com/2',
src: 'https://cdn.example.com/2.m3u8',
hlsManifest: 'https://cdn.example.com/2.m3u8',
mediaType: 'hls',
tab: 'Tab 2',
},
{ url: 'https://example.com/3', src: 'https://cdn.example.com/3.webm', mediaType: 'direct', tab: 'Tab 3' },
];
// Validate all entries
for (const [i, entry] of popupEntries.entries()) {
const valid = validate(entry);
if (!valid) {
throw new Error(`Entry ${i} fails schema: ${JSON.stringify(validate.errors)}`);
}
}
// Simulate storage round-trip
const serialized = JSON.stringify(popupEntries);
const deserialized = JSON.parse(serialized);
// Verify viewer can process all entries
const isHls = (entry) => typeof entry === 'object' && entry.mediaType === 'hls';
const resolveSrc = (entry) => {
if (typeof entry === 'string') return entry;
return entry.src || entry.url || '';
};
const resolveHlsManifest = (entry) => {
if (typeof entry === 'object' && entry.hlsManifest) return entry.hlsManifest;
return null;
};
// Entry 0: legacy (no mediaType)
if (isHls(deserialized[0])) throw new Error('Entry 0 should not be HLS');
if (resolveSrc(deserialized[0]) !== 'https://cdn.example.com/1.mp4') throw new Error('Entry 0 src wrong');
// Entry 1: HLS
if (!isHls(deserialized[1])) throw new Error('Entry 1 should be HLS');
if (resolveHlsManifest(deserialized[1]) !== 'https://cdn.example.com/2.m3u8') throw new Error('Entry 1 manifest wrong');
// Entry 2: explicit direct
if (isHls(deserialized[2])) throw new Error('Entry 2 should not be HLS');
if (resolveSrc(deserialized[2]) !== 'https://cdn.example.com/3.webm') throw new Error('Entry 2 src wrong');
});
it('should reject malformed entries that could crash the viewer', () => {
const malformedEntries = [
{ hlsManifest: 'https://example.com/stream.m3u8' }, // missing url
{ url: 'https://example.com/video.mp4', mediaType: 'invalid' }, // invalid mediaType
{ url: 'not-a-url' }, // technically valid per schema (format: uri not enforced by Ajv without custom formats)
];
for (const [i, entry] of malformedEntries.entries()) {
const valid = validate(entry);
if (!valid && i < 2) {
// Expected failures for entries 0 and 1
continue;
}
}
// Entry 0 (missing url) should be rejected
const entry0Valid = validate(malformedEntries[0]);
if (entry0Valid) {
throw new Error('Entry without url should be rejected');
}
// Entry 1 (invalid mediaType) should be rejected
const entry1Valid = validate(malformedEntries[1]);
if (entry1Valid) {
throw new Error('Entry with invalid mediaType should be rejected');
}
});
it('an HLS entry carrying a scan-time referer passes schema + round-trips', () => {
// The referer is bound to the entry at scan time so it survives the
// watch-later flow (the source tab may be long closed). Because the schema
// sets additionalProperties:false, a `referer` the store carries must be an
// explicit property or storage-layer validation would reject the entry.
const scrapedEntry = {
url: 'https://pageA.example.com/watch',
src: 'https://hv-h.phncdn.com/hls/live/master.m3u8',
hlsManifest: 'https://hv-h.phncdn.com/hls/live/master.m3u8',
mediaType: 'hls',
poster: 'https://pageA.example.com/thumb.jpg',
tab: 'Referer-protected stream',
referer: 'https://pageA.example.com/watch',
};
const valid = validate(scrapedEntry);
if (!valid) {
throw new Error(`Entry with referer fails schema: ${JSON.stringify(validate.errors)}`);
}
// The referer must survive the storage round-trip intact so the viewer can
// send it to the proxy when the user opens a previously-scanned entry.
const roundTripped = JSON.parse(JSON.stringify(scrapedEntry));
if (roundTripped.referer !== 'https://pageA.example.com/watch') {
throw new Error('referer lost during round-trip');
}
if (!validate(roundTripped)) {
throw new Error('Round-tripped entry with referer fails schema');
}
});
});