const { caps, feed } = require('../src/torznab'); describe('caps()', () => { let result; beforeAll(() => { result = caps(); }); it('returns a string', () => expect(typeof result).toBe('string')); it('starts with XML declaration', () => expect(result).toMatch(/^<\?xml/)); it('contains caps element', () => expect(result).toContain('')); it('advertises tv-search', () => expect(result).toContain('tv-search')); it('advertises movie-search', () => expect(result).toContain('movie-search')); it('has Movies category id 2000', () => expect(result).toContain('id="2000"')); it('has TV category id 5000', () => expect(result).toContain('id="5000"')); it('marks registration as not available', () => expect(result).toContain('available="no"')); }); describe('feed()', () => { const baseUrl = 'http://localhost:3001'; it('returns valid XML with no items', () => { const result = feed([], baseUrl); expect(result).toMatch(/^<\?xml/); expect(result).toContain(''); expect(result).not.toContain(''); }); it('includes item title', () => { const result = feed([{ name: 'Show.S01E01.mkv', ident: 'abc', size: 1000, votes: 5 }], baseUrl); expect(result).toContain('Show.S01E01.mkv'); }); it('includes correct download URL in enclosure', () => { const result = feed([{ name: 'f.mkv', ident: 'xyz789', size: 500, votes: 0 }], baseUrl); expect(result).toContain(`${baseUrl}/download/xyz789`); }); it('includes file size in enclosure', () => { const result = feed([{ name: 'f.mkv', ident: 'id1', size: 123456, votes: 0 }], baseUrl); expect(result).toContain('length="123456"'); }); it('escapes & in title', () => { const result = feed([{ name: 'Tom & Jerry.mkv', ident: 'id', size: 0, votes: 0 }], baseUrl); expect(result).toContain('Tom & Jerry.mkv'); expect(result).not.toContain('Tom & Jerry'); }); it('escapes < and > in name', () => { const result = feed([{ name: '.mkv', ident: 'id', size: 0, votes: 0 }], baseUrl); expect(result).toContain('<bad>.mkv'); }); it('escapes " in name', () => { const result = feed([{ name: '"quoted".mkv', ident: 'id', size: 0, votes: 0 }], baseUrl); expect(result).toContain('"quoted".mkv'); }); it('includes torznab:attr for category 5040', () => { const result = feed([{ name: 'f.mkv', ident: 'id', size: 0, votes: 0 }], baseUrl); expect(result).toContain('name="category" value="5040"'); }); it('handles multiple items', () => { const items = [ { name: 'ep1.mkv', ident: 'a', size: 100, votes: 1 }, { name: 'ep2.mkv', ident: 'b', size: 200, votes: 2 }, ]; const result = feed(items, baseUrl); expect(result).toContain('ep1.mkv'); expect(result).toContain('ep2.mkv'); expect((result.match(//g) || []).length).toBe(2); }); });