Use service-core

This commit is contained in:
Jonatan Nilsson 2022-08-13 21:52:45 +00:00
parent 9a0fca4a22
commit 666c4bee89
20 changed files with 436 additions and 393 deletions

View file

@ -1,22 +0,0 @@
{
"NODE_ENV": "development",
"server": {
"port": 4020,
"host": "0.0.0.0"
},
"bunyan": {
"name": "storage-upload",
"streams": [{
"stream": "process.stdout",
"level": "debug"
}
]
},
"jwt": {
"secret": "this-is-my-secret",
"options": {
"expiresIn": 604800
}
},
"fileSize": 524288000
}

View file

@ -1,67 +1,61 @@
import fs from 'fs' import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
import Nconf from 'nconf-lite' import Nconf from 'nconf-lite'
import { Util } from 'service-core'
const nconf = new Nconf() const nconf = new Nconf()
const __dirname = path.dirname(fileURLToPath(import.meta.url))
let pckg = JSON.parse(fs.readFileSync(path.resolve(path.join(__dirname, '../package.json'))))
// Helper method for global usage. // Helper method for global usage.
nconf.inTest = () => nconf.get('NODE_ENV') === 'test' nconf.inTest = () => nconf.get('NODE_ENV') === 'test'
// Config follow the following priority check order: // Config follow the following priority check order:
// 1. package.json // 1. Enviroment variables
// 2. Enviroment variables // 2. package.json
// 3. config/config.json // 3. config/config.json
// 4. config/config.default.json // 4. config/config.default.json
// Load enviroment variables as first priority
pckg = { nconf.env({
name: pckg.name, separator: '__',
version: pckg.version, whitelist: [
description: pckg.description, 'NODE_ENV',
author: pckg.author, 'server__port',
license: pckg.license, 'server__host',
homepage: pckg.homepage, 'bunyan__name',
} 'frontend__url',
'fileSize',
'name',
'NODE_VERSION',
],
parseValues: true,
})
// Load overrides as first priority // Load empty overrides that can be overwritten later
nconf.overrides(pckg) nconf.overrides({})
let util = new Util(import.meta.url)
let pckg = JSON.parse(fs.readFileSync(util.getPathFromRoot(`../package.json`)))
// Load enviroment variables as second priority nconf.defaults({
nconf.env() "name": pckg.name,
"version": pckg.version,
"NODE_ENV": "development",
// Load any overrides from the appropriate config file "server": {
let configFile = '../config/config.json' "port": 4040,
"host": "0.0.0.0"
/* istanbul ignore else */ },
if (nconf.get('NODE_ENV') === 'test') { "bunyan": {
configFile = '../config/config.test.json' "name": "storage-upload",
} "streams": [{
"stream": "process.stdout",
/* istanbul ignore if */ "level": "debug"
if (nconf.get('NODE_ENV') === 'production') { }
configFile = '../config/config.production.json' ]
} },
"sites": {
nconf.file('main', path.resolve(path.join(__dirname, configFile))) },
"uploadFolder": "./public",
// Load defaults "fileSize": 524288000
nconf.file('default', path.resolve(path.join(__dirname, '../api/config.default.json'))) })
// Final sanity checks
/* istanbul ignore if */
if (typeof global.it === 'function' & !nconf.inTest()) {
// eslint-disable-next-line no-console
console.log('Critical: potentially running test on production enviroment. Shutting down.')
process.exit(1)
}
export default nconf export default nconf

View file

@ -1,7 +0,0 @@
export class HttpError extends Error {
constructor(message, status = 500) {
super(message)
this.status = status
}
}

View file

@ -1,22 +0,0 @@
import bunyan from 'bunyan-lite'
import config from './config.mjs'
import * as defaults from './defaults.mjs'
// Clone the settings as we will be touching
// on them slightly.
let settings = defaults.default(config.get('bunyan'))
// Replace any instance of 'process.stdout' with the
// actual reference to the process.stdout.
for (let i = 0; i < settings.streams.length; i++) {
/* istanbul ignore else */
if (settings.streams[i].stream === 'process.stdout') {
settings.streams[i].stream = process.stdout
}
}
// Create our logger.
const log = bunyan.createLogger(settings)
export default log

View file

