import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as smartfeed from '../ts/index.js'; let testSmartFeed: smartfeed.Smartfeed; tap.test('setup', async () => { testSmartFeed = new smartfeed.Smartfeed(); }); tap.test('should validate required podcast fields', async () => { let errorThrown = false; try { testSmartFeed.createPodcastFeed({ domain: 'test.com', title: 'Test', description: 'Test', category: 'Test', company: 'Test Inc', companyEmail: 'test@example.com', companyDomain: 'https://example.com', // Missing iTunes required fields } as any); } catch (error) { errorThrown = true; expect(error.message).toInclude('validation failed'); } expect(errorThrown).toEqual(true); }); tap.test('should validate iTunes owner email', async () => { let errorThrown = false; try { testSmartFeed.createPodcastFeed({ domain: 'test.com', title: 'Test', description: 'Test', category: 'Test', company: 'Test Inc', companyEmail: 'test@example.com', companyDomain: 'https://example.com', itunesCategory: 'Technology', itunesAuthor: 'Author', itunesOwner: { name: 'Owner', email: 'not-an-email' }, itunesImage: 'https://example.com/image.jpg', itunesExplicit: false, podcastGuid: 'test-validation-guid-001', }); } catch (error) { errorThrown = true; expect(error.message).toInclude('Invalid email'); } expect(errorThrown).toEqual(true); }); tap.test('should validate iTunes image URL', async () => { let errorThrown = false; try { testSmartFeed.createPodcastFeed({ domain: 'test.com', title: 'Test', description: 'Test', category: 'Test', company: 'Test Inc', companyEmail: 'test@example.com', companyDomain: 'https://example.com', itunesCategory: 'Technology', itunesAuthor: 'Author', itunesOwner: { name: 'Owner', email: 'owner@example.com' }, itunesImage: 'not-a-url', itunesExplicit: false, podcastGuid: 'test-validation-guid-002', }); } catch (error) { errorThrown = true; expect(error.message).toInclude('Invalid or relative URL'); } expect(errorThrown).toEqual(true); }); tap.test('should validate iTunes type', async () => { let errorThrown = false; try { testSmartFeed.createPodcastFeed({ domain: 'test.com', title: 'Test', description: 'Test', category: 'Test', company: 'Test Inc', companyEmail: 'test@example.com', companyDomain: 'https://example.com', itunesCategory: 'Technology', itunesAuthor: 'Author', itunesOwner: { name: 'Owner', email: 'owner@example.com' }, itunesImage: 'https://example.com/image.jpg', itunesExplicit: false, podcastGuid: 'test-guid-auto', itunesType: 'invalid' as any, }); } catch (error) { errorThrown = true; expect(error.message).toInclude('must be either "episodic" or "serial"'); } expect(errorThrown).toEqual(true); }); tap.test('should validate episode audio URL', async () => { const podcast = testSmartFeed.createPodcastFeed({ domain: 'test.com', title: 'Test Podcast', description: 'Test', category: 'Test', company: 'Test Inc', companyEmail: 'test@example.com', companyDomain: 'https://example.com', itunesCategory: 'Technology', itunesAuthor: 'Author', itunesOwner: { name: 'Owner', email: 'owner@example.com' }, itunesImage: 'https://example.com/image.jpg', itunesExplicit: false, podcastGuid: 'test-guid-auto', }); let errorThrown = false; try { podcast.addEpisode({ title: 'Episode 1', authorName: 'Author', imageUrl: 'https://example.com/episode.jpg', timestamp: Date.now(), url: 'https://example.com/episode/1', content: 'Content', audioUrl: 'not-a-url', audioType: 'audio/mpeg', audioLength: 1000000, itunesDuration: 600, }); } catch (error) { errorThrown = true; expect(error.message).toInclude('Invalid or relative URL'); } expect(errorThrown).toEqual(true); }); tap.test('should validate audio type', async () => { const podcast = testSmartFeed.createPodcastFeed({ domain: 'test.com', title: 'Test Podcast', description: 'Test', category: 'Test', company: 'Test Inc', companyEmail: 'test@example.com', companyDomain: 'https://example.com', itunesCategory: 'Technology', itunesAuthor: 'Author', itunesOwner: { name: 'Owner', email: 'owner@example.com' }, itunesImage: 'https://example.com/image.jpg', itunesExplicit: false, podcastGuid: 'test-guid-auto', }); let errorThrown = false; try { podcast.addEpisode({ title: 'Episode 1', authorName: 'Author', imageUrl: 'https://example.com/episode.jpg', timestamp: Date.now(), url: 'https://example.com/episode/1', content: 'Content', audioUrl: 'https://example.com/audio.mp3', audioType: 'video/mp4', // Wrong type! audioLength: 1000000, itunesDuration: 600, }); } catch (error) { errorThrown = true; expect(error.message).toInclude('Invalid audio type'); expect(error.message).toInclude('Must start with \'audio/\''); } expect(errorThrown).toEqual(true); }); tap.test('should validate audio length', async () => { const podcast = testSmartFeed.createPodcastFeed({ domain: 'test.com', title: 'Test Podcast', description: 'Test', category: 'Test', company: 'Test Inc', companyEmail: 'test@example.com', companyDomain: 'https://example.com', itunesCategory: 'Technology', itunesAuthor: 'Author', itunesOwner: { name: 'Owner', email: 'owner@example.com' }, itunesImage: 'https://example.com/image.jpg', itunesExplicit: false, podcastGuid: 'test-guid-auto', }); let errorThrown = false; try { podcast.addEpisode({ title: 'Episode 1', authorName: 'Author', imageUrl: 'https://example.com/episode.jpg', timestamp: Date.now(), url: 'https://example.com/episode/1', content: 'Content', audioUrl: 'https://example.com/audio.mp3', audioType: 'audio/mpeg', audioLength: -100, // Invalid! itunesDuration: 600, }); } catch (error) { errorThrown = true; expect(error.message).toInclude('must be a positive number'); } expect(errorThrown).toEqual(true); }); tap.test('should validate duration', async () => { const podcast = testSmartFeed.createPodcastFeed({ domain: 'test.com', title: 'Test Podcast', description: 'Test', category: 'Test', company: 'Test Inc', companyEmail: 'test@example.com', companyDomain: 'https://example.com', itunesCategory: 'Technology', itunesAuthor: 'Author', itunesOwner: { name: 'Owner', email: 'owner@example.com' }, itunesImage: 'https://example.com/image.jpg', itunesExplicit: false, podcastGuid: 'test-guid-auto', }); let errorThrown = false; try { podcast.addEpisode({ title: 'Episode 1', authorName: 'Author', imageUrl: 'https://example.com/episode.jpg', timestamp: Date.now(), url: 'https://example.com/episode/1', content: 'Content', audioUrl: 'https://example.com/audio.mp3', audioType: 'audio/mpeg', audioLength: 1000000, itunesDuration: 0, // Invalid! }); } catch (error) { errorThrown = true; expect(error.message).toInclude('duration must be a positive number'); } expect(errorThrown).toEqual(true); }); tap.test('should validate episode type', async () => { const podcast = testSmartFeed.createPodcastFeed({ domain: 'test.com', title: 'Test Podcast', description: 'Test', category: 'Test', company: 'Test Inc', companyEmail: 'test@example.com', companyDomain: 'https://example.com', itunesCategory: 'Technology', itunesAuthor: 'Author', itunesOwner: { name: 'Owner', email: 'owner@example.com' }, itunesImage: 'https://example.com/image.jpg', itunesExplicit: false, podcastGuid: 'test-guid-auto', }); let errorThrown = false; try { podcast.addEpisode({ title: 'Episode 1', authorName: 'Author', imageUrl: 'https://example.com/episode.jpg', timestamp: Date.now(), url: 'https://example.com/episode/1', content: 'Content', audioUrl: 'https://example.com/audio.mp3', audioType: 'audio/mpeg', audioLength: 1000000, itunesDuration: 600, itunesEpisodeType: 'invalid' as any, }); } catch (error) { errorThrown = true; expect(error.message).toInclude('must be "full", "trailer", or "bonus"'); } expect(errorThrown).toEqual(true); }); tap.test('should validate episode number', async () => { const podcast = testSmartFeed.createPodcastFeed({ domain: 'test.com', title: 'Test Podcast', description: 'Test', category: 'Test', company: 'Test Inc', companyEmail: 'test@example.com', companyDomain: 'https://example.com', itunesCategory: 'Technology', itunesAuthor: 'Author', itunesOwner: { name: 'Owner', email: 'owner@example.com' }, itunesImage: 'https://example.com/image.jpg', itunesExplicit: false, podcastGuid: 'test-guid-auto', }); let errorThrown = false; try { podcast.addEpisode({ title: 'Episode 1', authorName: 'Author', imageUrl: 'https://example.com/episode.jpg', timestamp: Date.now(), url: 'https://example.com/episode/1', content: 'Content', audioUrl: 'https://example.com/audio.mp3', audioType: 'audio/mpeg', audioLength: 1000000, itunesDuration: 600, itunesEpisode: 0, // Invalid! }); } catch (error) { errorThrown = true; expect(error.message).toInclude('episode number must be a positive integer'); } expect(errorThrown).toEqual(true); }); tap.test('should validate season number', async () => { const podcast = testSmartFeed.createPodcastFeed({ domain: 'test.com', title: 'Test Podcast', description: 'Test', category: 'Test', company: 'Test Inc', companyEmail: 'test@example.com', companyDomain: 'https://example.com', itunesCategory: 'Technology', itunesAuthor: 'Author', itunesOwner: { name: 'Owner', email: 'owner@example.com' }, itunesImage: 'https://example.com/image.jpg', itunesExplicit: false, podcastGuid: 'test-guid-auto', }); let errorThrown = false; try { podcast.addEpisode({ title: 'Episode 1', authorName: 'Author', imageUrl: 'https://example.com/episode.jpg', timestamp: Date.now(), url: 'https://example.com/episode/1', content: 'Content', audioUrl: 'https://example.com/audio.mp3', audioType: 'audio/mpeg', audioLength: 1000000, itunesDuration: 600, itunesSeason: -1, // Invalid! }); } catch (error) { errorThrown = true; expect(error.message).toInclude('season number must be a positive integer'); } expect(errorThrown).toEqual(true); }); tap.test('should validate duplicate episode IDs', async () => { const podcast = testSmartFeed.createPodcastFeed({ domain: 'test.com', title: 'Test Podcast', description: 'Test', category: 'Test', company: 'Test Inc', companyEmail: 'test@example.com', companyDomain: 'https://example.com', itunesCategory: 'Technology', itunesAuthor: 'Author', itunesOwner: { name: 'Owner', email: 'owner@example.com' }, itunesImage: 'https://example.com/image.jpg', itunesExplicit: false, podcastGuid: 'test-guid-auto', }); const episodeData = { title: 'Episode 1', authorName: 'Author', imageUrl: 'https://example.com/episode.jpg', timestamp: Date.now(), url: 'https://example.com/episode/1', content: 'Content', audioUrl: 'https://example.com/audio.mp3', audioType: 'audio/mpeg', audioLength: 1000000, itunesDuration: 600, }; podcast.addEpisode(episodeData); let errorThrown = false; try { podcast.addEpisode(episodeData); } catch (error) { errorThrown = true; expect(error.message).toInclude('Duplicate episode ID'); } expect(errorThrown).toEqual(true); }); export default tap.start();