Files
hub/tools/librivox-test.js
T

44 lines
4.9 KiB
JavaScript

// Free audiobooks: LibriVox search and book details, chapter audio is only ever sent to the recording hosts, each person's shelf and listening position.
const http = require('http'), assert = require('assert'), fs = require('fs'), os = require('os'), path = require('path');
process.env.DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'nehub-lv-'));
const gif = Buffer.from('R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==', 'base64'), seen = []; let port;
const fake = http.createServer((req, res) => {
const u = new URL(req.url, 'http://x'), p = u.pathname; seen.push(p + u.search);
const J = (o, code = 200) => { res.writeHead(code, { 'content-type': 'application/json' }); res.end(JSON.stringify(o)); };
if (p === '/api/feed/audiobooks/') {
const book = { id: '55', title: 'Pride &amp; Prejudice', description: '<p>A novel</p>', url_librivox: 'https://librivox.org/pp', language: 'English', copyright_year: '1813', totaltimesecs: 600, authors: [{ first_name: 'Jane', last_name: 'Austen' }], coverart_jpg: `http://127.0.0.1:${port}/cover.gif` };
const full = { ...book, sections: [{ section_number: '1', title: 'Chapter 1', listen_url: `http://127.0.0.1:${port}/dl/1.mp3`, playtime: '300', readers: [{ display_name: 'Ann' }] }, { section_number: '2', title: 'Chapter 2', listen_url: 'https://evil.example.com/2.mp3', playtime: '300' }, { section_number: '3', title: 'Chapter 3', listen_url: `http://127.0.0.1:${port}/dl/3.mp3`, playtime: '300' }] };
if (u.searchParams.get('id') === '55') return J({ books: [u.searchParams.get('extended') ? full : book] });
if (u.searchParams.get('title') === 'prejudice' || u.searchParams.get('author') === 'austen') return J({ books: [book] });
return J({ error: 'Audiobooks could not be found' }, 404);
}
if (p === '/cover.gif') { res.writeHead(200, { 'content-type': 'image/gif' }); return res.end(gif); }
res.writeHead(404); res.end();
}).listen(0, () => {
port = fake.address().port; process.env.LIBRIVOX_URL = 'http://127.0.0.1:' + port;
const lv = require('../librivox'), users = require('../users');
const people = { a: users.create({ name: 'Ann', role: 'admin', handle: 'YQ' }), b: users.create({ name: 'Bob', role: 'user', tabs: ['audio'], handle: 'Yg' }) };
const hub = http.createServer((req, res) => users.runAs(req.headers['x-user'] === 'b' ? people.b : people.a, () => lv.handle(req, res))).listen(0, async () => {
const base = 'http://127.0.0.1:' + hub.address().port + '/api/ab/free', call = (p, o = {}) => fetch(base + p, { redirect: 'manual', ...o }), j = r => r.json();
try {
let r = await call('/search?q=prejudice'); let s = await j(r); assert.equal(s.items.length, 1, 'found by title and by author, listed once'); assert.equal(s.items[0].title, 'Pride & Prejudice'); assert.equal(s.items[0].author, 'Jane Austen'); assert.match(s.items[0].cover, /^\/api\/ab\/free\/cover\/55/);
assert.deepEqual((await j(await call('/search?q=zzz'))).items, [], 'nothing found is an empty list, not an error');
assert.ok((await j(await call('/search'))).items.length >= 0, 'no search words lists the catalogue');
r = await call('/book/55'); const b = await j(r); assert.equal(b.chapters.length, 2, 'chapters on other sites are dropped'); assert.ok(!JSON.stringify(b).includes('127.0.0.1:' + port + '/dl'), 'audio addresses are not sent to the browser'); assert.equal(b.chapters[1].title, 'Chapter 3'); assert.equal(b.description, 'A novel');
r = await call('/audio/55/0'); assert.equal(r.status, 302); assert.equal(r.headers.get('location'), `http://127.0.0.1:${port}/dl/1.mp3`);
assert.equal((await call('/audio/55/9')).status, 404); assert.equal((await call('/book/abc')).status, 404);
r = await call('/cover/55'); assert.equal(r.headers.get('content-type'), 'image/gif');
assert.equal((await call('/cover/55?u=' + encodeURIComponent('http://169.254.169.254/x'))).status, 200, 'an address off the allowed sites is ignored (the book\'s own cover is used)');
assert.ok(!seen.some(x => x.includes('169.254')));
assert.deepEqual(await j(await call('/shelf')), []); await call('/shelf', { method: 'POST', body: JSON.stringify({ id: '55' }) });
s = await j(await call('/shelf')); assert.equal(s[0].title, 'Pride & Prejudice'); assert.deepEqual(await j(await call('/shelf', { headers: { 'x-user': 'b' } })), [], "each person's shelf is their own");
const post = o => call('/progress', { method: 'POST', body: JSON.stringify(o) }).then(j);
await post({ 'lv:55': { pos: 400, dur: 600, t: 10 } }); assert.equal((await post({ 'lv:55': { pos: 5, dur: 600, t: 3 } }))['lv:55'].pos, 400, 'the newest position wins');
assert.deepEqual(await j(await call('/progress', { headers: { 'x-user': 'b' } })), {});
await call('/shelf/55', { method: 'DELETE' }); assert.deepEqual(await j(await call('/shelf')), []);
console.log('librivox test ok');
} catch (err) { console.error(err); process.exitCode = 1; }
hub.close(); fake.close();
});
});