sc-voice
Version:
SuttaCentral Voice
1,219 lines (1,172 loc) • 49.1 kB
JavaScript
(typeof describe === 'function') && describe("scv-rest", function() {
const should = require("should");
const fs = require('fs');
const path = require('path');
const supertest = require('supertest');
const jwt = require('jsonwebtoken');
const { logger } = require('log-instance');
logger.logLevel = 'warn';
const {
UserStore,
} = require('rest-bundle');
const {
Definitions,
} = require('suttacentral-api');
const {
SCAudio,
ScvRest,
Section,
SoundStore,
Sutta,
SuttaFactory,
VoiceFactory,
Words,
} = require("../index");
const TEST_ADMIN = {
username: "test-admin",
isAdmin: true,
};
const Queue = require('promise-queue');
const PUBLIC = path.join(__dirname, '../public');
const LOCAL = path.join(__dirname, '../local');
const SC = path.join(LOCAL, 'sc');
const app = require("../scripts/sc-voice.js"); // access cached instance
this.timeout(15*1000);
function sleep(ms=600) {
// The testing server takes a while to wakeup
// and will report 404 until it's ready
return new Promise(r=>setTimeout(()=>r(),ms));
}
var testInitialize = sleep(2000);
function testAuthPost(url, data) {
var token = jwt.sign(TEST_ADMIN, ScvRest.JWT_SECRET);
return supertest(app).post(url)
.set("Authorization", `Bearer ${token}`)
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.send(data);
}
async function testAuthGet(url, contentType='application/json', accept=contentType) {
var token = jwt.sign(TEST_ADMIN, ScvRest.JWT_SECRET);
await testInitialize;
return supertest(app).get(url)
.set("Authorization", `Bearer ${token}`)
.set('Content-Type', contentType)
.set('Accept', accept)
.expect('Content-Type', new RegExp(contentType))
;
}
function testGet(url, contentType='application/json', accept=contentType) {
return supertest(app).get(url)
.set('Content-Type', contentType)
.set('Accept', accept)
.expect('Content-Type', new RegExp(contentType))
;
}
async function testScvRest() {
// Wait for server to start
await testInitialize;
await app.locals.scvRest.initialize();
await sleep(500);
}
it("ScvRest must be initialized", function(done) {
(async function() { try {
var scvRest = app.locals.scvRest;
await(scvRest.initialize());
should(scvRest.initialized).equal(true);
done();
} catch(e) {done(e);} })();
});
it("ScvRest maintains a SoundStore singleton", function() {
var scvRest = app.locals.scvRest;
should(scvRest).instanceOf(ScvRest);
var soundStore = scvRest.soundStore;
should(soundStore).instanceOf(SoundStore);
var voiceFactory = scvRest.voiceFactory;
should(voiceFactory).instanceOf(VoiceFactory);
should(voiceFactory.soundStore).equal(soundStore);
});
it("GET /identity => restbundle identity JSON", done=>{
var async = function* () { try {
var response = yield supertest(app)
.get("/scv/identity").expect((res) => {
res.statusCode.should.equal(200);
var keys = Object.keys(res.body).sort();
should.deepEqual(keys, [
'diskavail', 'diskfree', 'disktotal',
'freemem', 'hostname', 'loadavg', 'name',
'package', 'totalmem', 'uptime', 'version'
]);
}).end((e,r) => e ? async.throw(e) : async.next(r));
done();
} catch (e) { done(e); } }();
async.next();
});
it("GET /sutta/mn1/en/sujato returns sutta", function(done) {
var async = function* () { try {
var response = yield supertest(app).get("/scv/sutta/mn1/en/sujato")
.expect((res) => {
res.statusCode.should.equal(200);
var sutta = res.body;
should.deepEqual(Object.keys(sutta).sort(), [
'translation', 'suttaCode', 'sutta_uid',
'author', 'author_uid', 'titles', 'blurb',
"sections", "suttaplex", "support", 'lang',
].sort());
should.deepEqual(sutta.support,
Definitions.SUPPORT_LEVELS.Supported);
var sections = sutta.sections;
should(sections.length).equal(10);
should(sections[2].expandable).equal(false);
should(sections[2].expanded).equal(true);
should(sections[2].prefix).equal("");
should(sections[2].prop).equal("en");
should.deepEqual(sections[2].template, []);
should(sections[2].title).match(/.Take an uneducated.*/u);
should(sections[2].type).equal("Section");
should.deepEqual(sections[2].values, []);
should(sutta.blurb).match(
/Buddha examines how the notion of a permanent/);
}).end((e,r) => e ? async.throw(e) : async.next(r));
done();
} catch (e) { done(e); } }();
async.next();
});
it("GET /sutta/mn100/en/sujato returns sutta", function(done) {
var async = function* () { try {
var response = yield supertest(app).get("/scv/sutta/mn100/en/sujato")
.expect((res) => {
res.statusCode.should.equal(200);
should(res.body).properties([
"sections"
]);
var sections = res.body.sections;
should(sections.length).equal(2);
should.deepEqual(sections[0], {
expandable: false,
expanded: false,
prefix: "",
prop: 'en',
template: [],
title: `Middle Discourses 100 `,
type: 'Section',
values: [],
segments: [{
en: 'Middle Discourses 100 ',
pli: 'Majjhima Nikāya 100 ',
scid: 'mn100:0.1',
},{
en: 'With Saṅgārava ',
pli: 'Saṅgāravasutta ',
scid: 'mn100:0.2',
}],
});
}).end((e,r) => e ? async.throw(e) : async.next(r));
done();
} catch (e) { done(e); } }();
async.next();
});
it("GET /recite/section/mn1/en/sujato/2 returns recitation", function(done) {
var async = function* () { try {
var response = yield supertest(app)
.get("/scv/recite/section/mn1/en/sujato/2")
.expect((res) => {
res.statusCode.should.equal(200);
should(res.body).properties([
'guid',
]);
should(res.body).properties({
language: 'en',
name: 'Amy',
section: 2,
sutta_uid: 'mn1',
translator: 'sujato',
usage: 'recite',
});
}).end((e,r) => e ? async.throw(e) : async.next(r));
done();
} catch (e) { done(e); } }();
async.next();
});
it("GET /recite/sutta/mn100/en/sujato/1 returns recitation", function(done) {
var async = function* () { try {
var response = yield supertest(app)
.get("/scv/recite/section/mn100/en/sujato/1")
.expect((res) => {
res.statusCode.should.equal(200);
should(res.body).properties([
'guid',
]);
should(res.body).properties({
language: 'en',
name: 'Amy',
section: 1,
sutta_uid: 'mn100',
translator: 'sujato',
usage: 'recite',
});
}).end((e,r) => e ? async.throw(e) : async.next(r));
done();
} catch (e) { done(e); } }();
async.next();
});
it("GET download human audio playlist", async()=>{
console.log(`TODO`, __filename); return;
var scvRest = app.locals.scvRest;
var apiModel = await scvRest.initialize();
var url = `/scv/download/playlist/en/sujato_en/sn2.3%2Fen%2Fsujato`;
var res = await supertest(app)
.get(url)
.expect('Content-Type', /audio\/mp3/)
.expect('Content-Disposition',
'attachment; filename=sn2.3-en-sujato_en_sujato_en.mp3');
var contentLength = Number(res.headers['content-length']);
should(contentLength).above(3400000);
should(contentLength).below(4600000);
should(res.statusCode).equal(200);
});
it("GET /download/playlist/pli+en/amy/an3.76-77 => mp3", async()=>{
await testInitialize;
var scvRest = app.locals.scvRest;
var apiModel = await scvRest.initialize()
var res = await supertest(app)
.get("/scv/download/playlist/pli+en/amy/an3.76-77");
should(res.headers).properties({
'content-type': 'audio/mp3',
'content-disposition': 'attachment; filename=an3.76-77_pli+en_amy.mp3',
});
var contentLength = Number(res.headers['content-length']);
should(contentLength).above(3400000).below(4600000);
should(res.statusCode).equal(200);
});
it("GET /download/playlist/de/vicki/thig1.10 => ogg", async()=>{
await testInitialize;
var scvRest = app.locals.scvRest;
var apiModel = await scvRest.initialize()
//logger.logLevel = 'info';
var res = await supertest(app)
.get("/scv/download/ogg/de/vicki/thig1.10");
should(res.headers).properties({
'content-type': 'audio/ogg',
'content-disposition': 'attachment; filename=thig1.10_de_vicki.ogg',
});
var contentLength = Number(res.headers['content-length']);
should(res.statusCode).equal(200);
should(contentLength).above(50000).below(110000);
});
it("GET /download/playlist/pli+de/vicki/thig1.10 => ogg", async()=>{
await testInitialize;
var scvRest = app.locals.scvRest;
var apiModel = await scvRest.initialize()
//logger.logLevel = 'info';
var res = await supertest(app)
.get("/scv/download/ogg/pli+de/vicki/thig1.10/Aditi");
should(res.headers).properties({
'content-type': 'audio/ogg',
'content-disposition': 'attachment; filename=thig1.10_pli+de_vicki.ogg',
});
var contentLength = Number(res.headers['content-length']);
should(res.statusCode).equal(200);
should(contentLength).above(90000).below(110000);
});
it("GET /download/playlist/de/vicki/thig1.10 => opus", async()=>{
await testInitialize;
var scvRest = app.locals.scvRest;
var apiModel = await scvRest.initialize()
//logger.logLevel = 'info';
var res = await supertest(app)
.get("/scv/download/opus/de/vicki/thig1.10");
should(res.headers).properties({
'content-type': 'audio/opus',
'content-disposition': 'attachment; filename=thig1.10_de_vicki.opus',
});
var contentLength = Number(res.headers['content-length']);
should(res.statusCode).equal(200);
should(contentLength).above(50000).below(60000);
});
it("GET /download/playlist/pli+de/vicki/thig1.10 => opus", async()=>{
await testInitialize;
var scvRest = app.locals.scvRest;
var apiModel = await scvRest.initialize()
//logger.logLevel = 'info';
var res = await supertest(app)
.get("/scv/download/opus/pli+de/vicki/thig1.10/Aditi");
should(res.headers).properties({
'content-type': 'audio/opus',
'content-disposition': 'attachment; filename=thig1.10_pli+de_vicki.opus',
});
var contentLength = Number(res.headers['content-length']);
should(res.statusCode).equal(200);
should(contentLength).above(90000).below(110000);
});
it("GET /sutta/an2.1-10/en/sujato returns sutta", function(done) {
console.log("TODO", __filename); done(); return;
var async = function* () { try {
var response = yield supertest(app)
.get("/scv/sutta/an2.1-10/en/sujato").expect((res) => {
res.statusCode.should.equal(200);
should(res.body).properties([
"sections"
]);
var sections = res.body.sections;
should(sections.length).equal(2);
should.deepEqual(sections[0], {
expandable: false,
prefix: "",
prop: 'en',
template: [],
title: `Middle Discourses 100${Words.U_ELLIPSIS}`,
type: 'Section',
values: [],
segments: [{
en: 'Middle Discourses 100',
pli: 'Majjhima Nikāya 100',
scid: 'mn100:0.1',
},{
en: 'With Saṅgārava',
pli: 'Saṅgāravasutta',
scid: 'mn100:0.2',
}],
});
}).end((e,r) => e ? async.throw(e) : async.next(r));
done();
} catch (e) { done(e); } }();
async.next();
});
it("Queue handles promises", function(done) {
(async function() { try {
var monitor = [];
var q = new Queue(3, Infinity);
var doit = (ms) => new Promise((resolve, reject) => {
setTimeout(() => {
monitor.push(ms);
resolve(ms);
}, ms);
});
var queuedPromises = [1,2,3,4,5,6,7].map(i => q.add(() => doit(i)));
var all = Promise.all(queuedPromises);
should(q.getQueueLength()).equal(4); // waiting
should(q.getPendingLength()).equal(3); // processing
should.deepEqual(monitor, []);
var r1 = await(queuedPromises[1]);
should(r1).equal(2);
should.deepEqual(monitor, [1,2]);
should(q.getQueueLength()).equal(2); // waiting
should(q.getPendingLength()).equal(3); // processing
var r2 = await(queuedPromises[2]);
should(r2).equal(3);
should(q.getQueueLength()).equal(1); // waiting
should(q.getPendingLength()).equal(3); // processing
var r3 = await(queuedPromises[3]);
should(r3).equal(4);
should(q.getQueueLength()).equal(0); // waiting
should(q.getPendingLength()).equal(3); // processing
var allResult = await(all);
should.deepEqual(allResult, [1,2,3,4,5,6,7]);
done();
} catch(e) {done(e);} })();
});
it("GET /search/:pattern returns suttaplexes found", async()=>{
await testInitialize;
var maxResults = 3;
var pattern = `root%20of%20suffering`;
var url = `/scv/search/${pattern}?maxResults=${maxResults}`;
var res = await supertest(app).get(url);
res.statusCode.should.equal(200);
var {
method,
results,
} = res.body;
should(method).equal('phrase');
should(results).instanceOf(Array);
should(results.length).equal(3);
should.deepEqual(results.map(r => r.uid),[
'sn42.11', 'mn105', 'mn1',
]);
should.deepEqual(results.map(r => r.count),
[ 5.091, 3.016, 2.006 ]);
// use default maxResults
var url = `/scv/search/${pattern}`;
var res = await supertest(app).get(url);
res.statusCode.should.equal(200);
var {
method,
results,
} = res.body;
should(method).equal('phrase');
should(results).instanceOf(Array);
should(results.length).equal(5);
should.deepEqual(results[0].audio,undefined);
should.deepEqual(results.map(r => r.uid),[
'sn42.11', 'mn105', 'mn1', 'sn56.21', 'mn116',
]);
});
it("GET /scv/play/section/... => playable section", done=>{
(async function() { try {
await new Promise(resolve=>setTimeout(()=>resolve(),1000));
var iSection = 1;
var vnameTrans = 'Raveena';
var vnameRoot = 'Aditi';
var url = `/scv/play/section/mn1/en/sujato/`+
`${iSection}/${vnameTrans}/${vnameRoot}`;
var res = await supertest(app).get(url);
res.statusCode.should.equal(200);
var section = res.body instanceof Buffer
? JSON.parse(res.body) : res.body;
should(section.segments.length).equal(332);
should(section.sutta_uid).equal('mn1');
should(section.vnameTrans).equal(vnameTrans);
should(section.vnameRoot).equal(vnameRoot);
should(section.section).equal(iSection);
should(section.nSections).equal(2);
should(section.language).equal('en');
should(section.translator).equal('sujato');
should.deepEqual(section.segments[0].audio, {});
var testPath = path.join(PUBLIC,
`play/section/mn1/en/sujato/${iSection}/${vnameTrans}`);
fs.writeFileSync(testPath, JSON.stringify(section, null,2));
done();
} catch(e) {done(e);} })();
});
it("GET /play/segment/... => playable segment", done=>{
(async function() { try {
await new Promise(resolve=>setTimeout(()=>resolve(),1000));
var voicename = 'Matthew';
var scid = "mn1:0.1";
var url =
`/scv/play/segment/mn1/en/sujato/${scid}/${voicename}`;
var res = await supertest(app).get(url);
res.statusCode.should.equal(200);
var data = res.body instanceof Buffer
? JSON.parse(res.body) : res.body;
should(data.segment.en).match(/^Middle Discourses 1/);
should(data.segment.audio.pli)
.match(/eb2c6cf0626c7a0f422da93a230c4ab7/); // no numbers
var scid = "mn1:3.1";
var url =
`/scv/play/segment/mn1/en/sujato/${scid}/${voicename}`;
var res = await supertest(app).get(url);
res.statusCode.should.equal(200);
var data = res.body instanceof Buffer
? JSON.parse(res.body) : res.body;
should(data.segment.en).match(/^.Take an uneducated ordinary/);
if (0) { // simulate REST response using static file
var testPath = path.join(PUBLIC,
`play/segment/mn1/en/sujato/${scid}/${voicename}`);
fs.writeFileSync(testPath, JSON.stringify(data, null,2));
}
done();
} catch(e) {done(e);} })();
});
it("GET /play/segment/... => playable segment", done=>{
(async function() { try {
var voicename = '0';
var scid = "mn1:0.1";
var url = `/scv/play/segment/mn1/en/sujato/${scid}/${voicename}`;
var res = await supertest(app).get(url);
res.statusCode.should.equal(200);
var data = res.body instanceof Buffer ? JSON.parse(res.body) : res.body;
should(data.segment.en).match(/^Middle Discourses 1/);
should(data.segment.audio.pli).match(/eb2c6cf0626c7a0f422da93a230c4ab7/); // no numbers
if (0) {
var scid = "mn1:52-74.23";
var url = `/scv/play/segment/mn1/en/sujato/${scid}/${voicename}`;
var res = await supertest(app).get(url);
res.statusCode.should.equal(200);
var data = res.body instanceof Buffer ? JSON.parse(res.body) : res.body;
should(data.sutta_uid).equal('mn1');
should(data.vnameLang).equal('Amy');
should(data.vnameRoot).equal('Aditi');
should(data.iSegment).equal(299);
should(data.section).equal(4);
should(data.nSections).equal(10);
should(data.voicename).equal(voicename);
should(data.language).equal('en');
should(data.translator).equal('sujato');
should(data.segment.en).match(/^They directly know extinguishment as/);
should(data.segment.audio.en).match(/^3f8996/);
should(data.segment.audio.pli).match(/^a777fb/);
}
if (1) {
var scid = "mn1:3.1";
var url = `/scv/play/segment/mn1/en/sujato/${scid}/${voicename}`;
var res = await supertest(app).get(url);
res.statusCode.should.equal(200);
var data = res.body instanceof Buffer ? JSON.parse(res.body) : res.body;
should(data.segment.en).match(/^.Take an uneducated ordinary/);
var testPath = path.join(PUBLIC,
`play/segment/mn1/en/sujato/${scid}/${voicename}`);
fs.writeFileSync(testPath, JSON.stringify(data, null,2));
}
if (0) {
var scid = "mn1:3.2";
var url = `/scv/play/segment/mn1/en/sujato/${scid}/${voicename}`;
var res = await supertest(app).get(url);
res.statusCode.should.equal(200);
var data = res.body instanceof Buffer ? JSON.parse(res.body) : res.body;
should(data.segment.en).match(/^They perceive earth as earth/);
var testPath = path.join(PUBLIC,
`play/segment/mn1/en/sujato/${scid}/${voicename}`);
fs.writeFileSync(testPath, JSON.stringify(data, null,2));
}
done();
} catch(e) {done(e);} })();
});
it("GET /play/audio/:suid/:lang/:trans/:voice/:guid returns audio", function(done) {
(async function() { try {
done();
} catch(e) {done(e);} })();
});
it("GET /play/segment/... handles large segment", async()=>{
console.log(`TODO`, __filename); return;
await new Promise(resolve=>setTimeout(()=>resolve(),1000));
var scid = "an2.281-309:1.1";
var sutta_uid = scid.split(":")[0];
var vnameTrans = "1"; // Matthew
var url = `/scv/play/segment/${sutta_uid}/`+
`en/sujato/${scid}/${vnameTrans}`;
var res = await supertest(app).get(url);
res.statusCode.should.equal(200);
var data = res.body instanceof Buffer
? JSON.parse(res.body) : res.body;
should(data.sutta_uid).equal('an2.281-309');
should(data.vnameTrans).equal('Brian');
should(data.vnameRoot).equal('Aditi');
should(data.iSegment).equal(9);
should(data.nSections).equal(3);
should(data.language).equal('en');
should(data.translator).equal('sujato');
should(data.segment.en)
.match(/^.For two reasons the Realized One/);
should(data.segment.audio.en)
.match(/4341471c187e12334475901a9599698c/);
should(data.segment.audio.pli)
.match(/7bd718c9fbda06ab56b2d09a05776353/);
});
it("GET /play/segment/... handles HumanTts dn33", async()=>{
var scid = "dn33:0.1";
var sutta_uid = scid.split(":")[0];
var langTrans = 'en';
var vnameTrans = "sujato_en";
var vnameRoot = "sujato_pli";
var url = [
`/scv/play/segment`,
sutta_uid,
langTrans,
'sujato',
scid,
vnameTrans,
vnameRoot,
].join('/');
var res = await supertest(app).get(url);
res.statusCode.should.equal(200);
var data = res.body instanceof Buffer
? JSON.parse(res.body) : res.body;
should(data.sutta_uid).equal(scid.split(':')[0]);
should(data.vnameTrans).equal(vnameTrans);
should(data.vnameRoot).equal(vnameRoot);
should(data.iSegment).equal(0);
should(data.section).equal(0);
should(data.nSections).equal(12);
should(data.language).equal('en');
should(data.translator).equal('sujato');
should(data.segment.pli).match(/^Dīgha Nikāya 33/);
should(data.segment.audio.vnamePali).equal('Aditi');
should(data.segment.audio.vnameTrans).equal('Amy');
should(data.segment.audio.en)
.match(/b06d3e95cd46714448903fa8bcb12004/);
should(data.segment.audio.pli)
.match(/899e4cd12b700b01200f295631b1576b/);
});
it("GET /play/segment/... handles HumanTts sn1.9", done=>{
(async function() { try {
var scid = "sn1.9:1.1";
var sutta_uid = scid.split(":")[0];
var langTrans = 'en';
var vnameTrans = "Matthew";
var vnameRoot = "sujato_pli";
var url = [
`/scv/play/segment`,
sutta_uid,
langTrans,
'sujato',
scid,
vnameTrans,
vnameRoot,
].join('/');
var res = await supertest(app).get(url);
res.statusCode.should.equal(200);
var data = res.body instanceof Buffer
? JSON.parse(res.body) : res.body;
should(data.sutta_uid).equal(scid.split(':')[0]);
should(data.vnameTrans).equal('Matthew');
should(data.vnameRoot).equal('sujato_pli');
should(data.iSegment).equal(3);
should(data.nSections).equal(2);
//should(data.section).equal(1);
should(data.language).equal('en');
should(data.translator).equal('sujato');
should(data.segment.pli).match(/Sāvatthinidānaṁ.*/);
should(data.segment.audio.en)
.match(/e5f5e2ec93f9f41908924177d5ee63ca/);
should(data.segment.audio.pli)
.match(/57eacb73319677cbe42256c332630451/);
should(data.segment.audio.vnamePali).equal(undefined);
done();
} catch(e) {done(e);} })();
});
it("GET /play/segment/... handles HumanTts sn12.1", done=>{
(async function() { try {
var scid = "sn12.1:1.2";
var sutta_uid = scid.split(":")[0];
var langTrans = 'en';
var vnameTrans = "Matthew";
var vnameRoot = "sujato_pli";
var url = [
`/scv/play/segment`,
sutta_uid,
langTrans,
'sujato',
scid,
vnameTrans,
vnameRoot,
].join('/');
logger.warn("EXPECTED WARN BEGIN");
var res = await supertest(app).get(url);
logger.warn("EXPECTED WARN END");
res.statusCode.should.equal(200);
var data = res.body instanceof Buffer
? JSON.parse(res.body) : res.body;
should(data.sutta_uid).equal(scid.split(':')[0]);
should(data.vnameTrans).equal('Matthew');
should(data.vnameRoot).equal('sujato_pli');
should(data.language).equal('en');
should(data.translator).equal('sujato');
should(data.segment.pli)
.match(/ekaṁ samayaṁ bhagavā sāvatthiyaṁ.*/);
should(data.segment.audio.en)
.match(/d0a8567a6fca2fbeaa5d14e610304826/);
should(data.segment.audio.pli)
.match(/a11ebc9a6bbe583d36e375ca163b6351/);
should(data.segment.audio.vnamePali).equal('Aditi');
done();
} catch(e) {done(e);} })();
});
it("GET /examples/:n return search examples", function(done) {
(async function() { try {
var n = 3;
var url = `/scv/examples/${n}`;
var res = await supertest(app).get(url);
res.statusCode.should.equal(200);
var data = res.body instanceof Buffer ? JSON.parse(res.body) : res.body;
should(data instanceof Array);
should(data.length).equal(3);
for (var i = 3; i-- > 0; ) { // at least one trial must be different
var res2 = await supertest(app).get(url);
try {
res2.statusCode.should.equal(200);
var data2 = res2.body instanceof Buffer
? JSON.parse(res2.body) : res2.body;
should(data).not.eql(data2);
break;
} catch(e) {
if (i === 0) {
throw e;
}
}
}
done();
} catch(e) {done(e);} })();
});
it("GET /wiki-aria/:page return Aria for wiki page", done=>{
var WIKIURL = `https://raw.githubusercontent.com/wiki/`+
`sc-voice/sc-voice`;
(async function() { try {
var url = `/scv/wiki-aria/Home.md`;
var res = await supertest(app).get(url);
res.statusCode.should.equal(200);
var html = res.body.html;
should(res.body.url).equal(`${WIKIURL}/Home.md`);
var html = res.body.html;
should(html).match(/These wiki pages/ui);
done();
} catch(e) {done(e);} })();
});
it("GET auth/sound-store/volume-info return stats", function(done) {
(async function() { try {
var url = `/scv/auth/sound-store/volume-info`;
var scvRest = app.locals.scvRest;
var token = jwt.sign(TEST_ADMIN, ScvRest.JWT_SECRET);
var res = await supertest(app).get(url)
.set("Authorization", `Bearer ${token}`);
res.statusCode.should.equal(200);
var soundStore = scvRest.soundStore;
should.deepEqual(res.body, soundStore.volumeInfo());
done();
} catch(e) {done(e);} })();
});
it("POST auth/sound-store/clear-volume clears volume cache", done=>{
(async function() { try {
var scvRest = app.locals.scvRest;
var soundStore = scvRest.soundStore;
var volume = 'test-clear-volume';
var fpath = soundStore.guidPath({
volume,
guid:'12345',
});
fs.writeFileSync(fpath, '12345data');
should(fs.existsSync(fpath)).equal(true);
var url = `/scv/auth/sound-store/clear-volume`;
var scvRest = app.locals.scvRest;
var token = jwt.sign(TEST_ADMIN, ScvRest.JWT_SECRET);
var data = { volume, };
var res = await testAuthPost(url, data);
res.statusCode.should.equal(200);
should.deepEqual(res.body, {
filesDeleted:1,
});
should(fs.existsSync(fpath)).equal(false);
var data = { volume:'invalid-volume', };
logger.warn(`EXPECTED WARN BEGIN`);
var res = await testAuthPost(url, data);
res.statusCode.should.equal(500);
logger.warn(`EXPECTED WARN END`);
done();
} catch(e) {done(e);} })();
});
it("GET audio-url/... returns supported audio url", async()=>{
// short url
var url = '/scv/audio-urls/sn1.23';
var res = await supertest(app).get(url)
let urlBase =
`https://${SCAudio.SC_OPUS_STORE}.sgp1.cdn.digitaloceanspaces.com`;
res.statusCode.should.equal(200);
should.deepEqual(res.body.map(src=>src.url), [
`${urlBase}/pli/sn/sn1/sn1.23-pli-mahasangiti-sujato.webm`,
`${urlBase}/en/sn/sn1/sn1.23-en-sujato-sujato.webm`,
]);
});
it("GET auth/vsm/s3-credentials => sanitized vsm-s3.json", done=>{
var vsmS3Path = path.join(LOCAL, 'vsm-s3.json');
if (!fs.existsSync(vsmS3Path)) {
logger.warn('skipping vsm/s3-credentials GET test');
done();
return;
}
(async function() { try {
var url = `/scv/auth/vsm/s3-credentials`;
var goodCreds = JSON.parse(fs.readFileSync(vsmS3Path));
// Returns obfuscated credentials
var res = await testAuthGet(url);
res.statusCode.should.equal(200);
var actualCreds = res.body;
should(actualCreds.Bucket)
.equal(goodCreds.Bucket||goodCreds.Bucket);
should(actualCreds.s3.endpoint).equal(goodCreds.s3.endpoint);
should(actualCreds.s3.region).equal(goodCreds.s3.region);
should(actualCreds.s3.accessKeyId.substr(0,5)).equal('*****');
should(actualCreds.s3.secretAccessKey.substr(0,5)).equal('*****');
done();
} catch(e) {done(e);} })();
});
it("POST auth/vsm/s3-credentials good creds", async()=>{try{
await testInitialize;
var vsmS3Path = path.join(LOCAL, 'vsm-s3.json');
if (!fs.existsSync(vsmS3Path)) {
logger.warn('skipping vsm/s3-credentials POST test');
return;
}
var url = `/scv/auth/vsm/s3-credentials`;
//logger.logLevel = 'info';
// save good creds and make bad creds
var goodCreds = JSON.parse(fs.readFileSync(vsmS3Path));
var badCreds = JSON.parse(JSON.stringify(goodCreds));
badCreds.s3.secretAccessKey = "wrong-key";
await fs.promises.writeFile(vsmS3Path, JSON.stringify(badCreds, null, 2));
// Good credentials are saved
var res = await testAuthPost(url, goodCreds);
var actualCreds = JSON.parse(await fs.promises.readFile(vsmS3Path));
await fs.promises.writeFile(vsmS3Path, JSON.stringify(goodCreds, null, 2));
res.statusCode.should.equal(200);
should.deepEqual(actualCreds, goodCreds);
} catch(e) {
logger.info(`TEST: restoring ${vsmS3Path}`);
fs.writeFileSync(vsmS3Path, JSON.stringify(goodCreds, null, 2));
throw e;
}});
it("POST auth/vsm/s3-credentials bad creds", async()=>{try{
await testInitialize;
var vsmS3Path = path.join(LOCAL, 'vsm-s3.json');
if (!fs.existsSync(vsmS3Path)) {
logger.warn('skipping vsm/s3-credentials POST test');
return;
}
// Bad credentials are not saved
var url = `/scv/auth/vsm/s3-credentials`;
var logLevel = logger.logLevel;
logger.logLevel = 'error';
var goodCreds = JSON.parse(fs.readFileSync(vsmS3Path));
logger.warn("EXPECTED WARNING BEGIN");
var badCreds = JSON.parse(JSON.stringify(goodCreds));
badCreds.s3.secretAccessKey = 'bad-secretAccessKey';
var res = await testAuthPost(url, badCreds);
logger.warn("EXPECTED WARNING END");
res.statusCode.should.equal(500);
var actualCreds = JSON.parse(fs.readFileSync(vsmS3Path));
should.deepEqual(actualCreds, goodCreds);
} finally {
logger.logLevel = logLevel;
}});
it("GET auth/vsm/factory-task returns factory status", function(done) {
(async function() { try {
// Default Bucket
var url = `/scv/auth/vsm/factory-task`;
var res = await testAuthGet(url);
res.statusCode.should.equal(200);
should(res.body).properties({
error: null,
summary: 'VSMFactory created',
name: 'VSMFactory',
msActive: 0,
});
should.deepEqual(Object.keys(res.body).sort(), [
'isActive', 'lastActive',
'error', 'name', 'msActive', 'started', 'summary', 'uuid',
'actionsTotal', 'actionsDone',
].sort());
done();
} catch(e) {done(e);} })();
});
it("GET auth/vsm/list-objects lists bucket objects", function(done) {
var vsmS3Path = path.join(LOCAL, 'vsm-s3.json');
if (!fs.existsSync(vsmS3Path)) {
logger.warn("skipping auth/vsm/list-objects test");
done();
return;
}
(async function() { try {
// Default Bucket
var url = `/scv/auth/vsm/list-objects`;
var res = await testAuthGet(url);
res.statusCode.should.equal(200);
var s3Result = res.body;
should(s3Result).properties({
Name: fs.existsSync(vsmS3Path)
? 'sc-voice-vsm'
: 'sc-voice-test',
MaxKeys: 1000,
s3: {
endpoint: 'https://s3.us-west-1.amazonaws.com',
region: 'us-west-1',
},
});
var c0 = s3Result.Contents[0];
should(c0).properties([
'Key', 'LastModified', 'ETag', 'Size', 'StorageClass', 'Owner',
'upToDate',
]);
if (c0.upToDate) {
should(new Date(c0.restored))
.above(new Date(c0.LastModified));
}
should(s3Result.Contents[0].Key)
.match(/[a-z]*_[a-z]*_[a-z]*_[a-z]*.tar.gz/iu);
done();
} catch(e) {done(e);} })();
});
it("POST auth/vsm/restore-s3-archives", async()=>{
console.log(`TODO`,__filename); return; // Restore VSM file
var vsmS3Path = path.join(LOCAL, 'vsm-s3.json');
if (!fs.existsSync(vsmS3Path)) {
logger.warn('skipping vsm/s3-credentials POST test');
done();
return;
}
var url = `/scv/auth/vsm/list-objects`;
var resList = await testAuthGet(url);
var {
Contents,
} = resList.body;
var url = `/scv/auth/vsm/restore-s3-archives`;
var restore = [{
Key: 'kn_en_sujato_amy.tar.gz',
ETag: '"e2141be1eddffebe4bded17b83aaa5ee"',
}];
var clearVolume = false;
var data = {
restore,
clearVolume,
};
var res = await testAuthPost(url, data);
res.statusCode.should.equal(200);
should(res.body).properties({
Bucket: 'sc-voice-vsm',
clearVolume,
restore,
});
});
it("POST auth/vsm/create-archive create VSM", async()=>{
console.log(`TODO`,__filename); return;
var url = `/scv/auth/vsm/create-archive`;
var nikaya = 'kn';
var author = 'sujato';
var lang = 'pli';
var voice = 'aditi';
var maxSuttas = 1;
var postArchive = false;
var data = {
nikaya,
voice,
lang,
author,
maxSuttas,
postArchive,
};
// the response is immediate since processing is in the background
var res = await testAuthPost(url, data);
res.statusCode.should.equal(200);
should(res.body).properties({
postArchive,
author,
lang,
nikaya,
maxSuttas,
voice,
});
var summary = 'Building VSM for nikaya:kn language:pli voice:aditi';
should(res.body.task).properties({
actionsTotal: 2,
actionsDone: 0,
summary,
error: null,
name: 'VSMFactory',
});
// an immediately following request should be busy
logger.warn("EXPECTED WARNING BEGIN");
var res = await testAuthPost(url, data);
logger.warn("EXPECTED WARNING END");
res.statusCode.should.equal(500);
should(res.body.error).match(/VSM Factory is busy/);
var taskUrl = `/scv/auth/vsm/factory-task`;
var res = await testAuthGet(taskUrl);
res.statusCode.should.equal(200);
should(res.body).properties({
error: null,
summary,
name: 'VSMFactory',
isActive: true,
});
// and after a while it should be done
await new Promise((resolve, reject) => {
setTimeout(() => resolve(true), 5000);
});
var res = await testAuthGet(taskUrl);
res.statusCode.should.equal(200);
should(res.body).properties({
error: null,
name: 'VSMFactory',
isActive: false,
});
should(res.body.summary)
.match(/kn_pli_mahasangiti_aditi suttas imported/);
// and we can submit another request
var res = await testAuthPost(url, data);
res.statusCode.should.equal(200);
});
it("GET voices returns voices", function(done) {
(async function() { try {
// default
var url = "/scv/voices";
var res = await supertest(app).get(url)
should(res.statusCode).equal(200);
var voices = res.body;
should.deepEqual(voices.map(v=>v.name).slice(0,8), [
// en voices first
'Amy', 'Brian', 'Raveena', 'Matthew', 'sujato_en',
// non-en voices
'Vicki', 'Hans', 'Marlene', // de voices
//'Ricardo', // pt
//'Aditi', 'sujato_pli', // pli voices last
])
should(voices[0]).properties({
name: "Amy",
iVoice: 0,
usage: "recite",
});
// en
var url = "/scv/voices/en";
var res = await supertest(app).get(url)
should(res.statusCode).equal(200);
var voices = res.body;
should.deepEqual(voices.map(v=>v.name), [
'Amy', 'Brian', 'Raveena', 'Matthew', 'sujato_en',
'Aditi', 'sujato_pli', // pli voices last
])
// de
var url = "/scv/voices/de";
var res = await supertest(app).get(url)
should(res.statusCode).equal(200);
var voices = res.body;
should.deepEqual(voices.map(v=>v.name), [
'Vicki', 'Hans', 'Marlene', // de voices
'Aditi', 'sujato_pli', // pli voices last
])
done();
} catch(e) {done(e);} })();
});
it("GET authors returns authors", function(done) {
(async function() { try {
var scvRest = app.locals.scvRest;
await scvRest.initialize();
var url = "/scv/authors";
var res = await supertest(app).get(url)
should(res.statusCode).equal(200);
var authors = res.body;
should.deepEqual(authors.sabbamitta, {
name: 'Anagarika Sabbamitta',
lang: 'de',
type: 'translator',
})
done();
} catch(e) {done(e);} })();
});
it("GET auth/logs returns logfiles", function(done) {
(async function() { try {
var logDir = path.join(LOCAL, 'logs');
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir);
fs.writeFileSync(path.join(logDir, 'test3'), 'test-log3');
fs.writeFileSync(path.join(logDir, 'test2'), 'test-log2');
fs.writeFileSync(path.join(logDir, 'test1'), 'test-log1');
}
var files = fs.readdirSync(logDir).sort((a,b) => -a.localeCompare(b));
var url = "/scv/auth/logs";
var res = await testAuthGet(url);
should(res.statusCode).equal(200);
var resFiles = res.body;
should.deepEqual(resFiles.map(f=>f.name), files);
should(resFiles[0]).properties(['size', 'mtime']);
done();
} catch(e) {done(e);} })();
});
it("GET auth/log/:ilog returns logfile", async()=>{
var logDir = path.join(LOCAL, 'logs');
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir);
fs.writeFileSync(path.join(logDir, 'test3'), 'test-log3');
fs.writeFileSync(path.join(logDir, 'test2'), 'test-log2');
fs.writeFileSync(path.join(logDir, 'test1'), 'test-log1');
}
var files = fs.readdirSync(logDir).sort((a,b) => -a.localeCompare(b));
var index = 0;
if (files.length > index) {
var url = `/scv/auth/log/${index}`;
var res = await testAuthGet(url, 'text/plain');
should(res.statusCode).equal(200);
var log = fs.readFileSync(path.join(logDir, files[index])).toString();
should(res.text).equal(log);
}
index++;
if (files.length > index) {
var url = `/scv/auth/log/${index}`;
var res = await testAuthGet(url, 'text/plain');
should(res.statusCode).equal(200);
var log = fs.readFileSync(path.join(logDir, files[index])).toString();
should(res.text).equal(log);
}
// error
var url = `/scv/auth/log/asdf`;
logger.warn('EXPECTED WARN: BEGIN');
var res = await testAuthGet(url, 'text/plain');
logger.warn('EXPECTED WARN: END');
should(res.statusCode).equal(500);
should(res.text).match(/Log file not found:asdf/);
});
it("GET /search/:pattern/:lang returns German", async()=>{
await testInitialize;
var maxResults = 3;
var pattern = `dn7`;
var lang = 'de'
var url =
`/scv/search/${pattern}/${lang}?maxResults=${maxResults}`;
var response = await supertest(app).get(url);
response.statusCode.should.equal(200);
var {
method,
results,
} = response.body;
should(results).instanceOf(Array);
should(results.length).equal(1);
should.deepEqual(results.map(r => r.uid),[
'dn7',
]);
should(results[0].sutta.author_uid).equal('sabbamitta');
should(method).equal('sutta_uid');
});
it("POST auth/update-bilara", async()=>{
var scvRest = await(testScvRest());
var url = `/scv/auth/update-bilara`;
var data = { };
var res = await testAuthPost(url, data);
res.statusCode.should.equal(200);
should(res.body).properties({
error: null,
summary: 'Update completed',
});
should(res.body.elapsed).above(0);
});
it("GET audio info", async()=>{
var scvRest = await testScvRest();
var guid = `e0bd9aadd84f3f353f17cceced97ff13`;
var url = `/scv/auth/audio-info/an_en_sujato_amy/${guid}`;
var res = await testAuthGet(url);
var {
statusCode,
body: infoArray,
} = res;
statusCode.should.equal(200);
should(infoArray).instanceOf(Array);
should.deepEqual(infoArray.map(i=>i.api), [
"aws-polly",]);
should.deepEqual(infoArray.map(i=>i.voice), [
"Amy",]);
should.deepEqual(infoArray.map(i=>i.guid), [
"e0bd9aadd84f3f353f17cceced97ff13",
]);
});
it("TESTTESTGET /search/an4.182/ja returns Kaz sutta", async()=>{
await testInitialize;
var maxResults = 3;
var pattern = `an4.182`;
var lang = 'ja'
var url =
`/scv/search/${pattern}/${lang}?maxResults=${maxResults}`;
var response = await supertest(app).get(url);
response.statusCode.should.equal(200);
var {
method,
results,
} = response.body;
should(results).instanceOf(Array);
should(results.length).equal(1);
should.deepEqual(results.map(r => r.uid),[
'an4.182',
]);
should(results[0].sutta.author_uid).equal('kaz');
should(method).equal('sutta_uid');
});
it("TESTTESTGET /examples/:n => ja examples", async()=>{
var n = 1000;
var lang = 'ja';
var url = `/scv/examples/${n}?lang=${lang}`;
var res = await supertest(app).get(url);
res.statusCode.should.equal(200);
var data = res.body instanceof Buffer ? JSON.parse(res.body) : res.body;
should.deepEqual(data.sort().slice(0,3), [
//"全ての活動が静まり",
"愛情による心の解放",
"美しさを贈り",
"老いる定め",
]);
});
});