service-core/test/core.test.mjs

489 lines
15 KiB
JavaScript

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))
})
})