More core development, full integration started
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
This commit is contained in:
parent
3f8e3cdc16
commit
4f4bc8cf6a
11 changed files with 631 additions and 92 deletions
|
@ -291,8 +291,12 @@ export default class Application extends EventEmitter {
|
|||
await this.fs.stat(indexPath).catch((err) => {
|
||||
return Promise.reject(new Error(`Version was missing index.mjs: ${err.message}`))
|
||||
})
|
||||
|
||||
this.fresh = false
|
||||
let module = await import(this.ctx.util.getUrlFromRoot(`./${this.name}/${version}/index.mjs`))
|
||||
this.registerModule(module, version)
|
||||
} else {
|
||||
this.fresh = false
|
||||
}
|
||||
|
||||
let errTimeout = new Error(`Version timed out (took over ${this.config.startWaitUntilFail}ms) while running start()`)
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import Application from './application.mjs'
|
||||
import Util from './util.mjs'
|
||||
import getLog from './log.mjs'
|
||||
import { Low } from 'lowdb'
|
||||
import StaticProvider from './providers/static.mjs'
|
||||
import GitProvider from './providers/git.mjs'
|
||||
|
||||
export default class Core {
|
||||
static providers = new Map()
|
||||
|
@ -46,10 +49,13 @@ export default class Core {
|
|||
}
|
||||
|
||||
async init() {
|
||||
this.util.verifyConfig(this.db.config)
|
||||
this.log.info(`Verifying config`)
|
||||
|
||||
this.util.verifyConfig(this.db.config)
|
||||
let names = this.util.getAppNames(this.db.config)
|
||||
|
||||
this.log.info(`Found applications: ${names.join(', ')}.`)
|
||||
|
||||
let lastError = null
|
||||
|
||||
for (let name of names) {
|
||||
|
@ -61,7 +67,7 @@ export default class Core {
|
|||
let application = new Application({
|
||||
db: this.db,
|
||||
util: this.util,
|
||||
log: this.log,
|
||||
log: getLog(name, this.db.config[name].log || null),
|
||||
core: this,
|
||||
}, provider, name)
|
||||
this.applications.push(application)
|
||||
|
@ -77,26 +83,65 @@ export default class Core {
|
|||
}
|
||||
}
|
||||
|
||||
async runApplication(name) {
|
||||
let application = this.applicationMap.get(name)
|
||||
|
||||
if (!application) {
|
||||
return Promise.reject(new Error(`Core.runApplication was called on a non-existing application name ${name}`))
|
||||
}
|
||||
async run() {
|
||||
this.log.info(`Running updater on ${this.applications.length} apps`)
|
||||
await Promise.all(this.applications.map((app) => {
|
||||
return app.update().catch(err => {
|
||||
this.log.error(err, `Error updating ${app.name}: ${err.message}`)
|
||||
})
|
||||
}))
|
||||
|
||||
let found = false
|
||||
|
||||
for (let app of this.applications) {
|
||||
app.startAutoupdater()
|
||||
|
||||
await this.runApplication(app).then(
|
||||
() => {
|
||||
found = true
|
||||
},
|
||||
err => {
|
||||
this.log.error(err, `Error running application ${app.name}: ${err.message}`)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
throw new Error('No stable application was found')
|
||||
}
|
||||
}
|
||||
|
||||
async runApplication(application) {
|
||||
let name = application.name
|
||||
let found = false
|
||||
|
||||
if (!this.db.data.core[name].versions.length) {
|
||||
return Promise.reject(new Error(`No versions were found`))
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.db.data.core[name].versions.length; i++) {
|
||||
let version = this.db.data.core[name].versions[i]
|
||||
if (!version.installed || version.stable < -1) continue
|
||||
if (version.stable < 0 && !application.fresh) continue
|
||||
await application.closeServer()
|
||||
|
||||
try {
|
||||
await application.runVersion()
|
||||
this.log.info(`Attempting to run application ${name} version ${version.version}`)
|
||||
await application.runVersion(version.version)
|
||||
found = true
|
||||
break
|
||||
} catch(err) {
|
||||
this.log.error(err, `Error starting ${name} ${version.id}: ${err.message}`)
|
||||
version.stable--
|
||||
await this.db.write()
|
||||
this.log.error(err, `Error starting ${name} ${version.version}: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(``)
|
||||
if (!found) {
|
||||
return Promise.reject(Error(`No stable versions were found`))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Core.addProvider('static', StaticProvider)
|
||||
Core.addProvider('git', GitProvider)
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
"scripts": {
|
||||
"dev": "nodemon --watch dev/api --watch core --watch runner.mjs --watch db.mjs --watch log.mjs runner.mjs | bunyan",
|
||||
"test": "eltro \"test/**/*.test.mjs\" -r dot",
|
||||
"test:integration": "eltro \"test/**/*.test.integration.mjs\" -r list",
|
||||
"test:test": "eltro \"test/application.test.integration.mjs\" -r list",
|
||||
"test:spec": "eltro \"test/**/*.test.mjs\" -r list",
|
||||
"test:watch": "npm-watch test"
|
||||
},
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
import { Eltro as t, assert, stub } from 'eltro'
|
||||
import fs from 'fs/promises'
|
||||
import Application from '../core/application.mjs'
|
||||
import GitProvider from '../core/providers/git.mjs'
|
||||
import Util from '../core/util.mjs'
|
||||
import { createFakeContext } from './helpers.mjs'
|
||||
|
||||
const util = new Util(import.meta.url)
|
||||
|
||||
|
||||
t.skip().timeout(10000).describe('Application update integration test', function() {
|
||||
let ctx
|
||||
let app
|
||||
let provider
|
||||
|
||||
t.before(function() {
|
||||
return createFakeContext({ }, util, util.getPathFromRoot('./db_test.json'))
|
||||
.then(function(res) {
|
||||
ctx = res
|
||||
provider = new GitProvider({ url: 'https://git.nfp.is/api/v1/repos/thething/sc-helloworld/releases' })
|
||||
app = new Application(ctx, provider, 'testapp')
|
||||
|
||||
return provider.getLatestVersion()
|
||||
}).then(function(version) {
|
||||
return fs.rm(`./test/testapp/${version.version}`, { force: true, recursive: true })
|
||||
})
|
||||
})
|
||||
|
||||
t.after(function() {
|
||||
return fs.rm(util.getPathFromRoot('./db_test.json'))
|
||||
.then(function() {
|
||||
if (ctx.db.data.core.testapp.versions.length) {
|
||||
return fs.rm(`./test/testapp/${ctx.db.data.core.testapp.versions[0].id}`, { force: true, recursive: true })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should run update and install correctly', async function(){
|
||||
try {
|
||||
await app.update()
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
if (ctx.db.data.core.testapp.versions.length) {
|
||||
console.log(ctx.db.data.core.testapp.versions[0].log)
|
||||
}
|
||||
throw err
|
||||
}
|
||||
|
||||
assert.ok(ctx.db.data.core.testapp.latestInstalled)
|
||||
await fs.stat(util.getPathFromRoot(`./testapp/${ctx.db.data.core.testapp.latestInstalled}/index.mjs`))
|
||||
await fs.stat(util.getPathFromRoot(`./testapp/${ctx.db.data.core.testapp.latestInstalled}/package.json`))
|
||||
await fs.stat(util.getPathFromRoot(`./testapp/${ctx.db.data.core.testapp.latestInstalled}/node_modules`))
|
||||
})
|
||||
})
|
|
@ -48,7 +48,9 @@ t.describe('#runVersion("static")', function() {
|
|||
assert.strictEqual(checkCtx.app, app)
|
||||
})
|
||||
|
||||
assert.strictEqual(app.fresh, true)
|
||||
let err = await assert.isRejected(app.runVersion('static'))
|
||||
assert.strictEqual(app.fresh, false)
|
||||
|
||||
assert.match(err.message, /http/i)
|
||||
assert.match(err.message, /createServer/i)
|
||||
|
@ -60,7 +62,9 @@ t.describe('#runVersion("static")', function() {
|
|||
app.config.startWaitUntilFail = 50
|
||||
app.registerModule(function() { return new Promise(function() {}) })
|
||||
|
||||
assert.strictEqual(app.fresh, true)
|
||||
let err = await assert.isRejected(app.runVersion('static'))
|
||||
assert.strictEqual(app.fresh, false)
|
||||
|
||||
assert.match(err.message, /time/i)
|
||||
assert.match(err.message, /out/i)
|
||||
|
@ -79,7 +83,9 @@ t.describe('#runVersion("static")', function() {
|
|||
})
|
||||
})
|
||||
|
||||
assert.strictEqual(app.fresh, true)
|
||||
await app.runVersion('static')
|
||||
assert.strictEqual(app.fresh, false)
|
||||
|
||||
assert.strictEqual(ctx.db.data.core.testapp.active, 'static')
|
||||
})
|
||||
|
@ -94,7 +100,10 @@ t.describe('#runVersion("static")', function() {
|
|||
app.config.heartbeatAttempts = 3
|
||||
app.registerModule(defaultHandler(handler))
|
||||
|
||||
assert.strictEqual(app.fresh, true)
|
||||
let err = await assert.isRejected(app.runVersion('static'))
|
||||
assert.strictEqual(app.fresh, false)
|
||||
|
||||
assert.match(err.message, /failed/i)
|
||||
assert.match(err.message, /400/i)
|
||||
assert.strictEqual(called, 3)
|
||||
|
@ -110,9 +119,12 @@ t.describe('#runVersion("static")', function() {
|
|||
app.config.heartbeatAttemptsWait = 30
|
||||
app.registerModule(defaultHandler(handler))
|
||||
|
||||
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.match(err.message, /failed/i)
|
||||
assert.match(err.message, /time/i)
|
||||
assert.match(err.message, /out/i)
|
||||
|
@ -193,7 +205,10 @@ t.describe('#runVersion("version")', function() {
|
|||
app.config.port = assertPort
|
||||
stubFsStat.rejects(assertNotError)
|
||||
|
||||
assert.strictEqual(app.fresh, true)
|
||||
let err = await assert.isRejected(app.runVersion('v100'))
|
||||
assert.strictEqual(app.fresh, true)
|
||||
|
||||
assert.notStrictEqual(err, assertNotError)
|
||||
assert.match(err.message, new RegExp(assertNotError.message))
|
||||
assert.match(err.message, /index\.mjs/i)
|
||||
|
@ -210,7 +225,10 @@ t.describe('#runVersion("version")', function() {
|
|||
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.fresh, true)
|
||||
let err = await assert.isRejected(app.runVersion('v99'))
|
||||
assert.strictEqual(app.fresh, false)
|
||||
|
||||
assert.notStrictEqual(err, assertError)
|
||||
assert.strictEqual(err.message, assertError.message)
|
||||
|
||||
|
@ -223,7 +241,9 @@ t.describe('#runVersion("version")', function() {
|
|||
await fs.mkdir(util.getPathFromRoot('./testnoexisting/v98'), { recursive: true })
|
||||
await fs.writeFile(util.getPathFromRoot('./testnoexisting/v98/index.mjs'), ``)
|
||||
|
||||
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.db.data.core.testnoexisting.active, 'v98')
|
||||
|
@ -241,7 +261,9 @@ t.describe('#runVersion("version")', function() {
|
|||
app.ctx.log.info.reset()
|
||||
app.ctx.log.event.info.reset()
|
||||
|
||||
assert.strictEqual(app.fresh, true)
|
||||
await app.runVersion('v97')
|
||||
assert.strictEqual(app.fresh, false)
|
||||
|
||||
assert.ok(app.ctx.log.info.called)
|
||||
assert.ok(app.ctx.log.event.info.called)
|
||||
|
|
50
test/application.test.integration.mjs
Normal file
50
test/application.test.integration.mjs
Normal file
|
@ -0,0 +1,50 @@
|
|||
import { Eltro as t, assert, stub } from 'eltro'
|
||||
import fs from 'fs/promises'
|
||||
import Application from '../core/application.mjs'
|
||||
import GitProvider from '../core/providers/git.mjs'
|
||||
import Util from '../core/util.mjs'
|
||||
import { createFakeContext } from './helpers.mjs'
|
||||
|
||||
const util = new Util(import.meta.url)
|
||||
let ctx
|
||||
let app
|
||||
let provider
|
||||
|
||||
t.before(function() {
|
||||
return createFakeContext({ }, util, util.getPathFromRoot('./db_test.json'))
|
||||
.then(function(res) {
|
||||
ctx = res
|
||||
provider = new GitProvider({ url: 'https://git.nfp.is/api/v1/repos/thething/sc-helloworld/releases' })
|
||||
app = new Application(ctx, provider, 'testapp')
|
||||
|
||||
return provider.getLatestVersion()
|
||||
}).then(function(version) {
|
||||
return fs.rm(`./test/testapp/${version.version}`, { force: true, recursive: true })
|
||||
})
|
||||
})
|
||||
|
||||
t.after(function() {
|
||||
return fs.rm(util.getPathFromRoot('./db_test.json'))
|
||||
.then(function() {
|
||||
if (ctx.db.data.core.testapp.versions.length) {
|
||||
return fs.rm(`./test/testapp/${ctx.db.data.core.testapp.versions[0].id}`, { force: true, recursive: true })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.timeout(10000).test('should run update and install correctly', async function(){
|
||||
try {
|
||||
await app.update()
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
if (ctx.db.data.core.testapp.versions.length) {
|
||||
console.log(ctx.db.data.core.testapp.versions[0].log)
|
||||
}
|
||||
throw err
|
||||
}
|
||||
|
||||
assert.ok(ctx.db.data.core.testapp.latestInstalled)
|
||||
await fs.stat(util.getPathFromRoot(`./testapp/${ctx.db.data.core.testapp.latestInstalled}/index.mjs`))
|
||||
await fs.stat(util.getPathFromRoot(`./testapp/${ctx.db.data.core.testapp.latestInstalled}/package.json`))
|
||||
await fs.stat(util.getPathFromRoot(`./testapp/${ctx.db.data.core.testapp.latestInstalled}/node_modules`))
|
||||
})
|
70
test/core.test.integration.mjs
Normal file
70
test/core.test.integration.mjs
Normal file
|
@ -0,0 +1,70 @@
|
|||
import { Eltro as t, assert} from 'eltro'
|
||||
import fs from 'fs/promises'
|
||||
import http from 'http'
|
||||
import Util from '../core/util.mjs'
|
||||
import { request } from '../core/client.mjs'
|
||||
|
||||
const util = new Util(import.meta.url)
|
||||
const port = 61412
|
||||
const defaultHandler = function(req, res) {
|
||||
res.statusCode = 200
|
||||
res.end('{"a":1}');
|
||||
}
|
||||
let server = null
|
||||
let prefix = `http://localhost:${port}/`
|
||||
let handler = defaultHandler
|
||||
let files = []
|
||||
let logs = []
|
||||
|
||||
t.describe('', function() {
|
||||
t.before(function(cb) {
|
||||
server = http.createServer(function(req, res) {
|
||||
req.on('error', function(err) {
|
||||
console.log('error', err)
|
||||
})
|
||||
res.on('error', function(err) {
|
||||
console.log('error', err)
|
||||
})
|
||||
handler(req, res)
|
||||
})
|
||||
server.listen(port, cb)
|
||||
})
|
||||
|
||||
t.after(function() {
|
||||
return Promise.resolve()
|
||||
return Promise.all(files.map(function(file) {
|
||||
return fs.rm(file, { force: true, recursive: true })
|
||||
}))
|
||||
})
|
||||
|
||||
const stable_version_1 = `
|
||||
export function start(http, port, ctx) {
|
||||
const server = http.createServer(function (req, res) {
|
||||
res.writeHead(200);
|
||||
res.end(JSON.stringify({ version: 'stable_version_1' }))
|
||||
})
|
||||
|
||||
return server.listenAsync(port, '0.0.0.0')
|
||||
.then(() => {
|
||||
ctx.log.info(\`Server is listening on \${port} serving stable_version_1\`)
|
||||
})
|
||||
}
|
||||
`
|
||||
|
||||
function file(relative) {
|
||||
let file = util.getPathFromRoot(relative)
|
||||
files.push(file)
|
||||
return file
|
||||
}
|
||||
|
||||
function log(message) {
|
||||
logs.push(message)
|
||||
console.log(message)
|
||||
}
|
||||
|
||||
t.test('should be fully operational', async function() {
|
||||
let index = file('./index.mjs')
|
||||
await fs.writeFile(index, stable_version_1)
|
||||
await util.runCommand(util.get7zipExecutable(), ['a', file('./v1.7z'), index], util.getPathFromRoot('./testapp/'), log)
|
||||
})
|
||||
})
|
|
@ -356,7 +356,7 @@ t.describe('#init()', function() {
|
|||
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.notStrictEqual(application.ctx.log, core.log)
|
||||
assert.strictEqual(application.ctx.core, core)
|
||||
assert.strictEqual(application.config.teststring, assertTestString)
|
||||
assert.ok(application.fresh)
|
||||
|
@ -375,6 +375,7 @@ t.describe('#init()', function() {
|
|||
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'
|
||||
|
@ -383,6 +384,10 @@ t.describe('#init()', function() {
|
|||
[assertFirstAppName]: {
|
||||
provider: assertProviderName,
|
||||
teststring: assertTestFirstString,
|
||||
log: [
|
||||
{ path: assertPathLog, level: 'info' },
|
||||
{ stream: 'process.stdout', level: 'warn' },
|
||||
]
|
||||
},
|
||||
[assertSecondAppName]: {
|
||||
provider: assertProviderFailName,
|
||||
|
@ -403,7 +408,7 @@ t.describe('#init()', function() {
|
|||
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.notStrictEqual(application.ctx.log, core.log) //
|
||||
assert.strictEqual(application.ctx.core, core)
|
||||
assert.strictEqual(application.config.teststring, assertTestFirstString)
|
||||
assert.ok(application.fresh)
|
||||
|
@ -413,54 +418,168 @@ t.describe('#init()', function() {
|
|||
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.only().describe('#runApplication()', function() {
|
||||
t.describe('#run()', function() {
|
||||
let core
|
||||
let testAppOneName
|
||||
let testAppTwoName
|
||||
let stubRunApplication
|
||||
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 app = {
|
||||
name: name,
|
||||
fresh: false,
|
||||
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(log.error.callCount, 2)
|
||||
assert.strictEqual(log.error.firstCall[0], assertFirstError)
|
||||
assert.match(log.error.firstCall[1], new RegExp(testAppOneName))
|
||||
assert.match(log.error.firstCall[1], /updat/)
|
||||
assert.match(log.error.firstCall[1], new RegExp(assertFirstError.message))
|
||||
assert.strictEqual(log.error.secondCall[0], assertSecondError)
|
||||
assert.match(log.error.secondCall[1], new RegExp(testAppTwoName))
|
||||
assert.match(log.error.secondCall[1], /updat/)
|
||||
assert.match(log.error.secondCall[1], new RegExp(assertSecondError.message))
|
||||
})
|
||||
|
||||
|
||||
t.test('should call startAutoupdater on all applications', async function() {
|
||||
stubRunApplication.rejects(new Error('not seen'))
|
||||
|
||||
await assert.isRejected(core.run())
|
||||
|
||||
assert.ok(core.applications[0].startAutoupdater.called)
|
||||
assert.ok(core.applications[1].startAutoupdater.called)
|
||||
})
|
||||
|
||||
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(log.error.callCount, 2)
|
||||
assert.strictEqual(stubRunApplication.firstCall[0], core.applications[0])
|
||||
assert.strictEqual(stubRunApplication.secondCall[0], core.applications[1])
|
||||
assert.strictEqual(log.error.firstCall[0], assertFirstError)
|
||||
assert.match(log.error.firstCall[1], new RegExp(testAppOneName))
|
||||
assert.match(log.error.firstCall[1], /run/)
|
||||
assert.match(log.error.firstCall[1], new RegExp(assertFirstError.message))
|
||||
assert.strictEqual(log.error.secondCall[0], assertSecondError)
|
||||
assert.match(log.error.secondCall[1], new RegExp(testAppTwoName))
|
||||
assert.match(log.error.secondCall[1], /run/)
|
||||
assert.match(log.error.secondCall[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(log.error.callCount, 1)
|
||||
})
|
||||
|
||||
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()
|
||||
testAppName = 'Precure'
|
||||
testApp = {
|
||||
fresh: true,
|
||||
name: testAppName,
|
||||
fresh: false,
|
||||
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: [{}]
|
||||
versions: [{installed: true, stable: 0}]
|
||||
}
|
||||
core.applicationMap.set(assertAppName, {
|
||||
name: assertAppName,
|
||||
closeServer: stubClose,
|
||||
})
|
||||
|
||||
let err = await assert.isRejected(core.runApplication(assertAppName))
|
||||
let err = await assert.isRejected(core.runApplication(core.applicationMap.get(assertAppName)))
|
||||
|
||||
assert.strictEqual(err, assertError)
|
||||
})
|
||||
|
@ -476,7 +595,7 @@ t.only().describe('#runApplication()', function() {
|
|||
stable: 0,
|
||||
})
|
||||
|
||||
let err = await assert.isRejected(core.runApplication(testAppName))
|
||||
let err = await assert.isRejected(core.runApplication(testApp))
|
||||
assert.notStrictEqual(err, assertError)
|
||||
assert.ok(log.error.called)
|
||||
|
||||
|
@ -484,5 +603,222 @@ t.only().describe('#runApplication()', function() {
|
|||
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))
|
||||
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.ok(log.error.called)
|
||||
|
||||
assert.strictEqual(log.error.callCount, 1)
|
||||
assert.strictEqual(log.error.firstCall[0], assertError)
|
||||
assert.match(log.error.firstCall[1], new RegExp(testAppName))
|
||||
assert.match(log.error.firstCall[1], new RegExp('42'))
|
||||
assert.match(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.ok(log.error.called)
|
||||
|
||||
assert.strictEqual(log.error.callCount, 2)
|
||||
assert.strictEqual(log.error.firstCall[0], assertError)
|
||||
assert.match(log.error.firstCall[1], new RegExp(testAppName))
|
||||
assert.match(log.error.firstCall[1], new RegExp('31'))
|
||||
assert.match(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.ok(log.error.called)
|
||||
|
||||
assert.strictEqual(log.error.callCount, 2)
|
||||
assert.strictEqual(log.error.firstCall[0], assertError)
|
||||
assert.match(log.error.firstCall[1], new RegExp(testAppName))
|
||||
assert.match(log.error.firstCall[1], new RegExp('30'))
|
||||
assert.match(log.error.firstCall[1], new RegExp(assertError.message))
|
||||
assert.strictEqual(log.error.secondCall[0], assertError)
|
||||
assert.match(log.error.secondCall[1], new RegExp(testAppName))
|
||||
assert.match(log.error.secondCall[1], new RegExp('32'))
|
||||
assert.match(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.ok(log.error.called)
|
||||
|
||||
assert.strictEqual(log.error.callCount, 2)
|
||||
assert.strictEqual(log.error.firstCall[0], assertError)
|
||||
assert.match(log.error.firstCall[1], new RegExp(testAppName))
|
||||
assert.match(log.error.firstCall[1], new RegExp('30'))
|
||||
assert.match(log.error.firstCall[1], new RegExp(assertError.message))
|
||||
assert.match(log.error.secondCall[1], new RegExp(testAppName))
|
||||
assert.match(log.error.secondCall[1], new RegExp('31'))
|
||||
assert.match(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 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: 0,
|
||||
}, {
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -3,13 +3,13 @@ import Util from '../../core/util.mjs'
|
|||
import fs from 'fs/promises'
|
||||
import GitProvider from '../../core/providers/git.mjs'
|
||||
|
||||
t.timeout(5000).describe('Git integration', function() {
|
||||
t.describe('test', function() {
|
||||
t.after(function() {
|
||||
return fs.rm('./test/providers/file.7z')
|
||||
.catch(function() { })
|
||||
})
|
||||
|
||||
t.describe('#getLatestVersion()', function() {
|
||||
t.timeout(5000).describe('#getLatestVersion()', function() {
|
||||
t.test('should return latest version in a valid repository', async function() {
|
||||
let provider = new GitProvider({ url: 'https://git.nfp.is/api/v1/repos/thething/sc-helloworld/releases' })
|
||||
let version = await provider.getLatestVersion()
|
||||
|
@ -20,8 +20,8 @@ t.timeout(5000).describe('Git integration', function() {
|
|||
assert.match(version.link, /\/attachments\//)
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('#checkConfig()', function() {
|
||||
|
||||
t.timeout(5000).describe('#checkConfig()', function() {
|
||||
t.test('should fail if link does not return json repository object', async function() {
|
||||
let err = await assert.isRejected(new GitProvider({ url: 'http://git.nfp.is/api/v1/repos/thething/ProgramQueuer' }).checkConfig())
|
||||
assert.match(err.message, /valid/i)
|
||||
|
@ -30,14 +30,14 @@ t.timeout(5000).describe('Git integration', function() {
|
|||
assert.match(err.message, /service-core/i)
|
||||
assert.match(err.message, /release/i)
|
||||
})
|
||||
|
||||
t.test('should fail if link returns no active release repository with assets', async function() {
|
||||
|
||||
t.test('should fail if no active release repository with assets', async function() {
|
||||
let err = await assert.isRejected(new GitProvider({ url: 'https://git.nfp.is/api/v1/repos/thething/eltro/releases' }).checkConfig())
|
||||
assert.match(err.message, /service-core/i)
|
||||
assert.match(err.message, /release/i)
|
||||
})
|
||||
|
||||
t.test('should fail on private repositories if token missing', async function() {
|
||||
|
||||
t.test('should fail on private repositories', async function() {
|
||||
let err = await assert.isRejected(new GitProvider({ url: 'https://git.nfp.is/api/v1/repos/TheThing/privateexample/releases' }).checkConfig())
|
||||
assert.match(err.message, /fail/i)
|
||||
assert.match(err.message, /404/i)
|
||||
|
@ -53,7 +53,7 @@ t.timeout(5000).describe('Git integration', function() {
|
|||
if (!process.env.gittesttoken) {
|
||||
test = test.skip()
|
||||
}
|
||||
test.test('should succeed on private repo if token is provided', function() {
|
||||
test.test('should succeed on private repo with token', function() {
|
||||
return new GitProvider({
|
||||
token: process.env.gittesttoken.trim(),
|
||||
url: 'https://git.nfp.is/api/v1/repos/TheThing/privateexample/releases',
|
||||
|
@ -61,7 +61,7 @@ t.timeout(5000).describe('Git integration', function() {
|
|||
})
|
||||
})
|
||||
|
||||
t.describe('#downloadVersion()', function() {
|
||||
t.timeout(5000).describe('#downloadVersion()', function() {
|
||||
const util = new Util(import.meta.url)
|
||||
|
||||
let test = t
|
47
test/runner.mjs
Normal file
47
test/runner.mjs
Normal file
|
@ -0,0 +1,47 @@
|
|||
import fs from 'fs'
|
||||
import Core from '../core/core.mjs'
|
||||
import GetDB from '../core/db.mjs'
|
||||
import getLog from '../core/log.mjs'
|
||||
import Util from '../core/util.mjs'
|
||||
|
||||
const name = 'service-core-runner'
|
||||
const util = new Util(import.meta.url)
|
||||
const log = getLog(name)
|
||||
const config = {
|
||||
name: name,
|
||||
testapp: {
|
||||
port: 31313,
|
||||
provider: 'static',
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
fs.rmSync(util.getPathFromRoot('./db.json'))
|
||||
} catch {}
|
||||
|
||||
GetDB(config, log, util.getPathFromRoot('./db.json')).then(async function(db) {
|
||||
const core = new Core(db, util, log, function() {})
|
||||
|
||||
console.log(db.data)
|
||||
|
||||
await core.init()
|
||||
|
||||
console.log(db.data)
|
||||
|
||||
db.data.core.testapp.versions.push({
|
||||
id: 'ok',
|
||||
version: 'ok',
|
||||
stable: 0,
|
||||
installed: true,
|
||||
})
|
||||
|
||||
await core.run()
|
||||
})
|
||||
.then(
|
||||
function() {
|
||||
log.info('core is running')
|
||||
},
|
||||
function(err) {
|
||||
log.error(err, 'Error starting runner')
|
||||
process.exit(1)
|
||||
})
|
17
test/testapp/ok/index.mjs
Normal file
17
test/testapp/ok/index.mjs
Normal file
|
@ -0,0 +1,17 @@
|
|||
export function start(http, port, ctx) {
|
||||
const server = http.createServer(function (req, res) {
|
||||
res.writeHead(200);
|
||||
res.end(JSON.stringify({ version: 'exampleindex' }))
|
||||
})
|
||||
|
||||
return new Promise(function(res, rej) {
|
||||
server.listen(port || 4000, '0.0.0.0', function(err) {
|
||||
if (err) {
|
||||
return rej(err)
|
||||
}
|
||||
ctx.log.event.info(`Server is listening on ${port} serving exampleindex`)
|
||||
ctx.log.info(`Server is listening on ${port} serving exampleindex`)
|
||||
res()
|
||||
})
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue