import { Eltro as t, assert, stub } from 'eltro' import fs from 'fs/promises' import Core from '../core/core.mjs' import Util from '../core/util.mjs' import { createFakeLog } from './helpers.mjs' import StaticProvider from '../core/providers/static.mjs' import lowdb from '../core/db.mjs' const util = new Util(import.meta.url) const log = createFakeLog() let db t.before(function() { return lowdb({}, log, null).then(function(res) { db = res }) }) t.describe('Core.addProvider()', function() { t.beforeEach(function() { Core.providers.clear() }) t.after(function() { Core.providers.clear() }) t.test('should fail if name is not a string', function() { let tests = [ [1, 'number'], [0, 'false number'], ['', 'false string'], [[], 'array'], [{}, 'object'], ] tests.forEach(function(check) { assert.throws(function() { Core.addProvider(check[0], StaticProvider) }, function(err) { assert.match(err.message, /name/i) assert.match(err.message, /string/i) return true }, `throw if name is ${check[1]}`) }) }) t.test('should fail if provider not a function', function() { let tests = [ [1, 'number'], [0, 'false number'], ['asdf', 'string'], ['', 'false string'], [[], 'array'], [{}, 'object'], ] tests.forEach(function(check) { assert.throws(function() { Core.addProvider('insertname', check[0]) }, function(err) { assert.match(err.message, /provider/i) assert.match(err.message, /class/i) assert.match(err.message, /insertname/i) return true }, `throw if provider is ${check[1]}`) }) }) t.test('should fail if provider instance is missing checkConfig', function() { let tests = [ [1, 'number'], [0, 'false number'], ['asdf', 'string'], ['', 'false string'], [[], 'array'], [{}, 'object'], ] tests.forEach(function(check) { assert.throws(function() { let provider = function() { this.getLatestVersion = function() {}; this.checkConfig = check[0] } Core.addProvider('somename', provider) }, function(err) { assert.match(err.message, /provider/i) assert.match(err.message, /class/i) assert.match(err.message, /missing/i) assert.match(err.message, /checkConfig/i) assert.match(err.message, /somename/i) return true }, `throw if provider checkConfig is ${check[1]}`) }) }) t.test('should fail if provider instance is missing getLatestVersion', function() { let tests = [ [1, 'number'], [0, 'false number'], ['asdf', 'string'], ['', 'false string'], [[], 'array'], [{}, 'object'], ] tests.forEach(function(check) { assert.throws(function() { let provider = function() { this.checkConfig = function() {}; this.getLatestVersion = check[0] } Core.addProvider('somename', provider) }, function(err) { assert.match(err.message, /provider/i) assert.match(err.message, /class/i) assert.match(err.message, /missing/i) assert.match(err.message, /getLatestVersion/i) assert.match(err.message, /somename/i) return true }, `throw if provider getLatestVersion is ${check[1]}`) }) }) t.test('should otherwise add provider to map', function() { assert.strictEqual(Core.providers.size, 0) Core.addProvider('testnamehere', StaticProvider) assert.strictEqual(Core.providers.size, 1) assert.strictEqual(Core.providers.get('testnamehere'), StaticProvider) }) }) t.describe('#constructor()', function() { t.test('should throw if close is not a function', function() { let tests = [ [1, 'number'], [0, 'false number'], ['asdf', 'string'], ['', 'false string'], [[], 'array'], [{}, 'object'], ] tests.forEach(function(check) { assert.throws(function() { new Core(db, util, log, check[0]) }, function(err) { assert.match(err.message, /restart/i) assert.match(err.message, /function/i) return true }, `throw if restart is ${check[1]}`) }) }) t.test('should throw if util is not util', function() { let tests = [ [1, 'number'], [0, 'false number'], ['asdf', 'string'], ['', 'false string'], [[], 'array'], [{}, 'object'], [Util, 'not instance'], ] tests.forEach(function(check) { assert.throws(function() { new Core(db, check[0], log, function() {}) }, function(err) { assert.match(err.message, /util/i) assert.match(err.message, /instance/i) return true }, `throw if util is ${check[1]}`) }) }) t.test('should throw if db is not lowdb', function() { let tests = [ [1, 'number'], [0, 'false number'], ['asdf', 'string'], ['', 'false string'], [[], 'array'], [{}, 'object'], [lowdb.Low, 'not instance'], ] tests.forEach(function(check) { assert.throws(function() { new Core(check[0], util, log, function() {}) }, function(err) { assert.match(err.message, /db/i) assert.match(err.message, /instance/i) return true }, `throw if db is ${check[1]}`) }) }) t.test('should throw if log is not an object with event', function() { let func = function() {} let validEvent = { info: func, warn: func, error: func } let tests = [ [1, 'number'], [0, 'false number'], [null, 'null'], [undefined, 'undefined'], ['asdf', 'string'], ['', 'false string'], [[], 'array'], [{}, 'object'], [{warn: func, event: validEvent }, 'log only warn'], [{error: func, event: validEvent }, 'log only error'], [{info: func, event: validEvent }, 'log only info'], [{warn: func, error: func, event: validEvent }, 'log only warn and error'], [{warn: func, info: func, event: validEvent }, 'log only warn and info'], [{error: func, info: func, event: validEvent }, 'log only error and info'], [{ warn: func, error: func, info: func, event: { warn: func } }, 'event only warn'], [{ warn: func, error: func, info: func, event: { error: func } }, 'event only error'], [{ warn: func, error: func, info: func, event: { info: func } }, 'event only info'], [{ warn: func, error: func, info: func, event: { warn: func, error: func } }, 'event only warn and error'], [{ warn: func, error: func, info: func, event: { warn: func, info: func } }, 'event only warn and info'], [{ warn: func, error: func, info: func, event: { error: func, info: func } }, 'event only error and info'], ] tests.forEach(function(check) { assert.throws(function() { new Core(db, util, check[0], func) }, function(err) { assert.match(err.message, /log/i) assert.match(err.message, /valid/i) return true }, `throw if log is ${check[1]}`) }) }) t.test('should accept log, util and close function', function() { const assertLog = log const assertClose = function() {} let core = new Core(db, util, assertLog, assertClose) assert.strictEqual(core.db, db) assert.strictEqual(core.util, util) assert.strictEqual(core.log, assertLog) assert.strictEqual(core.restart, assertClose) assert.deepStrictEqual(core.applications, []) assert.ok(core.applicationMap) }) }) t.describe('#getApplication()', function() { t.test('should return application based on the name', function() { const assertName = 'Yami no Naka' const assertApplication = { a: 1 } let core = new Core(db, util, log, function() {}) core.applicationMap.set(assertName, assertApplication) assert.strictEqual(core.getApplication(assertName), assertApplication) }) }) t.describe('#init()', function() { const assertProviderName = 'Kyousuu Gakku Gogyou Kikan' let core let fakeUtil let fakeProvider let fakeProviderConfig function FakeProvider(config) { fakeProvider(config) this.static = true this.checkConfig = fakeProviderConfig } t.beforeEach(function() { core = new Core(db, util, log, function() {}) core.util = fakeUtil = { verifyConfig: stub(), getAppNames: stub().returns([]), } fakeProvider = stub() fakeProviderConfig = stub() Core.providers.set(assertProviderName, FakeProvider) }) t.test('it should call util.verifyConfig correctly', async function() { const assertError = new Error('Red faction IO drive mix') const assertConfig = { a: 1 } db.config = assertConfig fakeUtil.verifyConfig.throws(assertError) let err = await assert.isRejected(core.init()) assert.strictEqual(err, assertError) assert.strictEqual(fakeUtil.verifyConfig.firstCall[0], assertConfig) }) t.test('should call util.getNames correctly', async function() { const assertError = new Error('Hero within') const assertConfig = { a: 1 } db.config = assertConfig fakeUtil.getAppNames.throws(assertError) let err = await assert.isRejected(core.init()) assert.strictEqual(err, assertError) assert.strictEqual(fakeUtil.getAppNames.firstCall[0], assertConfig) }) t.test('should call provider constructor correctly', async function() { const assertError = new Error('Funny days') const assertAppName = 'Tsuugakuro' const assertConfig = { [assertAppName]: { provider: assertProviderName, } } db.config = assertConfig fakeProvider.throws(assertError) fakeUtil.getAppNames.returns([assertAppName]) let err = await assert.isRejected(core.init()) assert.strictEqual(err, assertError) assert.strictEqual(fakeProvider.firstCall[0], assertConfig[assertAppName]) }) t.test('should call provider checkConfig correctly', async function() { const assertAppName = 'Zetsubou' const assertConfig = { [assertAppName]: { provider: assertProviderName, } } db.config = assertConfig const assertError = new Error('Shousou') fakeProviderConfig.rejects(assertError) fakeUtil.getAppNames.returns([assertAppName]) let err = await assert.isRejected(core.init()) assert.strictEqual(err, assertError) assert.strictEqual(fakeProviderConfig.firstCall[0], assertConfig[assertAppName]) }) t.test('should create an application with the provider and name and config', async function() { const assertAppName = 'Yasashii Ketsumatsu' const assertTestString = 'Serozore no Omoi' const assertConfig = { [assertAppName]: { provider: assertProviderName, teststring: assertTestString, } } db.config = assertConfig fakeUtil.getAppNames.returns([assertAppName]) assert.strictEqual(core.applications.length, 0) await core.init() let application = core.getApplication(assertAppName) assert.ok(application) assert.strictEqual(core.applications.length, 1) assert.strictEqual(core.applications[0], application) assert.strictEqual(application.name, assertAppName) assert.strictEqual(application.ctx.db, core.db) assert.strictEqual(application.ctx.util, core.util) assert.strictEqual(application.ctx.log, core.log) assert.strictEqual(application.ctx.core, core) assert.strictEqual(application.config.teststring, assertTestString) assert.ok(application.fresh) assert.ok(application.provider instanceof FakeProvider) }) t.test('should continue even if one application fails', async function() { log.error.reset() const assertProviderFailName = 'Knee Socks' let stubFailCheckConfig = stub() function FakeFailProvider(config) { fakeProvider(config) this.static = true this.checkConfig = stubFailCheckConfig } Core.providers.set(assertProviderFailName, FakeFailProvider) const assertError = new Error('Justice of light') const assertFirstAppName = 'Kaeshite' const assertSecondAppName = 'Motteke' const assertTestFirstString = 'Magia' const assertTestSecondString = 'Snow Falling' const assertConfig = { [assertFirstAppName]: { provider: assertProviderName, teststring: assertTestFirstString, }, [assertSecondAppName]: { provider: assertProviderFailName, teststring: assertTestSecondString, }, } stubFailCheckConfig.throws(assertError) db.config = assertConfig fakeUtil.getAppNames.returns([assertSecondAppName, assertFirstAppName]) assert.strictEqual(core.applications.length, 0) await core.init() let application = core.getApplication(assertFirstAppName) assert.ok(application) assert.strictEqual(core.applications.length, 1) assert.strictEqual(core.applications[0], application) assert.strictEqual(application.name, assertFirstAppName) assert.strictEqual(application.ctx.db, core.db) assert.strictEqual(application.ctx.util, core.util) assert.strictEqual(application.ctx.log, core.log) assert.strictEqual(application.ctx.core, core) assert.strictEqual(application.config.teststring, assertTestFirstString) assert.ok(application.fresh) assert.ok(application.provider instanceof FakeProvider) assert.ok(log.error.called) assert.strictEqual(log.error.firstCall[0], assertError) assert.match(log.error.firstCall[1], new RegExp(assertProviderFailName)) assert.match(log.error.firstCall[1], new RegExp(assertSecondAppName)) assert.match(log.error.firstCall[1], new RegExp(assertError.message)) }) }) t.only().describe('#runApplication()', function() { let core let testApp let testAppName t.beforeEach(function() { db.data.core = { [testAppName]: { versions: [] } } core = new Core(db, util, log, function() {}) log.info.reset() log.warn.reset() log.error.reset() testAppName = 'Precure' testApp = { fresh: true, closeServer: stub(), runVersion: stub(), } core.applicationMap.set(testAppName, testApp) }) t.test('should throw if application not found', async function() { const assertAppName = 'Sweet Sweet Sweets' let err = await assert.isRejected(core.runApplication(assertAppName)) assert.match(err.message, new RegExp(assertAppName)) assert.match(err.message, /exist/i) }) t.test('Should always attempt to close application first', async function() { const assertAppName = 'Pyramid Collapse' const assertError = new Error('Pyramid Collapse') const stubClose = stub().rejects(assertError) db.data.core[assertAppName] = { versions: [{}] } core.applicationMap.set(assertAppName, { closeServer: stubClose, }) let err = await assert.isRejected(core.runApplication(assertAppName)) assert.strictEqual(err, assertError) }) t.test('should select first valid version', async function() { const assertVersion = 'v50' const assertError = new Error('jellyfish') testApp.runVersion.rejects(assertError) db.data.core[testAppName].versions.push({ id: assertVersion, version: assertVersion + 'asdf', installed: true, stable: 0, }) let err = await assert.isRejected(core.runApplication(testAppName)) assert.notStrictEqual(err, assertError) assert.ok(log.error.called) assert.strictEqual(log.error.firstCall[0], assertError) assert.match(log.error.firstCall[1], new RegExp(testAppName)) assert.match(log.error.firstCall[1], new RegExp(assertVersion)) assert.match(log.error.firstCall[1], new RegExp(assertError.message)) }) })