#!/usr/bin/env node const tape = require('tape') const request = require('supertest') const AddonClient = require('stremio-addon-client') const { addonBuilder, serveHTTP, getRouter, publishToCentral } = require('../') const PORT = 5000 let addonUrl let addonServer const manifest = { id: 'org.myexampleaddon', version: '1.0.0', description: 'not so simple', name: 'simple example', logo: `http://localhost:${PORT}/static/imgs/logo.png`, background: `http://localhost:${PORT}/static/imgs/background.jpg`, resources: ['stream'], types: ['movie'], catalogs: [{ type: 'movie', id: 'test' }], } tape('try to create an addon with an invalid manifest', function(t) { try { new addonBuilder(null) } catch(e) { t.ok(e.message, 'invalid manifest') t.end() } }) tape('try to create an addon with an invalid manifest: linter', function(t) { try { new addonBuilder({ name: 'something' }) } catch(e) { t.equal(e.message, 'manifest.id must be a string') t.end() } }) tape('try to create an addon with an unspecified resource', function(t) { try { new addonBuilder(manifest).defineMetaHandler(function() { }).getInterface() } catch(e) { t.equal(e.message, 'manifest.resources does not contain: meta') t.end() } }) tape('create an addon and get the router', function(t) { // getRouter requires all handlers to be defined var addon = new addonBuilder(manifest) .defineCatalogHandler(() => Promise.resolve()) .defineStreamHandler(() => Promise.resolve()) t.ok(getRouter(addon.getInterface()), 'can get router') t.end() }) tape('create an addon and expose on HTTP with serveHTTP()', function(t) { const builder = new addonBuilder(manifest) .defineCatalogHandler(() => Promise.resolve({ metas: [] })) // NOTE: we're not supposed to mirror back the `args`, but we're doing it for easier testing .defineStreamHandler((args) => Promise.resolve({ streams: [], args })) serveHTTP(builder.getInterface(), { port: PORT, cacheMaxAge: 3600 }).then(function(h) { t.ok(h.url, 'has url') t.ok(h.url.endsWith('manifest.json'), 'url ends with manifest.json') t.ok(h.server, 'has h.server') addonUrl = h.url addonServer = h.server request(addonServer) .get('/manifest.json') .expect(200) .end((err, res) => { t.error(err, 'request error') t.error(res.error, 'response error') t.equal(res.ok, true, 'has response status 200') t.equal(res.status, 200, 'has response status ok') t.equal(res.headers['cache-control'], 'max-age=3600, public', 'cache headers are correct') t.end() }) }) }) tape('try to serve a directory that does not exist', function (t) { var addon = new addonBuilder(manifest) .defineCatalogHandler(() => Promise.resolve()) .defineStreamHandler(() => Promise.resolve()) try { serveHTTP(addon.getInterface(), { static: '/notexist' }) } catch (e) { t.equal(e.message, 'directory to serve does not exist') t.end() } }) tape('try to serve a directory', function (t) { var addon = new addonBuilder(manifest) .defineCatalogHandler(() => Promise.resolve()) .defineStreamHandler(() => Promise.resolve()) serveHTTP(addon.getInterface(), { static: '/docs' }).then(function (h) { request(h.server) .get('/docs/README.md') .expect(200) .end((err, res) => { h.server.close() t.error(err, 'request error') t.error(res.error, 'response error') t.equal(res.ok, true, 'has response status 200') t.equal(res.status, 200, 'has response status ok') t.equal(res.type, 'text/markdown', 'is a valid markdown document') t.end() }) }) }) // Test the homepage of the addon tape('should return a valid html document', function (t) { request(addonServer) .get('/') .expect(200) .end((err, res) => { t.error(err, 'request error') t.error(res.error, 'response error') t.equal(res.ok, true, 'has response status ok') t.equal(res.status, 200, 'has response status 200') t.notEqual(res.text, undefined, 'is not undefined') t.equal(res.type, 'text/html', 'is a valid html document') t.end() }) }) tape('initialize an addon client for the addon', function(t) { AddonClient.detectFromURL(addonUrl) .then(function(resp) { t.ok(resp.addon, 'has addon') t.ok(resp.addon.manifest, 'has manifest') // NOTE: this is an important design principle - immutability on the manifest object t.deepEquals(resp.addon.manifest, manifest, 'addon.manifest is immutable') const addonClient = resp.addon return addonClient.get('stream', 'channel', '11') .then(function(resp) { t.ok(resp.streams, 'has streams') t.deepEqual(resp.args, { type: 'channel', id: '11', extra: {}, config: {} }, 'args parsed right') return addonClient.get('stream', 'channel', '11', { search: 'foobar' }) }) .then(function(resp) { t.ok(resp.streams, 'has streams') t.deepEqual(resp.args, { type: 'channel', id: '11', extra: { search: 'foobar' }, config: {} }, 'args parsed right') }) }) .then(() => t.end()) .catch(function(err) { t.error(err, 'error on addonClient') t.end() }) }) tape('getInterface: define a stream handler on the addon and test it', function(t) { const addon = new addonBuilder(manifest) .defineCatalogHandler(() => Promise.resolve({ metas: [] })) .defineStreamHandler(function(args) { t.equals(args.type, 'channel', 'args.type is right') t.equals(args.id, '11', 'args.id is right') t.deepEquals(args.extra, {}, 'args.extra is empty') return Promise.resolve({streams:[]}) }) const addonInterface = addon.getInterface() t.ok(addonInterface.manifest, 'interface has manifest') addonInterface.manifest.name += 'foobar' t.equal(addonInterface.manifest.name, manifest.name, 'interface.manifest is immutable') addonInterface.get('stream', 'channel', '11') .then(r => { t.ok(r.streams, 'response has streams') }) .then(() => t.end()) .catch(function(err) { t.error(err, 'error on addonClient stream request') t.end() }) }) tape('defining the same handler throws', function(t) { const addon = new addonBuilder(manifest) .defineCatalogHandler(() => Promise.resolve({ metas: [] })) .defineStreamHandler(() => Promise.resolve({ streams: [] })) try { addon.defineStreamHandler(() => Promise.resolve()) } catch(e) { t.ok(e, 'has exception') t.equal(e.message, 'handler for stream already defined') t.end() } }) // publishToCentral publishes to the API tape('publishToCentral', function(t) { publishToCentral('https://cinemeta.strem.io/manifest.json') .then(function(resp) { t.equal(resp.success, true, 'can announce') t.end() }) .catch(function(err) { t.error(err) t.end() }) }) tape.onFinish(function() { // cause the server is still listening addonServer.close() })