import { afterEach, describe, expect, it } from 'vitest'; import { YtDlpError, runYtDlp } from '../src/services/ytDlpRunner.js'; describe('runYtDlp', () => { afterEach(() => { // biome-ignore lint/performance/noDelete: une assignation à `undefined` stockerait la chaîne "undefined" dans process.env. delete process.env.YTDLP_PATH; }); it('retourne stdout quand le processus réussit', async () => { process.env.YTDLP_PATH = process.execPath; const stdout = await runYtDlp(['-e', "console.log('bonjour')"]); expect(stdout.trim()).toBe('bonjour'); }); it('lève YtDlpError quand le processus se termine en échec', async () => { process.env.YTDLP_PATH = process.execPath; await expect(runYtDlp(['-e', "console.error('boom'); process.exit(3)"])).rejects.toBeInstanceOf( YtDlpError, ); }); it('appelle onStdoutLine pour chaque ligne, sans attendre la fin du processus', async () => { process.env.YTDLP_PATH = process.execPath; const lines: string[] = []; await runYtDlp(['-e', "console.log('a'); console.log('b'); console.log('c')"], { onStdoutLine: (line) => lines.push(line), }); expect(lines).toEqual(['a', 'b', 'c']); }); it('émet le fragment final même sans saut de ligne terminal', async () => { process.env.YTDLP_PATH = process.execPath; const lines: string[] = []; await runYtDlp(['-e', "process.stdout.write('sans-newline')"], { onStdoutLine: (line) => lines.push(line), }); expect(lines).toEqual(['sans-newline']); }); it('donne le processus lancé à onSpawn, avant toute sortie', async () => { process.env.YTDLP_PATH = process.execPath; const spawns: Array<{ pid: number | undefined; canPause: boolean; canResume: boolean; linesSoFar: number; }> = []; let lines = 0; await runYtDlp(['-e', "console.log('a')"], { onSpawn: (child) => { spawns.push({ pid: child.pid, canPause: typeof child.stdout.pause === 'function', canResume: typeof child.stdout.resume === 'function', linesSoFar: lines, }); }, onStdoutLine: () => { lines += 1; }, }); expect(spawns).toEqual([ { pid: expect.any(Number), canPause: true, canResume: true, linesSoFar: 0 }, ]); expect(lines).toBe(1); }); it("rejette avec une AbortError quand le signal d'annulation se déclenche", async () => { process.env.YTDLP_PATH = process.execPath; const controller = new AbortController(); const promise = runYtDlp(['-e', 'setTimeout(() => {}, 5000)'], { signal: controller.signal }); controller.abort(); await expect(promise).rejects.toMatchObject({ name: 'AbortError' }); }); });