Jonatan Nilsson
e540a54844
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
1005 lines
33 KiB
JavaScript
1005 lines
33 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() {
|
|
log.error.reset()
|
|
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.notStrictEqual(err, assertError)
|
|
assert.strictEqual(log.error.firstCall[0], assertError)
|
|
assert.match(err.message, /successful/i)
|
|
assert.match(err.message, /none/i)
|
|
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(log.error.firstCall[0], assertError)
|
|
assert.match(err.message, /successful/i)
|
|
assert.match(err.message, /none/i)
|
|
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.notStrictEqual(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 assertPathLog = 'log_test_1.log'
|
|
const assertFirstAppName = 'Kaeshite'
|
|
const assertSecondAppName = 'Motteke'
|
|
const assertTestFirstString = 'Magia'
|
|
const assertTestSecondString = 'Snow Falling'
|
|
const assertConfig = {
|
|
[assertFirstAppName]: {
|
|
provider: assertProviderName,
|
|
teststring: assertTestFirstString,
|
|
log: [
|
|
{ path: assertPathLog, level: 'info' },
|
|
{ stream: 'process.stdout', level: 'warn' },
|
|
]
|
|
},
|
|
[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.notStrictEqual(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))
|
|
assert.ok(application.ctx.log.streams.length >= 4)
|
|
assert.strictEqual(application.ctx.log.streams[0].path, assertPathLog)
|
|
assert.strictEqual(application.ctx.log.streams[0].level, 30)
|
|
assert.strictEqual(application.ctx.log.streams[0].type, 'file')
|
|
assert.strictEqual(application.ctx.log.streams[1].stream, process.stdout)
|
|
assert.strictEqual(application.ctx.log.streams[1].level, 40)
|
|
assert.strictEqual(application.ctx.log.streams[1].type, 'stream')
|
|
})
|
|
})
|
|
|
|
t.describe('#run()', function() {
|
|
let core
|
|
let testAppOneName
|
|
let testAppTwoName
|
|
let stubRunApplication
|
|
let stubOnOrOnce
|
|
let stubWrite
|
|
|
|
t.beforeEach(function() {
|
|
testAppOneName = 'Tenshi'
|
|
testAppTwoName = 'no CLOVER'
|
|
db.data.core = {
|
|
[testAppOneName]: {
|
|
versions: []
|
|
},
|
|
[testAppTwoName]: {
|
|
versions: []
|
|
},
|
|
}
|
|
core = new Core(db, util, log, function() {})
|
|
core.runApplication = stubRunApplication = stub().resolves()
|
|
db.write = stubWrite = stub().resolves()
|
|
log.info.reset()
|
|
log.warn.reset()
|
|
log.error.reset()
|
|
for (let name of [testAppOneName, testAppTwoName]) {
|
|
let onOrOnce = stub()
|
|
let app = {
|
|
name: name,
|
|
fresh: false,
|
|
on: onOrOnce,
|
|
once: onOrOnce,
|
|
ctx: {
|
|
log: {
|
|
info: stub(),
|
|
warn: stub(),
|
|
error: stub()
|
|
},
|
|
},
|
|
update: stub().resolves(),
|
|
startAutoupdater: stub(),
|
|
}
|
|
core.applicationMap.set(name, app)
|
|
core.applications.push(app)
|
|
}
|
|
})
|
|
|
|
t.test('should call update on all applications', async function() {
|
|
const assertFirstError = new Error('Manatsu')
|
|
const assertSecondError = new Error('no Photograph')
|
|
core.applicationMap.get(testAppOneName).update.rejects(assertFirstError)
|
|
core.applicationMap.get(testAppTwoName).update.rejects(assertSecondError)
|
|
|
|
await core.run()
|
|
|
|
assert.strictEqual(core.applications[0].ctx.log.error.callCount, 1)
|
|
assert.strictEqual(core.applications[0].ctx.log.error.firstCall[0], assertFirstError)
|
|
assert.match(core.applications[0].ctx.log.error.firstCall[1], /updat/)
|
|
assert.match(core.applications[0].ctx.log.error.firstCall[1], new RegExp(assertFirstError.message))
|
|
assert.strictEqual(core.applications[1].ctx.log.error.callCount, 1)
|
|
assert.strictEqual(core.applications[1].ctx.log.error.firstCall[0], assertSecondError)
|
|
assert.match(core.applications[1].ctx.log.error.firstCall[1], /updat/)
|
|
assert.match(core.applications[1].ctx.log.error.firstCall[1], new RegExp(assertSecondError.message))
|
|
})
|
|
|
|
|
|
t.test('app.on updated should be hooked eventually and should call runApplication', async function() {
|
|
assert.notOk(core.applicationMap.get(testAppOneName).once.called)
|
|
assert.notOk(core.applicationMap.get(testAppTwoName).once.called)
|
|
assert.notOk(core.applicationMap.get(testAppOneName).on.called)
|
|
assert.notOk(core.applicationMap.get(testAppTwoName).on.called)
|
|
const assertFirstError = new Error('Manatsu')
|
|
const assertSecondError = new Error('no Photograph')
|
|
core.applicationMap.get(testAppOneName).update.returnWith(function() {
|
|
assert.notOk(core.applicationMap.get(testAppOneName).once.called)
|
|
assert.notOk(core.applicationMap.get(testAppOneName).on.called)
|
|
return Promise.reject(assertFirstError)
|
|
})
|
|
core.applicationMap.get(testAppTwoName).update.returnWith(function() {
|
|
assert.notOk(core.applicationMap.get(testAppTwoName).once.called)
|
|
assert.notOk(core.applicationMap.get(testAppOneName).on.called)
|
|
return Promise.reject(assertSecondError)
|
|
})
|
|
core.runApplication.returnWith(function(app) {
|
|
assert.notOk(app.once.called)
|
|
assert.notOk(app.on.called)
|
|
return Promise.resolve()
|
|
})
|
|
|
|
await core.run()
|
|
|
|
assert.ok(core.applicationMap.get(testAppOneName).on.called)
|
|
assert.ok(core.applicationMap.get(testAppTwoName).on.called)
|
|
assert.strictEqual(core.applicationMap.get(testAppOneName).on.firstCall[0], 'updated')
|
|
assert.strictEqual(core.applicationMap.get(testAppTwoName).on.firstCall[0], 'updated')
|
|
|
|
assert.strictEqual(core.applications[0].ctx.log.error.callCount, 1)
|
|
assert.strictEqual(core.applications[0].ctx.log.error.firstCall[0], assertFirstError)
|
|
assert.match(core.applications[0].ctx.log.error.firstCall[1], /updat/)
|
|
assert.match(core.applications[0].ctx.log.error.firstCall[1], new RegExp(assertFirstError.message))
|
|
assert.strictEqual(core.applications[1].ctx.log.error.callCount, 1)
|
|
assert.strictEqual(core.applications[1].ctx.log.error.firstCall[0], assertSecondError)
|
|
assert.match(core.applications[1].ctx.log.error.firstCall[1], /updat/)
|
|
assert.match(core.applications[1].ctx.log.error.firstCall[1], new RegExp(assertSecondError.message))
|
|
|
|
assert.strictEqual(stubRunApplication.callCount, 2)
|
|
|
|
assert.strictEqual(stubRunApplication.getCallN(1)[0], core.applications[0])
|
|
assert.strictEqual(stubRunApplication.getCallN(2)[0], core.applications[1])
|
|
|
|
core.runApplication.resolves()
|
|
|
|
core.applications[0].on.firstCall[1]()
|
|
assert.strictEqual(stubRunApplication.callCount, 3)
|
|
assert.strictEqual(stubRunApplication.getCallN(3)[0], core.applications[0])
|
|
|
|
core.applications[0].on.firstCall[1]()
|
|
assert.strictEqual(stubRunApplication.callCount, 4)
|
|
assert.strictEqual(stubRunApplication.getCallN(4)[0], core.applications[0])
|
|
|
|
|
|
core.applications[1].on.firstCall[1]()
|
|
assert.strictEqual(stubRunApplication.callCount, 5)
|
|
assert.strictEqual(stubRunApplication.getCallN(5)[0], core.applications[1])
|
|
})
|
|
|
|
t.test('should call startAutoupdater on all applications', async function() {
|
|
stubRunApplication.rejects(new Error('not seen'))
|
|
assert.notOk(core.applications[0].startAutoupdater.called)
|
|
assert.notOk(core.applications[1].startAutoupdater.called)
|
|
|
|
await assert.isRejected(core.run())
|
|
|
|
assert.ok(core.applications[0].startAutoupdater.called)
|
|
assert.ok(core.applications[1].startAutoupdater.called)
|
|
})
|
|
|
|
t.test('should be safe to call multiple times', async function() {
|
|
await core.run()
|
|
|
|
assert.strictEqual(stubRunApplication.callCount, 2)
|
|
|
|
await core.run()
|
|
await core.run()
|
|
await core.run()
|
|
await core.run()
|
|
|
|
assert.strictEqual(stubRunApplication.callCount, 2)
|
|
})
|
|
|
|
t.test('should call runApplication on all applications', async function() {
|
|
const assertFirstError = new Error('Manatsu')
|
|
const assertSecondError = new Error('no Photograph')
|
|
const assertNoError = new Error('unseen')
|
|
stubRunApplication.onCallN(1).rejects(assertFirstError)
|
|
stubRunApplication.onCallN(2).rejects(assertSecondError)
|
|
stubRunApplication.onCallN(3).rejects(assertNoError)
|
|
|
|
await assert.isRejected(core.run())
|
|
|
|
assert.strictEqual(core.applications[0].ctx.log.error.callCount, 1)
|
|
assert.strictEqual(core.applications[0].ctx.log.error.firstCall[0], assertFirstError)
|
|
assert.match(core.applications[0].ctx.log.error.firstCall[1], /run/)
|
|
assert.match(core.applications[0].ctx.log.error.firstCall[1], new RegExp(assertFirstError.message))
|
|
assert.strictEqual(core.applications[1].ctx.log.error.callCount, 1)
|
|
assert.strictEqual(core.applications[1].ctx.log.error.firstCall[0], assertSecondError)
|
|
assert.match(core.applications[1].ctx.log.error.firstCall[1], /run/)
|
|
assert.match(core.applications[1].ctx.log.error.firstCall[1], new RegExp(assertSecondError.message))
|
|
})
|
|
|
|
t.test('should throw if all applications fail', async function() {
|
|
stubRunApplication.rejects(new Error('bla'))
|
|
|
|
let err = await assert.isRejected(core.run())
|
|
|
|
assert.match(err.message, /no/i)
|
|
assert.match(err.message, /application/i)
|
|
})
|
|
|
|
t.test('should not throw if at least one application is run', async function() {
|
|
stubRunApplication.onCallN(1).rejects(new Error('Scramble'))
|
|
|
|
await core.run()
|
|
|
|
assert.strictEqual(core.applications[0].ctx.log.error.callCount, 1)
|
|
assert.strictEqual(core.applications[1].ctx.log.error.callCount, 0)
|
|
})
|
|
|
|
t.test('should not throw if all applications are running', async function() {
|
|
await core.run()
|
|
|
|
assert.strictEqual(log.error.callCount, 0)
|
|
})
|
|
})
|
|
|
|
t.describe('#runApplication()', function() {
|
|
let core
|
|
let testApp
|
|
let testAppName
|
|
let stubWrite
|
|
|
|
t.beforeEach(function() {
|
|
testAppName = 'Precure'
|
|
db.data.core = {
|
|
[testAppName]: {
|
|
versions: []
|
|
}
|
|
}
|
|
core = new Core(db, util, log, function() {})
|
|
db.write = stubWrite = stub().resolves()
|
|
log.info.reset()
|
|
log.warn.reset()
|
|
log.error.reset()
|
|
testApp = {
|
|
name: testAppName,
|
|
fresh: false,
|
|
ctx: {
|
|
log: {
|
|
info: stub(),
|
|
warn: stub(),
|
|
error: stub(),
|
|
},
|
|
},
|
|
closeServer: stub(),
|
|
runVersion: stub(),
|
|
}
|
|
core.applicationMap.set(testAppName, testApp)
|
|
})
|
|
|
|
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: [{installed: true, stable: 0}]
|
|
}
|
|
core.applicationMap.set(assertAppName, {
|
|
name: assertAppName,
|
|
closeServer: stubClose,
|
|
})
|
|
|
|
let err = await assert.isRejected(core.runApplication(core.applicationMap.get(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(testApp))
|
|
assert.notStrictEqual(err, assertError)
|
|
assert.notOk(log.error.called)
|
|
assert.ok(testApp.ctx.log.error.called)
|
|
|
|
assert.strictEqual(testApp.ctx.log.error.firstCall[0], assertError)
|
|
assert.match(testApp.ctx.log.error.firstCall[1], new RegExp(assertVersion))
|
|
assert.match(testApp.ctx.log.error.firstCall[1], new RegExp(assertError.message))
|
|
assert.strict(testApp.runVersion.firstCall[0], assertVersion)
|
|
assert.match(err.message, /no/i)
|
|
assert.match(err.message, /found/i)
|
|
})
|
|
|
|
t.test('should skip versions that have not been installed or high error', async function() {
|
|
const assertError = new Error('Daikichi to Rin')
|
|
testApp.runVersion.rejects(assertError)
|
|
db.data.core[testAppName].versions.push({
|
|
id: '40',
|
|
version: 'v40',
|
|
installed: false,
|
|
stable: 0,
|
|
}, {
|
|
id: '41',
|
|
version: 'v41',
|
|
installed: true,
|
|
stable: -2,
|
|
}, {
|
|
id: '42',
|
|
version: 'v42',
|
|
installed: true,
|
|
stable: 0,
|
|
}, )
|
|
|
|
let err = await assert.isRejected(core.runApplication(testApp))
|
|
assert.notStrictEqual(err, assertError)
|
|
assert.notOk(log.error.called)
|
|
assert.ok(testApp.ctx.log.error.called)
|
|
|
|
assert.strictEqual(testApp.ctx.log.error.callCount, 1)
|
|
assert.strictEqual(testApp.ctx.log.error.firstCall[0], assertError)
|
|
assert.match(testApp.ctx.log.error.firstCall[1], new RegExp('42'))
|
|
assert.match(testApp.ctx.log.error.firstCall[1], new RegExp(assertError.message))
|
|
assert.strict(testApp.runVersion.firstCall[0], '42')
|
|
})
|
|
|
|
t.test('should attempt to run version with stable of -1 if fresh is true', async function() {
|
|
const assertError = new Error('Daikichi to Rin')
|
|
testApp.runVersion.rejects(assertError)
|
|
testApp.fresh = true
|
|
db.data.core[testAppName].versions.push({
|
|
id: '30',
|
|
version: 'v30',
|
|
installed: false,
|
|
stable: 0,
|
|
}, {
|
|
id: '31',
|
|
version: 'v31',
|
|
installed: true,
|
|
stable: -1,
|
|
}, {
|
|
id: '32',
|
|
version: 'v32',
|
|
installed: true,
|
|
stable: 0,
|
|
}, )
|
|
|
|
let err = await assert.isRejected(core.runApplication(testApp))
|
|
assert.notStrictEqual(err, assertError)
|
|
assert.notOk(log.error.called)
|
|
assert.ok(testApp.ctx.log.error.called)
|
|
|
|
assert.strictEqual(testApp.ctx.log.error.callCount, 2)
|
|
assert.strictEqual(testApp.ctx.log.error.firstCall[0], assertError)
|
|
assert.match(testApp.ctx.log.error.firstCall[1], new RegExp('31'))
|
|
assert.match(testApp.ctx.log.error.firstCall[1], new RegExp(assertError.message))
|
|
assert.strict(testApp.runVersion.firstCall[0], '31')
|
|
assert.strict(testApp.runVersion.firstCall[0], '32')
|
|
})
|
|
|
|
|
|
t.test('should skip version with stable of -1 if fresh is false', async function() {
|
|
const assertError = new Error('Daikichi to Rin')
|
|
testApp.runVersion.rejects(assertError)
|
|
testApp.fresh = false
|
|
db.data.core[testAppName].versions.push({
|
|
id: '30',
|
|
version: 'v30',
|
|
installed: true,
|
|
stable: 0,
|
|
}, {
|
|
id: '31',
|
|
version: 'v31',
|
|
installed: true,
|
|
stable: -1,
|
|
}, {
|
|
id: '32',
|
|
version: 'v32',
|
|
installed: true,
|
|
stable: 0,
|
|
}, )
|
|
|
|
let err = await assert.isRejected(core.runApplication(testApp))
|
|
assert.notStrictEqual(err, assertError)
|
|
assert.notOk(log.error.called)
|
|
assert.ok(testApp.ctx.log.error.called)
|
|
|
|
assert.strictEqual(testApp.ctx.log.error.callCount, 2)
|
|
assert.strictEqual(testApp.ctx.log.error.firstCall[0], assertError)
|
|
assert.match(testApp.ctx.log.error.firstCall[1], new RegExp('30'))
|
|
assert.match(testApp.ctx.log.error.firstCall[1], new RegExp(assertError.message))
|
|
assert.strictEqual(testApp.ctx.log.error.secondCall[0], assertError)
|
|
assert.match(testApp.ctx.log.error.secondCall[1], new RegExp('32'))
|
|
assert.match(testApp.ctx.log.error.secondCall[1], new RegExp(assertError.message))
|
|
assert.strict(testApp.runVersion.firstCall[0], '30')
|
|
assert.strict(testApp.runVersion.firstCall[0], '32')
|
|
})
|
|
|
|
t.test('should change status accordingly when application is fresh', async function() {
|
|
const assertError = new Error('Daikichi to Rin')
|
|
testApp.runVersion.rejects(assertError)
|
|
testApp.fresh = true
|
|
db.data.core[testAppName].versions.push({
|
|
id: '30',
|
|
version: 'v30',
|
|
installed: true,
|
|
stable: 0,
|
|
}, {
|
|
id: '31',
|
|
version: 'v31',
|
|
installed: true,
|
|
stable: -1,
|
|
}, {
|
|
id: '32',
|
|
version: 'v32',
|
|
installed: true,
|
|
stable: -2,
|
|
})
|
|
|
|
let err = await assert.isRejected(core.runApplication(testApp))
|
|
assert.notStrictEqual(err, assertError)
|
|
assert.notOk(log.error.called)
|
|
assert.ok(testApp.ctx.log.error.called)
|
|
|
|
assert.strictEqual(testApp.ctx.log.error.callCount, 2)
|
|
assert.strictEqual(testApp.ctx.log.error.firstCall[0], assertError)
|
|
assert.match(testApp.ctx.log.error.firstCall[1], new RegExp('30'))
|
|
assert.match(testApp.ctx.log.error.firstCall[1], new RegExp(assertError.message))
|
|
assert.match(testApp.ctx.log.error.secondCall[1], new RegExp('31'))
|
|
assert.match(testApp.ctx.log.error.secondCall[1], new RegExp(assertError.message))
|
|
|
|
assert.strictEqual(db.data.core[testAppName].versions[0].stable, -1)
|
|
assert.strictEqual(db.data.core[testAppName].versions[1].stable, -2)
|
|
|
|
assert.ok(stubWrite.callCount, 2)
|
|
})
|
|
|
|
t.test('should always go to -1 minimum on crash', async function() {
|
|
const assertError = new Error('Daikichi to Rin')
|
|
testApp.runVersion.rejects(assertError)
|
|
testApp.fresh = false
|
|
db.data.core[testAppName].versions.push({
|
|
id: '28',
|
|
version: 'v28',
|
|
installed: true,
|
|
stable: 5,
|
|
}, {
|
|
id: '29',
|
|
version: 'v29',
|
|
installed: true,
|
|
stable: 1,
|
|
}, {
|
|
id: '30',
|
|
version: 'v30',
|
|
installed: true,
|
|
stable: 0,
|
|
}, {
|
|
id: '31',
|
|
version: 'v31',
|
|
installed: true,
|
|
stable: -1,
|
|
})
|
|
|
|
let err = await assert.isRejected(core.runApplication(testApp))
|
|
assert.notStrictEqual(err, assertError)
|
|
assert.notOk(log.error.called)
|
|
assert.ok(testApp.ctx.log.error.called)
|
|
|
|
assert.strictEqual(testApp.ctx.log.error.callCount, 3)
|
|
|
|
assert.strictEqual(db.data.core[testAppName].versions[0].stable, -1)
|
|
assert.strictEqual(db.data.core[testAppName].versions[1].stable, -1)
|
|
assert.strictEqual(db.data.core[testAppName].versions[2].stable, -1)
|
|
assert.strictEqual(db.data.core[testAppName].versions[3].stable, -1)
|
|
|
|
assert.ok(stubWrite.callCount, 2)
|
|
})
|
|
|
|
t.test('should throw if no stable version is found', async function() {
|
|
const assertError = new Error('Daikichi to Rin')
|
|
testApp.runVersion.rejects(assertError)
|
|
testApp.fresh = true
|
|
db.data.core[testAppName].versions.push({
|
|
id: '20',
|
|
version: 'v20',
|
|
installed: true,
|
|
stable: 0,
|
|
}, {
|
|
id: '21',
|
|
version: 'v21',
|
|
installed: true,
|
|
stable: 0,
|
|
}, {
|
|
id: '22',
|
|
version: 'v22',
|
|
installed: true,
|
|
stable: 0,
|
|
})
|
|
|
|
let err = await assert.isRejected(core.runApplication(testApp))
|
|
assert.notStrictEqual(err, assertError)
|
|
assert.match(err.message, /found/)
|
|
})
|
|
|
|
|
|
t.test('should throw if no version are found', async function() {
|
|
const assertError = new Error('Daikichi to Rin')
|
|
testApp.runVersion.rejects(assertError)
|
|
testApp.fresh = true
|
|
db.data.core[testAppName].versions = []
|
|
|
|
let err = await assert.isRejected(core.runApplication(testApp))
|
|
assert.notStrictEqual(err, assertError)
|
|
assert.match(err.message, /found/)
|
|
assert.notMatch(err.message, /stable/i)
|
|
})
|
|
|
|
t.test('should otherwise succeed if one is found', async function() {
|
|
const assertError = new Error('Daikichi to Rin')
|
|
let count = 0
|
|
testApp.runVersion.returnWith(function() {
|
|
if (count > 0) return Promise.reject(assertError)
|
|
count++
|
|
return Promise.resolve()
|
|
})
|
|
testApp.fresh = true
|
|
db.data.core[testAppName].versions.push({
|
|
id: '70',
|
|
version: 'v70',
|
|
installed: true,
|
|
stable: 100,
|
|
}, {
|
|
id: '71',
|
|
version: 'v71',
|
|
installed: true,
|
|
stable: 0,
|
|
}, {
|
|
id: '72',
|
|
version: 'v72',
|
|
installed: true,
|
|
stable: 0,
|
|
})
|
|
|
|
await core.runApplication(testApp)
|
|
assert.notOk(log.error.called)
|
|
assert.notOk(testApp.ctx.log.error.called)
|
|
assert.strictEqual(testApp.runVersion.callCount, 1)
|
|
assert.strictEqual(testApp.runVersion.firstCall[0], 'v70')
|
|
assert.strictEqual(db.data.core[testAppName].versions[0].stable, 1)
|
|
assert.strictEqual(db.data.core[testAppName].versions[1].stable, 0)
|
|
assert.strictEqual(db.data.core[testAppName].versions[2].stable, 0)
|
|
assert.ok(stubWrite.called)
|
|
})
|
|
|
|
|
|
t.test('should succeed if running a minus one on fresh', async function() {
|
|
const assertError = new Error('Daikichi to Rin')
|
|
let count = 0
|
|
testApp.fresh = true
|
|
testApp.runVersion.returnWith(function() {
|
|
if (count > 0) return Promise.reject(assertError)
|
|
count++
|
|
return Promise.resolve()
|
|
})
|
|
testApp.fresh = true
|
|
db.data.core[testAppName].versions.push({
|
|
id: '70',
|
|
version: 'v70',
|
|
installed: true,
|
|
stable: -1,
|
|
}, {
|
|
id: '71',
|
|
version: 'v71',
|
|
installed: true,
|
|
stable: 0,
|
|
}, {
|
|
id: '72',
|
|
version: 'v72',
|
|
installed: true,
|
|
stable: 0,
|
|
})
|
|
|
|
await core.runApplication(testApp)
|
|
assert.notOk(log.error.called)
|
|
assert.notOk(testApp.ctx.log.error.called)
|
|
assert.strictEqual(testApp.runVersion.callCount, 1)
|
|
assert.strictEqual(testApp.runVersion.firstCall[0], 'v70')
|
|
assert.strictEqual(db.data.core[testAppName].versions[0].stable, 1)
|
|
assert.strictEqual(db.data.core[testAppName].versions[1].stable, 0)
|
|
assert.strictEqual(db.data.core[testAppName].versions[2].stable, 0)
|
|
assert.ok(stubWrite.called)
|
|
})
|
|
})
|