419 lines
12 KiB
TypeScript
419 lines
12 KiB
TypeScript
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();
|