const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const vm = require('node:vm'); // Exercise the actual app functions without DOM/player/network dependencies. const source = fs.readFileSync(require('node:path').join(__dirname, 'app.js'), 'utf8'); function harness({ autoPreload = true, cached = [], result = { ok: true, cached: true }, error } = {}) { const calls = [], jobs = new Map(); const video = { id: 'benchmedia01', title: 'Local clip' }; const context = vm.createContext({ data: { settings: { autoPreload }, playlists: [{ id: 'pl', videos: [video] }], pinnedPlaylists: [] }, cachedIds: new Set(cached), downloading: new Set(), downloadMeta: new Map(), saveControllers: new Map(), view: { type: 'home' }, current: null, window: {}, AbortController, slim: v => v, markCardCacheState() {}, renderSidebar() {}, updateDownloadBadge() {}, toast() {}, SaveSlots: { acquire: async () => {}, release() {} }, SaveQueue: { add: v => jobs.set(v.id, { status: 'queued' }), remove: id => jobs.delete(id), progress() {}, state: (id, status, message) => jobs.set(id, { status, message }) }, API: { cacheDownload: async id => { calls.push(id); if (error) throw error; return result; } }, recordDeviceFile() {}, warmThumb() {}, thumbUrlFor: () => '', cacheMutations: 0, }); const preload = source.slice(source.indexOf('async function preload(video,'), source.indexOf('// A device copy that failed to play.')); const playlists = source.slice(source.indexOf('function preloadPlaylist(pl)'), source.indexOf('// Update a single card\'s saved badge')); vm.runInContext(preload + playlists, context); return { context, calls, jobs, video }; } test('auto preload tops up playlists, while pinned playlists save with the setting off', async () => { const h = harness({ autoPreload: false }); h.context.preloadPlaylist(h.context.data.playlists[0]); await new Promise(r => setImmediate(r)); assert.deepEqual(h.calls, []); h.context.data.pinnedPlaylists = ['pl']; h.context.preloadPinnedPlaylists(); await new Promise(r => setImmediate(r)); assert.deepEqual(h.calls, [h.video.id]); assert.ok(h.context.cachedIds.has(h.video.id)); assert.equal(h.jobs.size, 0); assert.equal(h.context.downloading.size, 0); const enabled = harness(); enabled.context.preloadPlaylist(enabled.context.data.playlists[0]); await new Promise(r => setImmediate(r)); assert.deepEqual(enabled.calls, [enabled.video.id]); }); test('a repeated save shares the in-flight operation and does not download an existing copy', async () => { const h = harness(); await Promise.all([h.context.preload(h.video), h.context.preload(h.video)]); await h.context.preload(h.video); assert.deepEqual(h.calls, [h.video.id]); assert.ok(h.context.cachedIds.has(h.video.id)); assert.equal(h.context.saveControllers.size, 0); }); test('failed and interrupted saves do not acquire a Saved badge and can be attempted again', async () => { for (const options of [{ result: { ok: false, error: 'server failed' } }, { error: new Error('storage quota') }, { result: { ok: false, paused: true } }]) { const h = harness(options); await h.context.preload(h.video, { quiet: true }); assert.equal(h.context.cachedIds.has(h.video.id), false); assert.equal(h.context.downloading.size, 0); assert.equal(h.context.saveControllers.size, 0); assert.ok(h.jobs.has(h.video.id)); await h.context.preload(h.video, { quiet: true }); assert.equal(h.calls.length, 2); } });