@ -1,5 +1,5 @@
import fs from 'fs' import fs from 'fs'
import { HttpError } from '../error.mjs' import { HttpError } from 'flaska'
import formidable from 'formidable' import formidable from 'formidable'
import config from '../config.mjs' import config from '../config.mjs'
@ -36,12 +36,12 @@ export function uploadFile(ctx, siteName, noprefix = false) {
let prefix = '' let prefix = ''
var form = new formidable.IncomingForm() var form = new formidable.IncomingForm()
form.uploadDir = `./public/${siteName}` form.uploadDir = `${config.get('uploadFolder')}/${siteName}`
form.maxFileSize = config.get('fileSize') form.maxFileSize = config.get('fileSize')
form.parse(ctx.req, function(err, fields, files) { form.parse(ctx.req, function(err, fields, files) {
if (err) return rej(err) if (err) return rej(err)
if (!files || !files.file) return rej(new HttpError('File in body was missing', 422)) if (!files || !files.file) return rej(new HttpError(422, 'File in body was missing'))
let file = files.file let file = files.file
Object.keys(fields).forEach(function(key) { Object.keys(fields).forEach(function(key) {
@ -51,13 +51,13 @@ export function uploadFile(ctx, siteName, noprefix = false) {
}) })
ctx.req.body = fields ctx.req.body = fields
if (!noprefix || fs.existsSync(`./public/${siteName}/${prefix}${file.name}`)) { if (!noprefix || fs.existsSync(`${config.get('uploadFolder')}/${siteName}/${prefix}${file.name}`)) {
prefix = getPrefix() prefix = getPrefix()
} }
fs.rename(files.file.path, `./public/${siteName}/${prefix}${file.name}`, function(err) { fs.rename(files.file.path, `${config.get('uploadFolder')}/${siteName}/${prefix}${file.name}`, function(err) {
if (err) return rej(err) if (err) return rej(err)
file.path = `./public/${siteName}/${prefix}${file.name}` file.path = `${config.get('uploadFolder')}/${siteName}/${prefix}${file.name}`
file.filename = `${prefix}${file.name}` file.filename = `${prefix}${file.name}`
return res(file) return res(file)

View file

@ -1,7 +1,8 @@
import path from 'path' import path from 'path'
import sharp from 'sharp' import sharp from 'sharp'
import fs from 'fs/promises' import fs from 'fs/promises'
import { HttpError } from '../error.mjs' import config from '../config.mjs'
import { HttpError } from 'flaska'
import * as security from './security.mjs' import * as security from './security.mjs'
import * as formidable from './formidable.mjs' import * as formidable from './formidable.mjs'
@ -17,13 +18,27 @@ export default class MediaRoutes {
this.collator = new Intl.Collator('is-IS', { numeric: false, sensitivity: 'accent' }) this.collator = new Intl.Collator('is-IS', { numeric: false, sensitivity: 'accent' })
} }
register(server) {
this.init().then(function() {}, function(err) {
server.core.log.error(err, 'Error initing media')
})
server.flaska.get('/media', [server.queryHandler()], this.listFiles.bind(this))
server.flaska.get('/media/:site', this.listPublicFiles.bind(this))
server.flaska.post('/media', [server.queryHandler()], this.upload.bind(this))
server.flaska.post('/media/noprefix', [server.queryHandler()], this.uploadNoPrefix.bind(this))
server.flaska.post('/media/resize', [server.queryHandler()], this.resize.bind(this))
server.flaska.post('/media/resize/:filename', [server.queryHandler(), server.jsonHandler()], this.resizeExisting.bind(this))
server.flaska.delete('/media/:filename', [server.queryHandler()], this.remove.bind(this))
}
init() { init() {
return fs.readdir('./public').then(folders => { return fs.readdir(config.get('uploadFolder')).then(folders => {
return Promise.all(folders.map(folder => { return Promise.all(folders.map(folder => {
return fs.readdir('./public/' + folder) return fs.readdir(config.get('uploadFolder') + '/' + folder)
.then(files => { .then(files => {
return Promise.all(files.map(file => { return Promise.all(files.map(file => {
return fs.stat(`./public/${folder}/${file}`) return fs.stat(`${config.get('uploadFolder')}/${folder}/${file}`)
.then(function(stat) { .then(function(stat) {
return { filename: file, size: stat.size } return { filename: file, size: stat.size }
}) })
@ -94,7 +109,7 @@ export default class MediaRoutes {
ctx.log.info(`Uploaded ${result.filename}`) ctx.log.info(`Uploaded ${result.filename}`)
let stat = await this.fs.stat(`./public/${ctx.state.site}/${result.filename}`) let stat = await this.fs.stat(`${config.get('uploadFolder')}/${ctx.state.site}/${result.filename}`)
this.filesCacheAdd(ctx.state.site, result.filename, stat.size) this.filesCacheAdd(ctx.state.site, result.filename, stat.size)
ctx.body = { ctx.body = {
@ -124,7 +139,7 @@ export default class MediaRoutes {
return Promise.resolve() return Promise.resolve()
.then(async () => { .then(async () => {
let item = ctx.req.body[key] let item = ctx.req.body[key]
let sharp = this.sharp(`./public/${ctx.state.site}/${sourceFile}`) let sharp = this.sharp(`${config.get('uploadFolder')}/${ctx.state.site}/${sourceFile}`)
.rotate() .rotate()
for (let operation of allowedOperations) { for (let operation of allowedOperations) {
@ -147,9 +162,9 @@ export default class MediaRoutes {
} }
return return
} }
await sharp.toFile(`./public/${ctx.state.site}/${target}`) await sharp.toFile(`${config.get('uploadFolder')}/${ctx.state.site}/${target}`)
let stat = await this.fs.stat(`./public/${ctx.state.site}/${target}`) let stat = await this.fs.stat(`${config.get('uploadFolder')}/${ctx.state.site}/${target}`)
this.filesCacheAdd(ctx.state.site, target, stat.size) this.filesCacheAdd(ctx.state.site, target, stat.size)
ctx.body[key] = { ctx.body[key] = {
@ -159,7 +174,7 @@ export default class MediaRoutes {
}).then( }).then(
function() {}, function() {},
function(err) { function(err) {
throw new HttpError(`Error processing ${key}: ${err.message}`, 422) throw new HttpError(422, `Error processing ${key}: ${err.message}`)
} }
) )
})) }))
@ -184,9 +199,9 @@ export default class MediaRoutes {
this.filesCacheRemove(site, ctx.params.filename) this.filesCacheRemove(site, ctx.params.filename)
await this.fs.unlink(`./public/${site}/${ctx.params.filename}`) await this.fs.unlink(`${config.get('uploadFolder')}/${site}/${ctx.params.filename}`)
.catch(function(err) { .catch(function(err) {
throw new HttpError(`Error removing ${site}/${ctx.params.filename}: ${err.message}`, 422) throw new HttpError(422, `Error removing ${site}/${ctx.params.filename}: ${err.message}`)
}) })
ctx.status = 204 ctx.status = 204

View file

@ -1,11 +1,11 @@
import { HttpError } from '../error.mjs' import { HttpError } from 'flaska'
import decode from '../jwt/decode.mjs' import decode from '../jwt/decode.mjs'
import config from '../config.mjs' import config from '../config.mjs'
export function verifyToken(ctx) { export function verifyToken(ctx) {
let token = ctx.query.get('token') let token = ctx.query.get('token')
if (!token) { if (!token) {
throw new HttpError('Token is missing in query', 422) throw new HttpError(422, 'Token is missing in query')
} }
let org = config.get('sites') let org = config.get('sites')
@ -21,14 +21,14 @@ export function verifyToken(ctx) {
return decoded.iss return decoded.iss
} catch (err) { } catch (err) {
ctx.log.error(err, 'Error decoding token: ' + token) ctx.log.error(err, 'Error decoding token: ' + token)
throw new HttpError('Token was invalid', 422) throw new HttpError(422, 'Token was invalid')
} }
} }
export function throwIfNotPublic(site) { export function throwIfNotPublic(site) {
let sites = config.get('sites') let sites = config.get('sites')
if (!sites[site] || sites[site].public !== true) { if (!sites[site] || sites[site].public !== true) {
throw new HttpError(`Requested site ${site} did not exist`, 404) throw new HttpError(404, `Requested site ${site} did not exist`)
} }
} }
@ -48,34 +48,34 @@ export function verifyBody(ctx) {
for (let key of keys) { for (let key of keys) {
if (key === 'filename' || key === 'path') { if (key === 'filename' || key === 'path') {
throw new HttpError('Body item with name filename or path is not allowed', 422) throw new HttpError(422, 'Body item with name filename or path is not allowed')
} }
let item = ctx.req.body[key] let item = ctx.req.body[key]
if (typeof(item) !== 'object' if (typeof(item) !== 'object'
|| !item || !item
|| Array.isArray(item)) { || Array.isArray(item)) {
throw new HttpError(`Body item ${key} was not valid`, 422) throw new HttpError(422, `Body item ${key} was not valid`)
} }
if (typeof(item.format) !== 'string' if (typeof(item.format) !== 'string'
|| !item.format || !item.format
|| validObjectOperations.includes(item.format) || validObjectOperations.includes(item.format)
|| item.format === 'out') { || item.format === 'out') {
throw new HttpError(`Body item ${key} missing valid format`, 422) throw new HttpError(422, `Body item ${key} missing valid format`)
} }
if (typeof(item[item.format]) !== 'object' if (typeof(item[item.format]) !== 'object'
|| !item[item.format] || !item[item.format]
|| Array.isArray(item[item.format])) { || Array.isArray(item[item.format])) {
throw new HttpError(`Body item ${key} options for format ${item.format} was not valid`, 422) throw new HttpError(422, `Body item ${key} options for format ${item.format} was not valid`)
} }
if (item.out != null) { if (item.out != null) {
if (typeof(item.out) !== 'string' if (typeof(item.out) !== 'string'
|| (item.out !== '' && item.out !== 'file' && item.out !== 'base64') || (item.out !== '' && item.out !== 'file' && item.out !== 'base64')
) { ) {
throw new HttpError(`Body item ${key} key out was invalid`, 422) throw new HttpError(422, `Body item ${key} key out was invalid`)
} }
} }
@ -83,7 +83,7 @@ export function verifyBody(ctx) {
if (item[operation] != null) { if (item[operation] != null) {
if (typeof(item[operation]) !== 'object' if (typeof(item[operation]) !== 'object'
|| Array.isArray(item[operation])) { || Array.isArray(item[operation])) {
throw new HttpError(`Body item ${key} key ${operation} was invalid`, 422) throw new HttpError(422, `Body item ${key} key ${operation} was invalid`)
} }
} }
} }
@ -91,7 +91,7 @@ export function verifyBody(ctx) {
for (let operation of validNumberOperations) { for (let operation of validNumberOperations) {
if (item[operation] != null) { if (item[operation] != null) {
if (typeof(item[operation]) !== 'number') { if (typeof(item[operation]) !== 'number') {
throw new HttpError(`Body item ${key} key ${operation} was invalid`, 422) throw new HttpError(422, `Body item ${key} key ${operation} was invalid`)
} }
} }
} }

View file

@ -5,82 +5,97 @@ import TestRoutes from './test/routes.mjs'
import MediaRoutes from './media/routes.mjs' import MediaRoutes from './media/routes.mjs'
import config from './config.mjs' import config from './config.mjs'
import log from './log.mjs'
const app = new Flaska({ export default class Server {
log: log, constructor(http, port, core, opts = {}) {
}) Object.assign(this, opts)
this.http = http
this.port = port
this.core = core
app.before(function(ctx) { this.jsonHandler = JsonHandler
ctx.__started = performance.now() this.queryHandler = QueryHandler
ctx.log = ctx.log.child({
ip: ctx.req.headers['x-forwarded-for'] || ctx.req.connection.remoteAddress,
})
})
app.after(function(ctx) { this.flaskOptions = {
let ended = performance.now() - ctx.__started log: this.core.log,
let status = ''
let level = 'info'
if (ctx.status >= 400) {
status = ctx.status + ' '
level = 'warn'
}
if (ctx.status >= 500) {
level = 'error'
}
ctx.log[level]({
duration: Math.round(ended),
status: ctx.status,
}, `<-- ${status}${ctx.method} ${ctx.url}`)
})
app.onerror(function(err, ctx) {
if (err.status && err.status >= 400 && err.status < 500) {
if (err.body && err.body.request) {
ctx.log.warn({ request: err.body.request}, err.message)
} else {
ctx.log.warn(err.message)
} }
} else {
ctx.log.error(err)
}
ctx.status = err.status || 500
if (err instanceof HttpError) { this.routes = {
ctx.body = err.body || { test: new TestRoutes(),
status: ctx.status, media: new MediaRoutes(),
message: err.message,
}
} else {
ctx.body = {
status: ctx.status,
message: err.message,
} }
} }
})
const test = new TestRoutes() run() {
app.get('/', test.static.bind(test)) // Create our server
app.get('/error', test.error.bind(test)) this.flaska = new Flaska(this.flaskOptions, this.http)
// configure our server
if (config.get('NODE_ENV') === 'development') {
this.flaska.devMode()
}
this.flaska.before(function(ctx) {
ctx.__started = performance.now()
ctx.log = ctx.log.child({
ip: ctx.req.headers['x-forwarded-for'] || ctx.req.connection.remoteAddress,
})
})
this.flaska.after(function(ctx) {
let ended = performance.now() - ctx.__started
let status = ''
let level = 'info'
if (ctx.status >= 400) {
status = ctx.status + ' '
level = 'warn'
}
if (ctx.status >= 500) {
level = 'error'
}
ctx.log[level]({
duration: Math.round(ended),
status: ctx.status,
}, `<-- ${status}${ctx.method} ${ctx.url}`)
})
this.flaska.onerror(function(err, ctx) {
if (err.status && err.status >= 400 && err.status < 500) {
if (err.body && err.body.request) {
ctx.log.warn({ request: err.body.request}, err.message)
} else {
ctx.log.warn(err.message)
}
} else {
ctx.log.error(err)
}
ctx.status = err.status || 500
if (err instanceof HttpError) {
ctx.body = err.body || {
status: ctx.status,
message: err.message,
}
} else {
ctx.body = {
status: ctx.status,
message: err.message,
}
}
})
const media = new MediaRoutes() // Register our routes
media.init().then(function() {}, function(err) { let keys = Object.keys(this.routes)
log.error(err, 'Error initing media') for (let key of keys) {
}) this.routes[key].register(this)
app.get('/media', [QueryHandler()], media.listFiles.bind(media)) }
app.get('/media/:site', media.listPublicFiles.bind(media))
app.post('/media', [QueryHandler()], media.upload.bind(media))
app.post('/media/noprefix', [QueryHandler()], media.uploadNoPrefix.bind(media))
app.post('/media/resize', [QueryHandler()], media.resize.bind(media))
app.post('/media/resize/:filename', [QueryHandler(), JsonHandler()], media.resizeExisting.bind(media))
app.delete('/media/:filename', [QueryHandler()], media.remove.bind(media))
app.listen(config.get('server:port'), function(a,b) { // Start listening
log.info(`Server listening at ${config.get('server:port')}`)
})
export default app return this.flaska.listenAsync(this.port).then(() => {
this.core.log.info(`Server is listening on port ${this.port} uploading to ${config.get('uploadFolder')}`)
})
}
}

View file

@ -5,6 +5,11 @@ export default class TestRoutes {
Object.assign(this, { }) Object.assign(this, { })
} }
register(server) {
server.flaska.get('/', this.static.bind(this))
server.flaska.get('/error', this.error.bind(this))
}
static(ctx) { static(ctx) {
ctx.body = { ctx.body = {
name: config.get('name'), name: config.get('name'),

View file

@ -1,23 +0,0 @@
{
"bunyan": {
"name": "storage-upload-test",
"streams": [{
"stream": "process.stdout",
"level": "error"
}
]
},
"sites": {
"development": {
"keys": {
"default@HS256": "asdf1234"
}
},
"existing": {
"public": true,
"keys": {
"default@HS256": "asdf1234"
}
}
}
}

34
dev.mjs Normal file
View file

@ -0,0 +1,34 @@
import fs from 'fs'
import { ServiceCore } from 'service-core'
import * as index from './index.mjs'
const port = 4040
var core = new ServiceCore('storage-upload', import.meta.url, port, '')
let config = {
"sites": {
"development": {
"keys": {
"default@HS256": "asdf1234"
}
},
"existing": {
"public": true,
"keys": {
"default@HS256": "asdf1234"
}
}
}
}
try {
config = JSON.parse(fs.readFileSync('./config.json'))
} catch {}
config.port = port
core.setConfig(config)
core.init(index).then(function() {
return core.run()
})

11
index.mjs Normal file
View file

@ -0,0 +1,11 @@
import config from './api/config.mjs'
export function start(http, port, ctx) {
config.sources[1].store = ctx.config
return import('./api/server.mjs')
.then(function(module) {
let server = new module.default(http, port, ctx)
return server.run()
})
}

View file

@ -8,9 +8,20 @@
"start:bunyan": "node api/server.mjs | bunyan", "start:bunyan": "node api/server.mjs | bunyan",
"test": "set NODE_ENV=test&& eltro test/**/*.test.mjs -r dot", "test": "set NODE_ENV=test&& eltro test/**/*.test.mjs -r dot",
"test:linux": "NODE_ENV=test eltro 'test/**/*.test.mjs' -r dot", "test:linux": "NODE_ENV=test eltro 'test/**/*.test.mjs' -r dot",
"test:watch": "npm-watch test" "test:watch": "npm-watch test",
"dev": "npm-watch dev:server",
"dev:server": "node dev.mjs | bunyan"
}, },
"watch": { "watch": {
"dev:server": {
"patterns": [
"api/*",
"{index,dev}.mjs"
],
"extensions": "js,mjs",
"quiet": true,
"inherit": true
},
"test": { "test": {
"patterns": [ "patterns": [
"{api,test}/*" "{api,test}/*"
@ -31,13 +42,13 @@
}, },
"homepage": "https://github.com/nfp-projects/storage-upload#readme", "homepage": "https://github.com/nfp-projects/storage-upload#readme",
"dependencies": { "dependencies": {
"bunyan-lite": "^1.2.0",
"flaska": "^1.2.3", "flaska": "^1.2.3",
"formidable": "^1.2.2", "formidable": "^1.2.2",
"nconf-lite": "^2.0.0", "nconf-lite": "^2.0.0",
"sharp": "^0.30.3" "sharp": "^0.30.3"
}, },
"devDependencies": { "devDependencies": {
"eltro": "^1.2.3" "eltro": "^1.2.3",
"service-core": "^3.0.0"
} }
} }

27
test.js
View file

@ -1,27 +0,0 @@
const fs = require('fs');
const os = require('os');
const path = require('path');
const env = process.env;
const mkdirSync = function (dirPath) {
try {
fs.mkdirSync(dirPath, { recursive: true });
} catch (err) {
/* istanbul ignore next */
if (err.code !== 'EEXIST') {
throw err;
}
}
};
const cachePath = function () {
const npmCachePath = env.npm_config_cache || /* istanbul ignore next */
(env.APPDATA ? path.join(env.APPDATA, 'npm-cache') : path.join(os.homedir(), '.npm'));
mkdirSync(npmCachePath);
const libvipsCachePath = path.join(npmCachePath, '_libvips');
mkdirSync(libvipsCachePath);
return libvipsCachePath;
};
cachePath()

View file

@ -1 +0,0 @@
{"name":"storage-upload","hostname":"JonatanPC","pid":26596,"level":30,"path":"/","status":200,"ms":4,"msg":"Request finished","time":"2021-10-10T23:55:12.390Z","v":0}

View file

@ -1,29 +1,66 @@
import { stub } from 'eltro' import { stub } from 'eltro'
import { ServiceCore } from 'service-core'
import Client from './helper.client.mjs' import Client from './helper.client.mjs'
import defaults from '../api/defaults.mjs' import defaults from '../api/defaults.mjs'
import serv from '../api/server.mjs' import * as index from '../index.mjs'
serv.log = { export const port = 5030
export const log = {
log: stub(), log: stub(),
warn: stub(), warn: stub(),
info: stub(), info: stub(),
error: stub(), error: stub(),
child: stub(), child: stub(),
event: {
warn: stub(),
info: stub(),
error: stub(),
}
}
log.child.returns(log)
let serverRunning = false
export function startServer() {
if (serverRunning) return Promise.resolve()
serverRunning = true
var core = new ServiceCore('storage-upload', import.meta.url, port, '')
core.setConfig({
"port": port,
"sites": {
"development": {
"keys": {
"default@HS256": "asdf1234"
}
},
"existing": {
"public": true,
"keys": {
"default@HS256": "asdf1234"
}
}
},
})
core.log = log
return core.init(index).then(function() {
return core.run()
})
} }
serv.log.child.returns(serv.log)
export const server = serv
export function createClient() { export function createClient() {
return new Client() return new Client(port)
} }
export function resetLog() { export function resetLog() {
serv.log.log.reset() log.log.reset()
serv.log.info.reset() log.info.reset()
serv.log.warn.reset() log.warn.reset()
serv.log.error.reset() log.error.reset()
} }
export function createContext(opts) { export function createContext(opts) {

View file

@ -4,8 +4,7 @@ import fs from 'fs/promises'
import { fileURLToPath } from 'url' import { fileURLToPath } from 'url'
import path from 'path' import path from 'path'
import { server, resetLog } from '../helper.server.mjs' import { createClient, startServer, log, resetLog } from '../helper.server.mjs'
import Client from '../helper.client.mjs'
import encode from '../../api/jwt/encode.mjs' import encode from '../../api/jwt/encode.mjs'
let __dirname = path.dirname(fileURLToPath(import.meta.url)) let __dirname = path.dirname(fileURLToPath(import.meta.url))
@ -17,10 +16,15 @@ function resolve(file) {
const currYear = new Date().getFullYear().toString() const currYear = new Date().getFullYear().toString()
t.describe('Media (API)', () => { t.describe('Media (API)', () => {
const client = new Client() let client
const secret = 'asdf1234' let secret = 'asdf1234'
let testFiles = [] let testFiles = []
t.before(function() {
client = createClient()
return startServer()
})
t.after(function() { t.after(function() {
return Promise.all(testFiles.map(function(file) { return Promise.all(testFiles.map(function(file) {
return fs.unlink(resolve(`../../public/${file}`)).catch(function() {}) return fs.unlink(resolve(`../../public/${file}`)).catch(function() {})
@ -33,8 +37,8 @@ t.describe('Media (API)', () => {
t.timeout(10000).describe('POST /media', function temp() { t.timeout(10000).describe('POST /media', function temp() {
t.test('should require authentication', async () => { t.test('should require authentication', async () => {
resetLog() resetLog()
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(log.warn.callCount, 0)
let err = await assert.isRejected( let err = await assert.isRejected(
client.upload('/media', client.upload('/media',
resolve('test.png') resolve('test.png')
@ -45,19 +49,19 @@ t.describe('Media (API)', () => {
assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Tt]oken/)
assert.match(err.message, /[Mm]issing/) assert.match(err.message, /[Mm]issing/)
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(server.log.warn.firstCall[0], /[Mm]issing/) assert.match(log.warn.firstCall[0], /[Mm]issing/)
}) })
t.test('should verify token correctly', async () => { t.test('should verify token correctly', async () => {
const assertToken = 'asdf.asdf.asdf' const assertToken = 'asdf.asdf.asdf'
resetLog() resetLog()
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(log.warn.callCount, 0)
assert.strictEqual(server.log.info.callCount, 0) assert.strictEqual(log.info.callCount, 0)
let err = await assert.isRejected( let err = await assert.isRejected(
client.upload('/media?token=' + assertToken, client.upload('/media?token=' + assertToken,
@ -69,13 +73,13 @@ t.describe('Media (API)', () => {
assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Tt]oken/)
assert.match(err.message, /[Ii]nvalid/) assert.match(err.message, /[Ii]nvalid/)
assert.strictEqual(server.log.error.callCount, 1) assert.strictEqual(log.error.callCount, 1)
assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(server.log.warn.firstCall[0], /[Ii]nvalid/) assert.match(log.warn.firstCall[0], /[Ii]nvalid/)
assert.ok(server.log.error.lastCall[0] instanceof Error) assert.ok(log.error.lastCall[0] instanceof Error)
assert.match(server.log.error.lastCall[1], new RegExp(assertToken)) assert.match(log.error.lastCall[1], new RegExp(assertToken))
}) })
t.test('should upload file and create file', async () => { t.test('should upload file and create file', async () => {
@ -111,8 +115,8 @@ t.describe('Media (API)', () => {
t.timeout(10000).describe('POST /media/noprefix', function temp() { t.timeout(10000).describe('POST /media/noprefix', function temp() {
t.test('should require authentication', async () => { t.test('should require authentication', async () => {
resetLog() resetLog()
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(log.warn.callCount, 0)
let err = await assert.isRejected( let err = await assert.isRejected(
client.upload('/media/noprefix', client.upload('/media/noprefix',
resolve('test.png') resolve('test.png')
@ -123,19 +127,19 @@ t.describe('Media (API)', () => {
assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Tt]oken/)
assert.match(err.message, /[Mm]issing/) assert.match(err.message, /[Mm]issing/)
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(server.log.warn.firstCall[0], /[Mm]issing/) assert.match(log.warn.firstCall[0], /[Mm]issing/)
}) })
t.test('should verify token correctly', async () => { t.test('should verify token correctly', async () => {
const assertToken = 'asdf.asdf.asdf' const assertToken = 'asdf.asdf.asdf'
resetLog() resetLog()
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(log.warn.callCount, 0)
assert.strictEqual(server.log.info.callCount, 0) assert.strictEqual(log.info.callCount, 0)
let err = await assert.isRejected( let err = await assert.isRejected(
client.upload('/media/noprefix?token=' + assertToken, client.upload('/media/noprefix?token=' + assertToken,
@ -147,13 +151,13 @@ t.describe('Media (API)', () => {
assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Tt]oken/)
assert.match(err.message, /[Ii]nvalid/) assert.match(err.message, /[Ii]nvalid/)
assert.strictEqual(server.log.error.callCount, 1) assert.strictEqual(log.error.callCount, 1)
assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(server.log.warn.firstCall[0], /[Ii]nvalid/) assert.match(log.warn.firstCall[0], /[Ii]nvalid/)
assert.ok(server.log.error.lastCall[0] instanceof Error) assert.ok(log.error.lastCall[0] instanceof Error)
assert.match(server.log.error.lastCall[1], new RegExp(assertToken)) assert.match(log.error.lastCall[1], new RegExp(assertToken))
}) })
t.test('should upload and create file with no prefix', async () => { t.test('should upload and create file with no prefix', async () => {
@ -227,8 +231,8 @@ t.describe('Media (API)', () => {
t.timeout(10000).describe('POST /media/resize', function temp() { t.timeout(10000).describe('POST /media/resize', function temp() {
t.test('should require authentication', async () => { t.test('should require authentication', async () => {
resetLog() resetLog()
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(log.warn.callCount, 0)
let err = await assert.isRejected( let err = await assert.isRejected(
client.upload('/media/resize', client.upload('/media/resize',
resolve('test.png') resolve('test.png')
@ -239,19 +243,19 @@ t.describe('Media (API)', () => {
assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Tt]oken/)
assert.match(err.message, /[Mm]issing/) assert.match(err.message, /[Mm]issing/)
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(server.log.warn.firstCall[0], /[Mm]issing/) assert.match(log.warn.firstCall[0], /[Mm]issing/)
}) })
t.test('should verify token correctly', async () => { t.test('should verify token correctly', async () => {
const assertToken = 'asdf.asdf.asdf' const assertToken = 'asdf.asdf.asdf'
resetLog() resetLog()
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(log.warn.callCount, 0)
assert.strictEqual(server.log.info.callCount, 0) assert.strictEqual(log.info.callCount, 0)
let err = await assert.isRejected( let err = await assert.isRejected(
client.upload('/media/resize?token=' + assertToken, client.upload('/media/resize?token=' + assertToken,
@ -263,13 +267,13 @@ t.describe('Media (API)', () => {
assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Tt]oken/)
assert.match(err.message, /[Ii]nvalid/) assert.match(err.message, /[Ii]nvalid/)
assert.strictEqual(server.log.error.callCount, 1) assert.strictEqual(log.error.callCount, 1)
assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(server.log.warn.firstCall[0], /[Ii]nvalid/) assert.match(log.warn.firstCall[0], /[Ii]nvalid/)
assert.ok(server.log.error.lastCall[0] instanceof Error) assert.ok(log.error.lastCall[0] instanceof Error)
assert.match(server.log.error.lastCall[1], new RegExp(assertToken)) assert.match(log.error.lastCall[1], new RegExp(assertToken))
}) })
t.test('should upload file and create file', async () => { t.test('should upload file and create file', async () => {
@ -458,8 +462,8 @@ t.describe('Media (API)', () => {
t.test('should require authentication', async () => { t.test('should require authentication', async () => {
resetLog() resetLog()
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(log.warn.callCount, 0)
let err = await assert.isRejected( let err = await assert.isRejected(
client.post(`/media/resize/${sourceFilename}`, {}) client.post(`/media/resize/${sourceFilename}`, {})
) )
@ -468,19 +472,19 @@ t.describe('Media (API)', () => {
assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Tt]oken/)
assert.match(err.message, /[Mm]issing/) assert.match(err.message, /[Mm]issing/)
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(server.log.warn.firstCall[0], /[Mm]issing/) assert.match(log.warn.firstCall[0], /[Mm]issing/)
}) })
t.test('should verify token correctly', async () => { t.test('should verify token correctly', async () => {
const assertToken = 'asdf.asdf.asdf' const assertToken = 'asdf.asdf.asdf'
resetLog() resetLog()
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(log.warn.callCount, 0)
assert.strictEqual(server.log.info.callCount, 0) assert.strictEqual(log.info.callCount, 0)
let err = await assert.isRejected( let err = await assert.isRejected(
client.post(`/media/resize/${sourceFilename}?token=${assertToken}`, {}) client.post(`/media/resize/${sourceFilename}?token=${assertToken}`, {})
@ -490,13 +494,13 @@ t.describe('Media (API)', () => {
assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Tt]oken/)
assert.match(err.message, /[Ii]nvalid/) assert.match(err.message, /[Ii]nvalid/)
assert.strictEqual(server.log.error.callCount, 1) assert.strictEqual(log.error.callCount, 1)
assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(server.log.warn.firstCall[0], /[Ii]nvalid/) assert.match(log.warn.firstCall[0], /[Ii]nvalid/)
assert.ok(server.log.error.lastCall[0] instanceof Error) assert.ok(log.error.lastCall[0] instanceof Error)
assert.match(server.log.error.lastCall[1], new RegExp(assertToken)) assert.match(log.error.lastCall[1], new RegExp(assertToken))
}) })
t.test('should create multiple sizes for existing file', async () => { t.test('should create multiple sizes for existing file', async () => {
@ -596,8 +600,8 @@ t.describe('Media (API)', () => {
t.timeout(10000).describe('DELETE /media/:filename', function temp() { t.timeout(10000).describe('DELETE /media/:filename', function temp() {
t.test('should require authentication', async () => { t.test('should require authentication', async () => {
resetLog() resetLog()
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(log.warn.callCount, 0)
let err = await assert.isRejected( let err = await assert.isRejected(
client.del('/media/20220105_101610_test1.jpg', client.del('/media/20220105_101610_test1.jpg',
resolve('test.png') resolve('test.png')
@ -608,19 +612,19 @@ t.describe('Media (API)', () => {
assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Tt]oken/)
assert.match(err.message, /[Mm]issing/) assert.match(err.message, /[Mm]issing/)
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(server.log.warn.firstCall[0], /[Mm]issing/) assert.match(log.warn.firstCall[0], /[Mm]issing/)
}) })
t.test('should verify token correctly', async () => { t.test('should verify token correctly', async () => {
const assertToken = 'asdf.asdf.asdf' const assertToken = 'asdf.asdf.asdf'
resetLog() resetLog()
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(log.warn.callCount, 0)
assert.strictEqual(server.log.info.callCount, 0) assert.strictEqual(log.info.callCount, 0)
let err = await assert.isRejected( let err = await assert.isRejected(
client.del('/media/20220105_101610_test1.jpg?token=' + assertToken, client.del('/media/20220105_101610_test1.jpg?token=' + assertToken,
@ -632,13 +636,13 @@ t.describe('Media (API)', () => {
assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Tt]oken/)
assert.match(err.message, /[Ii]nvalid/) assert.match(err.message, /[Ii]nvalid/)
assert.strictEqual(server.log.error.callCount, 1) assert.strictEqual(log.error.callCount, 1)
assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(server.log.warn.firstCall[0], /[Ii]nvalid/) assert.match(log.warn.firstCall[0], /[Ii]nvalid/)
assert.ok(server.log.error.lastCall[0] instanceof Error) assert.ok(log.error.lastCall[0] instanceof Error)
assert.match(server.log.error.lastCall[1], new RegExp(assertToken)) assert.match(log.error.lastCall[1], new RegExp(assertToken))
}) })
t.test('should remove the file', async () => { t.test('should remove the file', async () => {
@ -691,27 +695,27 @@ t.describe('Media (API)', () => {
t.describe('GET /media', function() { t.describe('GET /media', function() {
t.test('should require authentication', async () => { t.test('should require authentication', async () => {
resetLog() resetLog()
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(log.warn.callCount, 0)
let err = await assert.isRejected(client.get('/media')) let err = await assert.isRejected(client.get('/media'))
assert.strictEqual(err.status, 422) assert.strictEqual(err.status, 422)
assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Tt]oken/)
assert.match(err.message, /[Mm]issing/) assert.match(err.message, /[Mm]issing/)
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(server.log.warn.firstCall[0], /[Mm]issing/) assert.match(log.warn.firstCall[0], /[Mm]issing/)
}) })
t.test('should verify token correctly', async () => { t.test('should verify token correctly', async () => {
const assertToken = 'asdf.asdf.asdf' const assertToken = 'asdf.asdf.asdf'
resetLog() resetLog()
assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(log.warn.callCount, 0)
assert.strictEqual(server.log.info.callCount, 0) assert.strictEqual(log.info.callCount, 0)
let err = await assert.isRejected(client.get('/media?token=' + assertToken)) let err = await assert.isRejected(client.get('/media?token=' + assertToken))
@ -719,13 +723,13 @@ t.describe('Media (API)', () => {
assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Tt]oken/)
assert.match(err.message, /[Ii]nvalid/) assert.match(err.message, /[Ii]nvalid/)
assert.strictEqual(server.log.error.callCount, 1) assert.strictEqual(log.error.callCount, 1)
assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(server.log.warn.firstCall[0], /[Ii]nvalid/) assert.match(log.warn.firstCall[0], /[Ii]nvalid/)
assert.ok(server.log.error.lastCall[0] instanceof Error) assert.ok(log.error.lastCall[0] instanceof Error)
assert.match(server.log.error.lastCall[1], new RegExp(assertToken)) assert.match(log.error.lastCall[1], new RegExp(assertToken))
}) })
t.test('should return list of files in specified folder', async () => { t.test('should return list of files in specified folder', async () => {

View file

@ -1,9 +1,9 @@
import fs from 'fs/promises' import fs from 'fs/promises'
import { Eltro as t, assert, stub } from 'eltro' import { Eltro as t, assert, stub } from 'eltro'
import { HttpError } from 'flaska'
import { createContext } from '../helper.server.mjs' import { createContext } from '../helper.server.mjs'
import MediaRoutes from '../../api/media/routes.mjs' import MediaRoutes from '../../api/media/routes.mjs'
import { HttpError } from '../../api/error.mjs'
t.before(function() { t.before(function() {
return Promise.all([ return Promise.all([

View file

@ -1,23 +1,32 @@
import { Eltro as t, assert} from 'eltro' import { Eltro as t, assert} from 'eltro'
import { HttpError } from 'flaska'
import { createContext } from '../helper.server.mjs' import { createContext } from '../helper.server.mjs'
import { verifyToken, verifyBody, throwIfNotPublic } from '../../api/media/security.mjs' import { verifyToken, verifyBody, throwIfNotPublic } from '../../api/media/security.mjs'
import { HttpError } from '../../api/error.mjs'
import encode from '../../api/jwt/encode.mjs' import encode from '../../api/jwt/encode.mjs'
import config from '../../api/config.mjs' import config from '../../api/config.mjs'
t.describe('#throwIfNotPublic()', function() { t.describe('#throwIfNotPublic()', function() {
let backup = {}
t.before(function() { t.before(function() {
config.set('sites', { backup = config.sources[1].store
justatest: { config.sources[1].store = {
sites: {
justatest: {
},
justatest2: {
public: false,
},
justatest3: {
public: true,
},
}, },
justatest2: { }
public: false, })
},
justatest3: { t.after(function() {
public: true, config.sources[1].store = backup
},
})
}) })
t.test('should throw for sites that do not exist or are null', function() { t.test('should throw for sites that do not exist or are null', function() {
@ -53,14 +62,22 @@ t.describe('#throwIfNotPublic()', function() {
}) })
t.describe('#verifyToken()', function() { t.describe('#verifyToken()', function() {
let backup = {}
t.before(function() { t.before(function() {
config.set('sites', { backup = config.sources[1].store
justatest: { config.sources[1].store = {
keys: { sites: {
'default@HS512': 'mysharedkey', justatest: {
} keys: {
'default@HS512': 'mysharedkey',
}
},
}, },
}) }
})
t.after(function() {
config.sources[1].store = backup
}) })
t.test('should fail if query token is missing', function() { t.test('should fail if query token is missing', function() {

View file

@ -1,39 +1,41 @@
import { Eltro as t, assert} from 'eltro' import { Eltro as t, assert} from 'eltro'
import { createClient, server, resetLog } from './helper.server.mjs' import { createClient, startServer, log, resetLog } from './helper.server.mjs'
t.describe('Server', function() { t.describe('Server', function() {
let client let client
t.before(function() { t.before(function() {
client = createClient() client = createClient()
return startServer()
}) })
t.test('should run', async function() { t.test('should run', async function() {
resetLog() resetLog()
assert.strictEqual(server.log.info.callCount, 0) assert.strictEqual(log.info.callCount, 0)
let data = await client.get('/') let data = await client.get('/')
assert.ok(data) assert.ok(data)
assert.ok(data.name) assert.ok(data.name)
assert.ok(data.version) assert.ok(data.version)
assert.strictEqual(server.log.info.callCount, 1) assert.strictEqual(log.info.callCount, 1)
assert.strictEqual(server.log.info.lastCall[0].status, 200) assert.strictEqual(log.info.lastCall[0].status, 200)
assert.match(server.log.info.lastCall[1], /\<-- GET \//) assert.match(log.info.lastCall[1], /\<-- GET \//)
}) })
t.test('should handle errors fine', async function() { t.test('should handle errors fine', async function() {
resetLog() resetLog()
assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(log.warn.callCount, 0)
let data = await assert.isRejected(client.get('/error')) let data = await assert.isRejected(client.get('/error'))
assert.ok(data) assert.ok(data)
assert.ok(data.body) assert.ok(data.body)
assert.strictEqual(data.body.status, 500) assert.strictEqual(data.body.status, 500)
assert.match(data.body.message, /test/) assert.match(data.body.message, /test/)
assert.strictEqual(server.log.error.firstCall[0].message, 'This is a test') assert.strictEqual(log.error.firstCall[0].message, 'This is a test')
assert.strictEqual(server.log.error.callCount, 2) assert.strictEqual(log.error.callCount, 2)
assert.strictEqual(server.log.error.secondCall[0].status, 500) assert.strictEqual(log.error.secondCall[0].status, 500)
assert.match(server.log.error.secondCall[1], /\<-- 500 GET \/error/) assert.match(log.error.secondCall[1], /\<-- 500 GET \/error/)
}) })
}) })