Spaces:
Paused
Paused
| import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest'; | |
| import { setupServer } from 'msw/node'; | |
| import { http, HttpResponse } from 'msw'; | |
| import { fetchYoutubeOEmbed } from '../../src/utils/media/metadata.util.js'; | |
| /** | |
| * Regression tests for the YouTube oEmbed fast-path. | |
| * | |
| * fetchYoutubeOEmbed is the early-hit fetcher that replaced the slow | |
| * (1-3s) metascraper HTML download for YouTube URLs. It must: | |
| * 1. Return author_name as the channel display name (NOT a URL slug). | |
| * 2. Build a high-resolution thumbnail URL from the video id. | |
| * 3. Gracefully return null on any error (401, 404, network) so the | |
| * caller falls back to the next strategy instead of crashing. | |
| * 4. Handle multiple URL forms (youtu.be, /shorts/, /watch?v=). | |
| */ | |
| const server = setupServer(); | |
| beforeAll(() => server.listen({ onUnhandledRequest: 'bypass' })); | |
| afterEach(() => server.resetHandlers()); | |
| afterAll(() => server.close()); | |
| describe('fetchYoutubeOEmbed — happy path', () => { | |
| it('returns the channel display name (author_name) and title', async () => { | |
| server.use( | |
| http.get('https://www.youtube.com/oembed', () => | |
| HttpResponse.json({ | |
| title: 'Example Video Title', | |
| author_name: 'NJM RUI', | |
| author_url: 'https://www.youtube.com/@nijummd-ru1jz', | |
| thumbnail_url: 'https://i.ytimg.com/vi/nTbA7qrEsP0/default.jpg', | |
| provider_name: 'YouTube', | |
| }) | |
| ) | |
| ); | |
| const meta = await fetchYoutubeOEmbed( | |
| 'https://www.youtube.com/watch?v=nTbA7qrEsP0' | |
| ); | |
| expect(meta).not.toBeNull(); | |
| expect(meta?.title).toBe('Example Video Title'); | |
| expect(meta?.author).toBe('NJM RUI'); | |
| expect(meta?.publisher).toBe('YouTube'); | |
| }); | |
| it('upgrades the thumbnail URL to hqdefault', async () => { | |
| server.use( | |
| http.get('https://www.youtube.com/oembed', () => | |
| HttpResponse.json({ | |
| title: 'X', | |
| author_name: 'Y', | |
| thumbnail_url: 'https://i.ytimg.com/vi/abcDEF12345/default.jpg', | |
| }) | |
| ) | |
| ); | |
| const meta = await fetchYoutubeOEmbed('https://youtu.be/abcDEF12345'); | |
| expect(meta?.image).toBe( | |
| 'https://i.ytimg.com/vi/abcDEF12345/hqdefault.jpg' | |
| ); | |
| }); | |
| it('handles youtu.be short URLs', async () => { | |
| server.use( | |
| http.get('https://www.youtube.com/oembed', ({ request }) => { | |
| const parsed = new URL(request.url); | |
| const target = parsed.searchParams.get('url'); | |
| expect(target).toContain('youtube.com/watch?v=abcDEF12345'); | |
| return HttpResponse.json({ | |
| title: 'Short URL Test', | |
| author_name: 'Channel', | |
| }); | |
| }) | |
| ); | |
| const meta = await fetchYoutubeOEmbed('https://youtu.be/abcDEF12345'); | |
| expect(meta?.title).toBe('Short URL Test'); | |
| }); | |
| it('handles /shorts/ URLs', async () => { | |
| server.use( | |
| http.get('https://www.youtube.com/oembed', () => | |
| HttpResponse.json({ title: 'Shorts Title', author_name: 'Creator' }) | |
| ) | |
| ); | |
| const meta = await fetchYoutubeOEmbed( | |
| 'https://www.youtube.com/shorts/abcDEF12345' | |
| ); | |
| expect(meta?.title).toBe('Shorts Title'); | |
| }); | |
| }); | |
| describe('fetchYoutubeOEmbed — failure modes return null (no throw)', () => { | |
| it('returns null on 401 Unauthorized (private/unlisted videos)', async () => { | |
| server.use( | |
| http.get( | |
| 'https://www.youtube.com/oembed', | |
| () => new HttpResponse('Unauthorized', { status: 401 }) | |
| ) | |
| ); | |
| const meta = await fetchYoutubeOEmbed( | |
| 'https://www.youtube.com/watch?v=privateXYZ1' | |
| ); | |
| expect(meta).toBeNull(); | |
| }); | |
| it('returns null on 404 Not Found (deleted videos)', async () => { | |
| server.use( | |
| http.get( | |
| 'https://www.youtube.com/oembed', | |
| () => new HttpResponse('Not Found', { status: 404 }) | |
| ) | |
| ); | |
| const meta = await fetchYoutubeOEmbed( | |
| 'https://www.youtube.com/watch?v=deletedABC1' | |
| ); | |
| expect(meta).toBeNull(); | |
| }); | |
| it('returns null on network error', async () => { | |
| server.use( | |
| http.get('https://www.youtube.com/oembed', () => HttpResponse.error()) | |
| ); | |
| const meta = await fetchYoutubeOEmbed( | |
| 'https://www.youtube.com/watch?v=netfailABC1' | |
| ); | |
| expect(meta).toBeNull(); | |
| }); | |
| it('returns null when title and author are both missing', async () => { | |
| server.use( | |
| http.get('https://www.youtube.com/oembed', () => | |
| HttpResponse.json({ provider_name: 'YouTube' }) | |
| ) | |
| ); | |
| const meta = await fetchYoutubeOEmbed( | |
| 'https://www.youtube.com/watch?v=missingXYZ1' | |
| ); | |
| expect(meta).toBeNull(); | |
| }); | |
| it('returns null when the URL has no extractable video id', async () => { | |
| const meta = await fetchYoutubeOEmbed( | |
| 'https://www.youtube.com/results?search_query=foo' | |
| ); | |
| expect(meta).toBeNull(); | |
| }); | |
| }); | |
| describe('fetchYoutubeOEmbed — partial responses', () => { | |
| it('keeps title even when author_name is missing', async () => { | |
| server.use( | |
| http.get('https://www.youtube.com/oembed', () => | |
| HttpResponse.json({ title: 'Only Title Here' }) | |
| ) | |
| ); | |
| const meta = await fetchYoutubeOEmbed( | |
| 'https://www.youtube.com/watch?v=titleOnlyA1' | |
| ); | |
| expect(meta?.title).toBe('Only Title Here'); | |
| expect(meta?.author).toBeNull(); | |
| }); | |
| it('keeps author when title is missing', async () => { | |
| server.use( | |
| http.get('https://www.youtube.com/oembed', () => | |
| HttpResponse.json({ author_name: 'Only Author Here' }) | |
| ) | |
| ); | |
| const meta = await fetchYoutubeOEmbed( | |
| 'https://www.youtube.com/watch?v=authorOnlyAB' | |
| ); | |
| expect(meta?.author).toBe('Only Author Here'); | |
| expect(meta?.title).toBeNull(); | |
| }); | |
| }); | |