diff --git a/test/camo.js b/test/camo.js deleted file mode 100644 index 48bf1d02..00000000 --- a/test/camo.js +++ /dev/null @@ -1,91 +0,0 @@ -const assert = require('assert'); -const Camo = require('../lib/camo'); -const CamoConfig = require('../lib/configuration/camoconfig').CamoConfig; - -describe('Camo', () => { - const config = new CamoConfig({ - camo: { - server: 'http://localhost:8081', - key: '9LKC7708ZHOVRCTLOLE3G2YJ0U1T8F96', - 'whitelisted-domains': ['def.xyz', 'tii.kzz.qqq'], - encoding: 'hex' - } - }); - - describe('#camoify', () => { - it('constructs a camo url', () => { - const result = Camo.camoify(config, 'http://abc.xyz/image.jpeg'); - assert.strictEqual(result, 'http://localhost:8081/a9c295dd7d8dcbc8247dec97ac5d9b4ee8baeb31/687474703a2f2f6162632e78797a2f696d6167652e6a706567'); - }); - - it('constructs a camo url using url encoding', () => { - const config = new CamoConfig({ - camo: { - server: 'http://localhost:8081', - key: '9LKC7708ZHOVRCTLOLE3G2YJ0U1T8F96', - 'whitelisted-domains': ['def.xyz'], - encoding: 'url' - } - }); - - const result = Camo.camoify(config, 'http://abc.xyz/image.jpeg'); - assert.strictEqual(result, 'http://localhost:8081/a9c295dd7d8dcbc8247dec97ac5d9b4ee8baeb31?url=http%3A%2F%2Fabc.xyz%2Fimage.jpeg'); - }); - - it('bypasses camo for whitelisted domains', () => { - const result = Camo.camoify(config, 'http://def.xyz/image.jpeg'); - assert.strictEqual(result, 'https://def.xyz/image.jpeg'); - }); - - it('bypasses camo for whitelisted domains subdomains', () => { - const result = Camo.camoify(config, 'http://abc.def.xyz/image.jpeg'); - assert.strictEqual(result, 'https://abc.def.xyz/image.jpeg'); - }); - - it('does not bypass camo for a non-subdomain match', () => { - const result = Camo.camoify(config, 'http://abcdef.xyz/image.jpeg'); - assert.strictEqual(result, 'http://localhost:8081/19f53f65e8081a064cff54fbd665e8bb08612aa6/687474703a2f2f6162636465662e78797a2f696d6167652e6a706567'); - }); - - it('does not bypass camo when no whitelist is configured', () => { - const config = new CamoConfig({ - camo: { - server: 'http://localhost:8081', - key: '9LKC7708ZHOVRCTLOLE3G2YJ0U1T8F96', - encoding: 'hex' - } - }); - const result = Camo.camoify(config, 'http://abcdef.xyz/image.jpeg'); - assert.strictEqual(result, 'http://localhost:8081/19f53f65e8081a064cff54fbd665e8bb08612aa6/687474703a2f2f6162636465662e78797a2f696d6167652e6a706567'); - }); - }); - - describe('#transformImgTags', () => { - it('transforms an img tag with a src', () => { - const attribs = { - src: 'http://abc.xyz/image.jpeg', - 'class': 'some-image' - }; - const expectedAttribs = { - src: 'http://localhost:8081/a9c295dd7d8dcbc8247dec97ac5d9b4ee8baeb31/687474703a2f2f6162632e78797a2f696d6167652e6a706567', - 'class': 'some-image' - }; - const result = Camo.transformImgTags(config, 'img', attribs); - assert.deepStrictEqual(result, { tagName: 'img', attribs: expectedAttribs }); - }); - - it('skips img tags with no src', () => { - const attribs = { 'class': 'some-image' }; - const result = Camo.transformImgTags(config, 'img', attribs); - assert.deepStrictEqual(result, { tagName: 'img', attribs: attribs }); - }); - - it('fails gracefully', () => { - const attribs = { src: 'http://abc.xyz/image.jpeg' }; - const config = new CamoConfig({ camo: { enabled: true }}); - config.getKey = () => { throw new Error('something happened'); }; - const result = Camo.transformImgTags(config, 'img', attribs); - assert.deepStrictEqual(result, { tagName: 'img', attribs: attribs }); - }); - }); -}); diff --git a/test/channel/playlist.js b/test/channel/playlist.js deleted file mode 100644 index 12d383ea..00000000 --- a/test/channel/playlist.js +++ /dev/null @@ -1,53 +0,0 @@ -const PlaylistModule = require('../../lib/channel/playlist'); -const assert = require('assert'); -const Config = require('../../lib/config'); - -describe('PlaylistModule', () => { - describe('#handleClean', () => { - let fakeChannel = { - uniqueName: 'testChannel', - logger: { - log() { - - } - }, - broadcastToRoom() { - }, - broadcastAll() { - }, - modules: { - permissions: { - canDeleteVideo() { - return true; - } - } - } - }; - let fakeUser = { - getName() { - return 'testUser'; - }, - socket: { - emit() { - } - } - }; - let playlistModule = new PlaylistModule(fakeChannel); - - it('rejects invalid regexes', () => { - let sentError = false; - - fakeUser.socket.emit = (event, payload) => { - assert.strictEqual(event, 'errorMsg'); - assert.deepStrictEqual(payload, { - msg: "Invalid target: -i * -m" - }); - sentError = true; - }; - - playlistModule.handleClean(fakeUser, "/clean -i * -m", {}); - - assert(sentError, 'Expected error due to invalid regex'); - }); - }); -}); diff --git a/test/channel/poll.js b/test/channel/poll.js deleted file mode 100644 index 8e3c46dd..00000000 --- a/test/channel/poll.js +++ /dev/null @@ -1,221 +0,0 @@ -const PollModule = require('../../lib/channel/poll'); -const assert = require('assert'); -const Config = require('../../lib/config'); - -describe('PollModule', () => { - describe('#validatePollInput', () => { - let pollModule = new PollModule({ uniqueName: 'testChannel', modules: {} }); - - it('accepts valid input', () => { - let title = ''; - for (let i = 0; i < 20; i++) { - title += 'x'; - } - - pollModule.validatePollInput(title, ['ab', 'cd']); - }); - - it('rejects non-string titles', () => { - assert.throws(() => { - pollModule.validatePollInput(null, []); - }, /title/); - }); - - it('rejects invalidly long titles', () => { - let title = ''; - for (let i = 0; i < 256; i++) { - title += 'x'; - } - - assert.throws(() => { - pollModule.validatePollInput(title, []); - }, /title/); - }); - - it('rejects non-array option parameter', () => { - assert.throws(() => { - pollModule.validatePollInput('poll', 1234); - }, /options/); - }); - - it('rejects too many options', () => { - const limit = Config.get('poll.max-options'); - Config.set('poll.max-options', 2); - try { - assert.throws(() => { - pollModule.validatePollInput('poll', ['1', '2', '3', '4']); - }, /maximum of 2 options/); - } finally { - Config.set('poll.max-options', limit); - } - }); - - it('rejects non-string options', () => { - assert.throws(() => { - pollModule.validatePollInput('poll', [null]); - }, /options must be strings/); - }); - - it('rejects invalidly long options', () => { - let option = ''; - for (let i = 0; i < 256; i++) { - option += 'x'; - } - - assert.throws(() => { - pollModule.validatePollInput('poll', [option]); - }, /options must be 1-255 characters/); - }); - }); - - describe('#handleNewPoll', () => { - let fakeChannel = { - uniqueName: 'testChannel', - logger: { - log() { - - } - }, - broadcastToRoom() { - }, - broadcastAll() { - }, - modules: { - permissions: { - canControlPoll() { - return true; - } - } - } - }; - let fakeUser = { - getName() { - return 'testUser'; - }, - socket: { - emit() { - } - } - }; - let pollModule; - beforeEach(() => { - pollModule = new PollModule(fakeChannel); - }); - - it('creates a valid poll', () => { - let sentNewPoll = false; - let sentClosePoll = false; - fakeChannel.broadcastToRoom = (event, data, room) => { - if (room === 'testChannel:viewHidden' && event === 'newPoll') { - sentNewPoll = true; - } - }; - fakeChannel.broadcastAll = (event) => { - if (event === 'closePoll') { - sentClosePoll = true; - } - }; - pollModule.handleNewPoll(fakeUser, { - title: 'test poll', - opts: [ - 'option 1', - 'option 2' - ], - obscured: false - }, (ackResult) => { - assert(!ackResult.error, `Unexpected error: ${ackResult.error}`); - }); - assert(!sentClosePoll, 'Unexpected broadcast of closePoll event'); - assert(sentNewPoll, 'Expected broadcast of newPoll event'); - }); - - it('closes an existing poll when a new one is created', () => { - let sentNewPoll = 0; - let sentClosePoll = 0; - let sentUpdatePoll = 0; - fakeChannel.broadcastToRoom = (event, data, room) => { - if (room === 'testChannel:viewHidden' && event === 'newPoll') { - sentNewPoll++; - } - }; - fakeChannel.broadcastAll = (event, data) => { - if (event === 'closePoll') { - sentClosePoll++; - } else if (event === 'updatePoll') { - sentUpdatePoll++; - assert.deepStrictEqual(data.counts, [0, 0]); - } - }; - pollModule.handleNewPoll(fakeUser, { - title: 'test poll', - opts: [ - 'option 1', - 'option 2' - ], - obscured: true - }, (ackResult) => { - assert(!ackResult.error, `Unexpected error: ${ackResult.error}`); - }); - - pollModule.handleNewPoll(fakeUser, { - title: 'poll 2', - opts: [ - 'option 3', - 'option 4' - ], - obscured: false - }, (ackResult) => { - assert(!ackResult.error, `Unexpected error: ${ackResult.error}`); - }); - - assert.strictEqual(sentClosePoll, 1, 'Expected 1 broadcast of closePoll event'); - assert.strictEqual(sentUpdatePoll, 1, 'Expected 1 broadcast of updatePoll event'); - assert.strictEqual(sentNewPoll, 2, 'Expected 2 broadcasts of newPoll event'); - }); - - it('rejects an invalid poll', () => { - fakeChannel.broadcastToRoom = (event, data, room) => { - assert(false, 'Expected no events to be sent'); - }; - fakeChannel.broadcastAll = (event) => { - assert(false, 'Expected no events to be sent'); - }; - const options = []; - for (let i = 0; i < 200; i++) { - options.push('option ' + i); - } - pollModule.handleNewPoll(fakeUser, { - title: 'test poll', - opts: options, - obscured: false - }, (ackResult) => { - assert.equal(ackResult.error.message, 'Polls are limited to a maximum of 50 options.'); - }); - }); - - it('handles a rejection with no ack provided by socket.io', () => { - fakeChannel.broadcastToRoom = (event, data, room) => { - assert(false, 'Expected no events to be sent'); - }; - fakeChannel.broadcastAll = (event) => { - assert(false, 'Expected no events to be sent'); - }; - let sentErrorMsg = false; - fakeUser.socket.emit = (event, data) => { - if (event === 'errorMsg') { - sentErrorMsg = true; - } - }; - const options = []; - for (let i = 0; i < 200; i++) { - options.push('option ' + i); - } - pollModule.handleNewPoll(fakeUser, { - title: 'test poll', - opts: options, - obscured: false - }); - assert(sentErrorMsg, 'Expected to send errorMsg since ack was missing'); - }); - }) -}); diff --git a/test/channel/voteskip.js b/test/channel/voteskip.js deleted file mode 100644 index 4dca2813..00000000 --- a/test/channel/voteskip.js +++ /dev/null @@ -1,138 +0,0 @@ -const VoteskipModule = require('../../lib/channel/voteskip'); -const assert = require('assert'); -const Flags = require('../../lib/flags'); - -describe('VoteskipModule', () => { - let fakeUser; - let fakeChannel; - let voteskipModule; - - beforeEach(() => { - fakeUser = { - socket: { - emit() { - - } - }, - is() { - return false - } - }; - fakeChannel = { - logger: { - log() { - - } - }, - modules: { - permissions: { - canSeeVoteskipResults() { - return true; - }, - canVoteskip() { - return true; - } - }, - options: { - get(key) { - if (key === 'voteskip_ratio') { - return 0.5; - } else if (key === 'allow_voteskip') { - return true; - } - } - }, - playlist: { - _playNext() { - }, - - meta: { - count: 1 - } - } - }, - users: [fakeUser], - broadcastAll() { - } - }; - - voteskipModule = new VoteskipModule(fakeChannel); - }); - - describe('#update', () => { - it('resets the vote before changing to the next video', () => { - let reset = false, playNext = false; - fakeChannel.modules.playlist._playNext = () => { - if (!reset) { - assert(false, 'Expected voteskip reset prior to playlist._playNext'); - } - - playNext = true; - }; - fakeUser.socket.emit = (event, data) => { - if (event === 'voteskip') { - assert.deepEqual(data, { count: 0, need: 0 }); - reset = true; - } - }; - - voteskipModule.poll = { - toUpdateFrame() { - return { counts: [1] }; - } - }; - voteskipModule.update(); - assert.equal(voteskipModule.poll, false, 'Expected voteskip poll to be reset to false'); - assert(reset, 'Expected voteskip to be reset'); - assert(playNext, 'Expected playlist to be advanced'); - }); - - it('broadcasts a message', () => { - let sentMessage = false; - fakeChannel.broadcastAll = (frame, data) => { - assert.strictEqual(frame, 'chatMsg'); - assert(/voteskip passed/i.test(data.msg), 'Expected voteskip passed message') - sentMessage = true; - }; - voteskipModule.poll = { - toUpdateFrame() { - return { counts: [1] }; - } - }; - voteskipModule.update(); - assert(sentMessage, 'Expected voteskip passed message'); - }); - }); - - describe('#calcUsercounts', () => { - it('calculates correctly', () => { - fakeChannel.users = [ - // 1 with permission and not AFK - { is(f) { return false; }, _has_permission: true }, - // 1 without permission and not AFK - { is(f) { return false; }, _has_permission: false }, - // 1 afk with permission - { is(f) { return f === Flags.U_AFK; }, _has_permission: true }, - // 1 afk without permission - { is(f) { return f === Flags.U_AFK; }, _has_permission: false } - ] - - fakeChannel.modules.permissions.canVoteskip = u => u._has_permission; - - const { - total, - eligible, - afk, - noPermission - } = voteskipModule.calcUsercounts(); - - assert.strictEqual(total, 4, 'mismatch: total'); - assert.strictEqual(eligible, 1, 'mismatch: eligible'); - // Permission is checked before AFK; if user is AFK and also does - // not have permission, they should be counted in noPermission - // but not afk - assert.strictEqual(afk, 1, 'mismatch: afk'); - assert.strictEqual(noPermission, 2, 'mismatch: noPermission'); - }); - }); -}); diff --git a/test/configuration/camoconfig.js b/test/configuration/camoconfig.js deleted file mode 100644 index b676d1cb..00000000 --- a/test/configuration/camoconfig.js +++ /dev/null @@ -1,56 +0,0 @@ -const assert = require('assert'); -const CamoConfig = require('../../lib/configuration/camoconfig').CamoConfig; - -describe('CamoConfig', () => { - describe('#constructor', () => { - it('strips trailing slashes from the server', () => { - const config = new CamoConfig({ - camo: { - server: 'http://abc.xyz/' - } - }); - assert.strictEqual(config.getServer(), 'http://abc.xyz'); - }); - - it('defaults to enabled=false', () => { - assert.strictEqual(new CamoConfig().isEnabled(), false); - }); - - it('validates that encoding must be either url or hex', () => { - - assert.throws(() => { - new CamoConfig({ - camo: { - encoding: 'asdjfnasdf' - } - }); - }, /must be either 'url' or 'hex'/); - }); - }); - - describe('#getWhitelistedDomains', () => { - it('defaults to an empty array', () => { - assert.deepStrictEqual(new CamoConfig().getWhitelistedDomains(), []); - }); - }); - - describe('#getEncoding', () => { - it('defaults to url', () => { - assert.deepStrictEqual(new CamoConfig().getEncoding(), 'url'); - }); - }); - - describe('#getWhitelistedDomainsRegexp', () => { - it('generates a regex based on the whitelisted domains', () => { - const config = new CamoConfig({ - camo: { - server: 'localhost:8081', - 'whitelisted-domains': ['abc.xyz', 'tii.kzz.qqq'] - } - }); - - const re = config.getWhitelistedDomainsRegexp(); - assert.deepStrictEqual(re, /\.abc\.xyz$|\.tii\.kzz\.qqq$/i); - }); - }); -}); diff --git a/test/configuration/prometheusconfig.js b/test/configuration/prometheusconfig.js deleted file mode 100644 index cd9a9139..00000000 --- a/test/configuration/prometheusconfig.js +++ /dev/null @@ -1,10 +0,0 @@ -const assert = require('assert'); -const PrometheusConfig = require('../../lib/configuration/prometheusconfig').PrometheusConfig; - -describe('PrometheusConfig', () => { - describe('#constructor', () => { - it('defaults to enabled=false', () => { - assert.strictEqual(new PrometheusConfig().isEnabled(), false); - }); - }); -}); diff --git a/test/controller/email.js b/test/controller/email.js deleted file mode 100644 index 1f9ad494..00000000 --- a/test/controller/email.js +++ /dev/null @@ -1,50 +0,0 @@ -const assert = require('assert'); -const { createTransport } = require('nodemailer'); -const { EmailController } = require('../../lib/controller/email'); -const { EmailConfig } = require('../../lib/configuration/emailconfig'); - -describe('EmailController', () => { - describe('sendPasswordReset', () => { - it('sends a password reset email', () => { - const mailer = createTransport({ - jsonTransport: true - }); - const config = new EmailConfig({ - 'password-reset': { - from: 'Test ', - subject: 'Password Reset', - 'html-template': 'Reset here $user$', - 'text-template': 'Text is better than HTML $user$ $url$' - } - }); - - const controller = new EmailController(mailer, config); - - return controller.sendPasswordReset({ - address: 'some-user@example.com', - username: 'SomeUser', - url: 'http://localhost/password-reset/blah' - }).then(info => { - const sentMessage = JSON.parse(info.message); - - assert.strictEqual(sentMessage.subject, 'Password Reset'); - assert.deepStrictEqual( - sentMessage.from, - { name: 'Test', address: 'test@example.com' } - ); - assert.deepStrictEqual( - sentMessage.to, - [{ name: 'SomeUser', address: 'some-user@example.com' }] - ); - assert.strictEqual( - sentMessage.html, - 'Reset here SomeUser' - ); - assert.strictEqual( - sentMessage.text, - 'Text is better than HTML SomeUser http://localhost/password-reset/blah' - ); - }); - }); - }); -}); diff --git a/test/custom-media.js b/test/custom-media.js deleted file mode 100644 index 4bbca818..00000000 --- a/test/custom-media.js +++ /dev/null @@ -1,435 +0,0 @@ -const assert = require('assert'); -const { validate, convert, lookup } = require('../lib/custom-media'); -const http = require('http'); - -describe('custom-media', () => { - let valid, invalid; - beforeEach(() => { - invalid = valid = { - title: 'Test Video', - duration: 10, - live: false, - thumbnail: 'https://example.com/thumb.jpg', - sources: [ - { - url: 'https://example.com/video.mp4', - contentType: 'video/mp4', - quality: 1080, - bitrate: 5000 - } - ], - textTracks: [ - { - url: 'https://example.com/subtitles.vtt', - contentType: 'text/vtt', - name: 'English Subtitles' - } - ] - }; - }); - - describe('#validate', () => { - it('accepts valid metadata', () => { - validate(valid); - }); - - it('accepts valid metadata with no optional params', () => { - delete valid.live; - delete valid.thumbnail; - delete valid.textTracks; - delete valid.sources[0].bitrate; - - validate(valid); - }); - - it('rejects missing title', () => { - delete invalid.title; - - assert.throws(() => validate(invalid), /title must be a string/); - }); - - it('rejects blank title', () => { - invalid.title = ''; - - assert.throws(() => validate(invalid), /title must not be blank/); - }); - - it('rejects non-numeric duration', () => { - invalid.duration = 'twenty four seconds'; - - assert.throws(() => validate(invalid), /duration must be a number/); - }); - - it('rejects non-finite duration', () => { - invalid.duration = NaN; - - assert.throws(() => validate(invalid), /duration must be a non-negative finite number/); - }); - - it('rejects negative duration', () => { - invalid.duration = -1; - - assert.throws(() => validate(invalid), /duration must be a non-negative finite number/); - }); - - it('rejects non-boolean live', () => { - invalid.live = 'false'; - - assert.throws(() => validate(invalid), /live must be a boolean/); - }); - - it('rejects non-string thumbnail', () => { - invalid.thumbnail = 1234; - - assert.throws(() => validate(invalid), /thumbnail must be a string/); - }); - - it('rejects invalid thumbnail URL', () => { - invalid.thumbnail = 'http://example.com/thumb.jpg'; - - assert.throws(() => validate(invalid), /URL protocol must be HTTPS/); - }); - - it('rejects non-live DASH', () => { - invalid.live = false; - invalid.sources[0].contentType = 'application/dash+xml'; - - assert.throws( - () => validate(invalid), - /contentType "application\/dash\+xml" requires live: true/ - ); - }); - }); - - describe('#validateSources', () => { - it('rejects non-array sources', () => { - invalid.sources = { a: 'b' }; - - assert.throws(() => validate(invalid), /sources must be a list/); - }); - - it('rejects empty source list', () => { - invalid.sources = []; - - assert.throws(() => validate(invalid), /source list must be nonempty/); - }); - - it('rejects non-string source url', () => { - invalid.sources[0].url = 1234; - - assert.throws(() => validate(invalid), /source URL must be a string/); - }); - - it('rejects invalid source URL', () => { - invalid.sources[0].url = 'http://example.com/thumb.jpg'; - - assert.throws(() => validate(invalid), /URL protocol must be HTTPS/); - }); - - it('rejects unacceptable source contentType', () => { - invalid.sources[0].contentType = 'rtmp/flv'; - - assert.throws(() => validate(invalid), /unacceptable source contentType/); - }); - - it('rejects unacceptable source quality', () => { - invalid.sources[0].quality = 144; - - assert.throws(() => validate(invalid), /unacceptable source quality/); - }); - - it('rejects non-numeric source bitrate', () => { - invalid.sources[0].bitrate = '1000kbps' - - assert.throws(() => validate(invalid), /source bitrate must be a number/); - }); - - it('rejects non-finite source bitrate', () => { - invalid.sources[0].bitrate = Infinity; - - assert.throws(() => validate(invalid), /source bitrate must be a non-negative finite number/); - }); - - it('rejects negative source bitrate', () => { - invalid.sources[0].bitrate = -1000; - - assert.throws(() => validate(invalid), /source bitrate must be a non-negative finite number/); - }); - }); - - describe('#validateTextTracks', () => { - it('rejects non-array text track list', () => { - invalid.textTracks = { a: 'b' }; - - assert.throws(() => validate(invalid), /textTracks must be a list/); - }); - - it('rejects non-string track url', () => { - invalid.textTracks[0].url = 1234; - - assert.throws(() => validate(invalid), /text track URL must be a string/); - }); - - it('rejects invalid track URL', () => { - invalid.textTracks[0].url = 'http://example.com/thumb.jpg'; - - assert.throws(() => validate(invalid), /URL protocol must be HTTPS/); - }); - - it('rejects unacceptable track contentType', () => { - invalid.textTracks[0].contentType = 'text/plain'; - - assert.throws(() => validate(invalid), /unacceptable text track contentType/); - }); - - it('rejects non-string track name', () => { - invalid.textTracks[0].name = 1234; - - assert.throws(() => validate(invalid), /text track name must be a string/); - }); - - it('rejects blank track name', () => { - invalid.textTracks[0].name = ''; - - assert.throws(() => validate(invalid), /text track name must be nonempty/); - }); - }); - - describe('#validateURL', () => { - it('rejects non-URLs', () => { - invalid.sources[0].url = 'not a url'; - - assert.throws(() => validate(invalid), /invalid URL/); - }); - - it('rejects non-https', () => { - invalid.sources[0].url = 'http://example.com/thumb.jpg'; - - assert.throws(() => validate(invalid), /URL protocol must be HTTPS/); - }); - - it('rejects IP addresses', () => { - invalid.sources[0].url = 'https://0.0.0.0/thumb.jpg'; - - assert.throws(() => validate(invalid), /URL hostname must be a domain name/); - }); - }); - - describe('#convert', () => { - let expected; - let id = 'testing'; - - beforeEach(() => { - expected = { - id: 'testing', - title: 'Test Video', - seconds: 10, - duration: '00:10', - type: 'cm', - meta: { - direct: { - 1080: [ - { - link: 'https://example.com/video.mp4', - contentType: 'video/mp4', - quality: 1080 - } - ] - }, - textTracks: [ - { - url: 'https://example.com/subtitles.vtt', - contentType: 'text/vtt', - name: 'English Subtitles' - } - ] - } - }; - }); - - function cleanForComparison(actual) { - actual = actual.pack(); - - // Strip out extraneous undefineds - for (let key in actual.meta) { - if (actual.meta[key] === undefined) delete actual.meta[key]; - } - - return actual; - } - - it('converts custom metadata to a CyTube Media object', () => { - const media = convert(id, valid); - const actual = cleanForComparison(media); - - assert.deepStrictEqual(actual, expected); - }); - - it('sets duration to 0 if live = true', () => { - valid.live = true; - expected.duration = '00:00'; - expected.seconds = 0; - - const media = convert(id, valid); - const actual = cleanForComparison(media); - - assert.deepStrictEqual(actual, expected); - }); - }); - - describe('#lookup', () => { - let server; - let serveFunc; - - beforeEach(() => { - serveFunc = function (req, res) { - res.writeHead(200, { 'Content-Type': 'application/json' }); - res.write(JSON.stringify(valid, null, 2)); - res.end(); - }; - - server = http.createServer((req, res) => serveFunc(req, res)); - server.listen(10111); - }); - - afterEach(done => { - server.close(() => done()); - }); - - it('retrieves metadata', () => { - function cleanForComparison(actual) { - actual = actual.pack(); - delete actual.id; - - // Strip out extraneous undefineds - for (let key in actual.meta) { - if (actual.meta[key] === undefined) delete actual.meta[key]; - } - - return actual; - } - - const expected = { - title: 'Test Video', - seconds: 10, - duration: '00:10', - type: 'cm', - meta: { - direct: { - 1080: [ - { - link: 'https://example.com/video.mp4', - contentType: 'video/mp4', - quality: 1080 - } - ] - }, - textTracks: [ - { - url: 'https://example.com/subtitles.vtt', - contentType: 'text/vtt', - name: 'English Subtitles' - } - ] - } - }; - - return lookup('http://127.0.0.1:10111/').then(result => { - assert.deepStrictEqual(cleanForComparison(result), expected); - }); - }); - - it('rejects the wrong content-type', () => { - serveFunc = (req, res) => { - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.write(JSON.stringify(valid, null, 2)); - res.end(); - }; - - return lookup('http://127.0.0.1:10111/').then(() => { - throw new Error('Expected failure due to wrong content-type'); - }).catch(error => { - assert.strictEqual( - error.message, - 'Expected content-type application/json, not text/plain' - ); - }); - }); - - it('rejects non-200 status codes', () => { - serveFunc = (req, res) => { - res.writeHead(404, { 'Content-Type': 'application/json' }); - res.write(JSON.stringify(valid, null, 2)); - res.end(); - }; - - return lookup('http://127.0.0.1:10111/').then(() => { - throw new Error('Expected failure due to 404'); - }).catch(error => { - assert.strictEqual( - error.message, - 'Expected HTTP 200 OK, not 404 Not Found' - ); - }); - }); - - it('rejects responses >100KB', () => { - serveFunc = (req, res) => { - res.writeHead(200, { 'Content-Type': 'application/json' }); - res.write(Buffer.alloc(200 * 1024)); - res.end(); - }; - - return lookup('http://127.0.0.1:10111/').then(() => { - throw new Error('Expected failure due to response size'); - }).catch(error => { - assert.strictEqual( - error.message, - 'Response size exceeds 100KB' - ); - }); - }); - - it('times out', () => { - serveFunc = (req, res) => { - res.writeHead(200, { 'Content-Type': 'application/json' }); - res.write(JSON.stringify(valid, null, 2)); - - setTimeout(() => res.end(), 100); - }; - - return lookup('http://127.0.0.1:10111/', { timeout: 1 }).then(() => { - throw new Error('Expected failure due to request timeout'); - }).catch(error => { - assert.strictEqual( - error.message, - 'Request timed out' - ); - assert.strictEqual(error.code, 'ETIMEDOUT'); - }); - }); - - it('rejects URLs with non-http(s) protocols', () => { - return lookup('ftp://127.0.0.1:10111/').then(() => { - throw new Error('Expected failure due to unacceptable URL protocol'); - }).catch(error => { - assert.strictEqual( - error.message, - 'Unacceptable protocol "ftp:". Custom metadata must be retrieved' - + ' by HTTP or HTTPS' - ); - }); - }); - - it('rejects invalid URLs', () => { - return lookup('not valid').then(() => { - throw new Error('Expected failure due to invalid URL'); - }).catch(error => { - assert.strictEqual( - error.message, - 'Invalid URL "not valid"' - ); - }); - }); - }); -}); diff --git a/test/customembed.js b/test/customembed.js deleted file mode 100644 index 2ca59ef0..00000000 --- a/test/customembed.js +++ /dev/null @@ -1,47 +0,0 @@ -const customembed = require('../lib/customembed'); -const assert = require('assert'); -const crypto = require("crypto"); - -function sha256(input) { - let hash = crypto.createHash('sha256'); - hash.update(input); - return hash.digest('base64'); -} - -describe('customembed', () => { - describe('#filter', () => { - it('rejects inputs', () => { - const input = ''; - assert.throws(() => { customembed.filter(input) }, /must be an '; - assert.throws(() => { customembed.filter(input) }, /must be HTTPS/); - }); - - it('accepts a valid iframe', () => { - let input = ''; - const expected = ''; - assert.strictEqual(XSS.sanitizeHTML(input), expected); - }); - }); -});