Spaces:
Paused
Paused
| import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; | |
| import fs from 'node:fs'; | |
| import path from 'node:path'; | |
| import { | |
| COMMON_ARGS, | |
| TEMP_DIR, | |
| bootstrapCookies, | |
| } from '../../src/services/ytdlp/config.js'; | |
| const COOKIES_PATH = path.join(TEMP_DIR, 'cookies.txt'); | |
| function resetCommonArgs(): void { | |
| // remove any --cookies flag and its value | |
| while (true) { | |
| const idx = COMMON_ARGS.indexOf('--cookies'); | |
| if (idx === -1) break; | |
| COMMON_ARGS.splice(idx, 2); | |
| } | |
| } | |
| vi.mock('../../src/utils/network/security.util.js', () => ({ | |
| secureFetch: vi.fn(), | |
| getSecureUndiciAgent: vi.fn(() => null), | |
| resolveAndValidateHost: vi.fn(), | |
| })); | |
| import { secureFetch } from '../../src/utils/network/security.util.js'; | |
| const mockedFetch = vi.mocked(secureFetch); | |
| const SAMPLE_BAD_HEADER = `# Netscape NEW COKKIE | |
| # https://curl.haxx.se/rfc/cookie_spec.html | |
| .youtube.com\tTRUE\t/\tTRUE\t1814451931\tPREF\ttz=Asia.Manila | |
| .youtube.com\tTRUE\t/\tTRUE\t1791420556\t__Secure-BUCKET\tCNkB | |
| `; | |
| const SAMPLE_GOOD_HEADER = `# Netscape HTTP Cookie File | |
| .youtube.com\tTRUE\t/\tTRUE\t1814451931\tPREF\ttz=Asia.Manila | |
| `; | |
| describe('Cookie bootstrap (Phase 1.5.1)', () => { | |
| beforeEach(() => { | |
| vi.clearAllMocks(); | |
| resetCommonArgs(); | |
| process.env.COOKIES_URL = 'https://example.com/cookies-host'; | |
| if (fs.existsSync(COOKIES_PATH)) fs.unlinkSync(COOKIES_PATH); | |
| }); | |
| afterEach(() => { | |
| resetCommonArgs(); | |
| delete process.env.COOKIES_URL; | |
| if (fs.existsSync(COOKIES_PATH)) fs.unlinkSync(COOKIES_PATH); | |
| }); | |
| it('rewrites malformed "Netscape NEW COKKIE" header to "Netscape HTTP Cookie File"', async () => { | |
| mockedFetch.mockImplementation((url: string) => { | |
| if (url.endsWith('/youtube_cookies.txt')) { | |
| return Promise.resolve(new Response('', { status: 404 })); | |
| } | |
| return Promise.resolve(new Response(SAMPLE_BAD_HEADER, { status: 200 })); | |
| }); | |
| await bootstrapCookies(); | |
| const written = fs.readFileSync(COOKIES_PATH, 'utf8'); | |
| expect(written.split('\n')[0]).toBe('# Netscape HTTP Cookie File'); | |
| expect(written).not.toContain('NEW COKKIE'); | |
| expect(written).toContain('.youtube.com'); | |
| }); | |
| it('preserves valid "Netscape HTTP Cookie File" header unchanged', async () => { | |
| mockedFetch.mockImplementation((url: string) => { | |
| if (url.endsWith('/youtube_cookies.txt')) { | |
| return Promise.resolve(new Response('', { status: 404 })); | |
| } | |
| return Promise.resolve(new Response(SAMPLE_GOOD_HEADER, { status: 200 })); | |
| }); | |
| await bootstrapCookies(); | |
| const written = fs.readFileSync(COOKIES_PATH, 'utf8'); | |
| expect(written.split('\n')[0]).toBe('# Netscape HTTP Cookie File'); | |
| expect(written).toContain('.youtube.com'); | |
| }); | |
| it('skips download when content does not contain youtube.com (invalid cookies)', async () => { | |
| mockedFetch.mockResolvedValue( | |
| new Response('not a cookie file', { status: 200 }) | |
| ); | |
| await bootstrapCookies(); | |
| expect(fs.existsSync(COOKIES_PATH)).toBe(false); | |
| }); | |
| it('falls back to base URL when /youtube_cookies.txt returns 404', async () => { | |
| const calls: string[] = []; | |
| mockedFetch.mockImplementation((url: string) => { | |
| calls.push(url); | |
| if (url.endsWith('/youtube_cookies.txt')) { | |
| return Promise.resolve(new Response('', { status: 404 })); | |
| } | |
| return Promise.resolve(new Response(SAMPLE_BAD_HEADER, { status: 200 })); | |
| }); | |
| await bootstrapCookies(); | |
| expect(calls.length).toBe(2); | |
| expect(calls[0]).toContain('/youtube_cookies.txt'); | |
| expect(calls[1]).not.toContain('/youtube_cookies.txt'); | |
| expect(fs.existsSync(COOKIES_PATH)).toBe(true); | |
| }); | |
| }); | |