import { Eltro as t, assert, stub } from 'eltro' import fs from 'fs/promises' import Application from '../core/application.mjs' import Util from '../core/util.mjs' import lowdb from '../core/db.mjs' import StaticProvider from '../core/providers/static.mjs' import { createFakeContext } from './helpers.mjs' const util = new Util(import.meta.url) t.describe('#runVersion("static")', function() { const assertPort = 22345 let ctx let app const defaultHandler = function(orgHandler) { let handler = orgHandler || function (req, res) { res.writeHead(200); res.end(JSON.stringify({ a: 1 })) } return function(http, port, ctx) { const server = http.createServer(handler) return server.listenAsync(port) } } t.beforeEach(function() { return createFakeContext() .then(function(res) { ctx = res let provider = new StaticProvider() app = new Application(ctx, provider, 'testapp') app.config.port = assertPort app.registerModule(defaultHandler()) }) }) t.afterEach(function() { return app.closeServer() }) t.test('should throw if http is not called', async function() { app.registerModule(function(checkHttp, checkPort, checkCtx) { assert.strictEqual(checkHttp, app.http) assert.strictEqual(checkPort, assertPort) assert.strictEqual(checkCtx.db, ctx.db) assert.strictEqual(checkCtx.log, ctx.log) assert.strictEqual(checkCtx.app, app) }) assert.strictEqual(app.ctx.version, '') assert.strictEqual(app.fresh, true) let err = await assert.isRejected(app.runVersion('static')) assert.strictEqual(app.fresh, false) assert.strictEqual(app.ctx.version, '') assert.match(err.message, /http/i) assert.match(err.message, /createServer/i) assert.match(err.message, /call/i) assert.strictEqual(ctx.db.data.core.testapp.active, 'static') }) t.test('should throw if it timeouts waiting for promise to succeed', async function() { app.config.startWaitUntilFail = 50 app.registerModule(function() { return new Promise(function() {}) }) assert.strictEqual(app.ctx.version, '') assert.strictEqual(app.fresh, true) let err = await assert.isRejected(app.runVersion('static')) assert.strictEqual(app.fresh, false) assert.strictEqual(app.ctx.version, '') assert.match(err.message, /time/i) assert.match(err.message, /out/i) assert.match(err.message, /50ms/) assert.strictEqual(ctx.db.data.core.testapp.active, 'static') }) t.test('should otherwise succeed if it finished within the time limit', async function() { const handler = defaultHandler() app.config.startWaitUntilFail = 250 app.registerModule(function(http, port, ctx) { return new Promise(function(res) { setTimeout(res, 25) }).then(function() { return handler(http, port, ctx) }) }) assert.strictEqual(app.ctx.version, '') assert.strictEqual(app.fresh, true) await app.runVersion('static') assert.strictEqual(app.fresh, false) assert.strictEqual(app.ctx.version, 'static') assert.strictEqual(ctx.db.data.core.testapp.active, 'static') }) t.test('should fail if run succeeds but heartbeat errors', async function() { let called = 0 const handler = function(req, res) { called++ res.statusCode = 400 res.end(JSON.stringify({ a: 1 })) } app.config.heartbeatAttempts = 3 app.config.heartbeatAttemptsWait = 5 app.registerModule(defaultHandler(handler)) assert.strictEqual(app.ctx.version, '') assert.strictEqual(app.fresh, true) let err = await assert.isRejected(app.runVersion('static')) assert.strictEqual(app.fresh, false) assert.strictEqual(app.ctx.version, '') assert.match(err.message, /failed/i) assert.match(err.message, /400/i) assert.strictEqual(called, 3) assert.strictEqual(ctx.db.data.core.testapp.active, 'static') }) t.test('should fail if run succeeds but heartbeat times out', async function() { let called = 0 const handler = function(req, res) { called++ } app.config.heartbeatAttempts = 2 app.config.heartbeatTimeout = 30 app.config.heartbeatAttemptsWait = 10 app.registerModule(defaultHandler(handler)) assert.strictEqual(app.ctx.version, '') assert.strictEqual(app.fresh, true) let start = performance.now() let err = await assert.isRejected(app.runVersion('static')) let end = performance.now() assert.strictEqual(app.fresh, false) assert.strictEqual(app.ctx.version, '') assert.match(err.message, /failed/i) assert.match(err.message, /time/i) assert.match(err.message, /out/i) assert.match(err.message, /30ms/i) assert.ok(end - start > app.config.heartbeatAttempts * (app.config.heartbeatTimeout + app.config.heartbeatAttemptsWait)) assert.strictEqual(called, 2) assert.strictEqual(ctx.db.data.core.testapp.active, 'static') }) t.test('should call with correct path', async function() { const assertPath = '/test/something' const handler = function(req, res) { if (req.url === assertPath) { res.writeHead(204); res.end(JSON.stringify({ a: 1 })) } else { res.statusCode = 400 res.end(JSON.stringify({ a: 1 })) } } app.config.heartbeatAttempts = 3 app.config.heartbeatAttemptsWait = 5 app.registerModule(defaultHandler(handler)) assert.strictEqual(app.ctx.version, '') let err = await assert.isRejected(app.runVersion('static')) assert.match(err.message, /failed/i) assert.match(err.message, /400/i) assert.strictEqual(app.ctx.version, '') await app.closeServer() app.registerModule(defaultHandler(handler)) app.config.heartbeatPath = assertPath await app.runVersion('static') assert.strictEqual(ctx.db.data.core.testapp.active, 'static') }) t.test('should not check heartbeat if slave', async function() { let called = 0 const handler = function(req, res) { called++ res.statusCode = 400 res.end(JSON.stringify({ a: 1 })) } app.registerModule(defaultHandler(handler)) app.isSlave = true assert.strictEqual(app.ctx.version, '') assert.strictEqual(app.fresh, true) await app.runVersion('static') assert.strictEqual(app.fresh, false) assert.strictEqual(app.ctx.version, 'static') assert.strictEqual(called, 0) assert.strictEqual(ctx.db.data.core.testapp.active, 'static') }) }) t.describe('#runVersion("version")', function() { const assertConfig = util.getPathFromRoot('./db_test_applicationrun.json') const assertPort = 22345 let ctx let app t.after(function() { return Promise.all([ fs.rm(util.getPathFromRoot('./testnoexisting/v100'), { force: true, recursive: true }), fs.rm(util.getPathFromRoot('./testnoexisting/v99'), { force: true, recursive: true }), fs.rm(util.getPathFromRoot('./testnoexisting/v98'), { force: true, recursive: true }), fs.rm(util.getPathFromRoot('./testnoexisting/v97'), { force: true, recursive: true }), ]) }) t.beforeEach(function() { return createFakeContext({ }, util, assertConfig) .then(function(res) { ctx = res let provider = new StaticProvider() app = new Application(ctx, provider, 'testnoexisting') app.config.port = assertPort return app.ctx.db.write() }) }) t.afterEach(function() { return Promise.all([ fs.rm(assertConfig), app.closeServer(), ]) }) t.test('when version is specified, should check if index.mjs exists', async function() { const assertNotError = new Error('AI DO') const assertTarget = util.getPathFromRoot('./testnoexisting/v100/index.mjs') let stubFsStat = stub() let provider = new StaticProvider() app = new Application(ctx, provider, 'testnoexisting', { fs: { stat: stubFsStat } }) app.config.port = assertPort stubFsStat.rejects(assertNotError) assert.strictEqual(app.ctx.version, '') assert.strictEqual(app.fresh, true) let err = await assert.isRejected(app.runVersion('v100')) assert.strictEqual(app.fresh, true) assert.strictEqual(app.ctx.version, '') assert.notStrictEqual(err, assertNotError) assert.match(err.message, new RegExp(assertNotError.message)) assert.match(err.message, /index\.mjs/i) assert.match(err.message, /missing/i) assert.strictEqual(stubFsStat.firstCall[0], assertTarget) assert.strictEqual(app.ctx.db.data.core.testnoexisting.active, 'v100') let checkDb = await lowdb({}, ctx.log, assertConfig) assert.strictEqual(checkDb.data.core.testnoexisting.active, 'v100') }) t.test('when version is specified and file exists, should attempt to load module', async function() { const assertError = new Error('Parallel Days') await fs.mkdir(util.getPathFromRoot('./testnoexisting/v99'), { recursive: true }) await fs.writeFile(util.getPathFromRoot('./testnoexisting/v99/index.mjs'), `throw new Error('${assertError.message}')`) assert.strictEqual(app.ctx.version, '') assert.strictEqual(app.fresh, true) let err = await assert.isRejected(app.runVersion('v99')) assert.strictEqual(app.fresh, false) assert.strictEqual(app.ctx.version, '') assert.notStrictEqual(err, assertError) assert.strictEqual(err.message, assertError.message) assert.strictEqual(app.ctx.db.data.core.testnoexisting.active, 'v99') let checkDb = await lowdb({}, ctx.log, assertConfig) assert.strictEqual(checkDb.data.core.testnoexisting.active, 'v99') }) t.test('when version is specified and file exists, should check if it has start', async function() { await fs.mkdir(util.getPathFromRoot('./testnoexisting/v98'), { recursive: true }) await fs.writeFile(util.getPathFromRoot('./testnoexisting/v98/index.mjs'), ``) assert.strictEqual(app.ctx.version, '') assert.strictEqual(app.fresh, true) let err = await assert.isRejected(app.runVersion('v98')) assert.strictEqual(app.fresh, false) assert.match(err.message, /start/i) assert.strictEqual(app.ctx.version, '') assert.strictEqual(app.ctx.db.data.core.testnoexisting.active, 'v98') let checkDb = await lowdb({}, ctx.log, assertConfig) assert.strictEqual(checkDb.data.core.testnoexisting.active, 'v98') }) t.test('when version is specified and file exists and everything is okay, should work normally', async function() { await fs.mkdir(util.getPathFromRoot('./testnoexisting/v97'), { recursive: true }) await fs.copyFile( util.getPathFromRoot('./exampleindex.mjs'), util.getPathFromRoot('./testnoexisting/v97/index.mjs') ) app.ctx.log.info.reset() app.ctx.log.event.info.reset() assert.strictEqual(app.ctx.version, '') assert.strictEqual(app.fresh, true) await app.runVersion('v97') assert.strictEqual(app.fresh, false) assert.strictEqual(app.ctx.version, 'v97') assert.ok(app.ctx.log.info.called) assert.ok(app.ctx.log.event.info.called) }) })