Massive development. Application finished. Core started
This commit is contained in:
parent
df7e1e5509
commit
758e61b8b1
21 changed files with 2117 additions and 831 deletions
|
@ -1,13 +1,20 @@
|
|||
import { EventEmitter } from 'events'
|
||||
import fs from 'fs/promises'
|
||||
import { request } from './client.mjs'
|
||||
import HttpServer from './http.mjs'
|
||||
import { defaults } from './defaults.mjs'
|
||||
|
||||
export default class Application extends EventEmitter {
|
||||
constructor(util, db, provider, name, opts = {}) {
|
||||
constructor(ctx, provider, name, opts = {}) {
|
||||
super()
|
||||
this.util = util
|
||||
this.db = db
|
||||
this.config = db.config[name] || { }
|
||||
this.ctx = {
|
||||
db: ctx.db,
|
||||
util: ctx.util,
|
||||
log: ctx.log,
|
||||
core: ctx.core,
|
||||
app: this,
|
||||
}
|
||||
this.config = defaults({}, this.ctx.db.config[name])
|
||||
this.provider = provider
|
||||
this.name = name
|
||||
this.updating = false
|
||||
|
@ -26,14 +33,18 @@ export default class Application extends EventEmitter {
|
|||
|
||||
// Apply defaults to config
|
||||
this.config.updateEvery = this.config.updateEvery || 180
|
||||
this.config.waitUntilFail = this.config.waitUntilFail || (60 * 1000)
|
||||
this.config.startWaitUntilFail = this.config.startWaitUntilFail || (60 * 1000)
|
||||
this.config.heartbeatTimeout = this.config.heartbeatTimeout || (3 * 1000)
|
||||
this.config.heartbeatAttempts = this.config.heartbeatAttempts || 5
|
||||
this.config.heartbeatAttemptsWait = this.config.heartbeatAttemptsWait || (2 * 1000)
|
||||
this.config.heartbeatPath = this.config.heartbeatPath || '/'
|
||||
|
||||
Object.assign(this, {
|
||||
setInterval: opts.setInterval || setInterval,
|
||||
fs: opts.fs || fs,
|
||||
})
|
||||
|
||||
this.db.addApplication(name)
|
||||
this.ctx.db.addApplication(name)
|
||||
}
|
||||
|
||||
startAutoupdater() {
|
||||
|
@ -42,10 +53,10 @@ export default class Application extends EventEmitter {
|
|||
let timer = this.setInterval(() => {
|
||||
this.update().then(
|
||||
() => {
|
||||
this.db.data.core[this.name].updater += 'Automatic update finished successfully. '
|
||||
this.ctx.db.data.core[this.name].updater += 'Automatic update finished successfully. '
|
||||
},
|
||||
(err) => {
|
||||
this.db.data.core[this.name].updater += 'Error while running automatic update: ' + err.message + '. '
|
||||
this.ctx.db.data.core[this.name].updater += 'Error while running automatic update: ' + err.message + '. '
|
||||
}
|
||||
)
|
||||
}, this.config.updateEvery * 60 * 1000)
|
||||
|
@ -53,8 +64,8 @@ export default class Application extends EventEmitter {
|
|||
}
|
||||
|
||||
updateLog(message) {
|
||||
this.db.data.core[this.name].updater += message
|
||||
this.db.log.info(message)
|
||||
this.ctx.db.data.core[this.name].updater += message
|
||||
this.ctx.db.log.info(message)
|
||||
return message
|
||||
}
|
||||
|
||||
|
@ -62,10 +73,10 @@ export default class Application extends EventEmitter {
|
|||
|
||||
update() {
|
||||
if (this.provider.static) {
|
||||
if (this.db.data.core[this.name].updater !== this.msgStatic) {
|
||||
this.db.data.core[this.name].updater = ''
|
||||
if (this.ctx.db.data.core[this.name].updater !== this.msgStatic) {
|
||||
this.ctx.db.data.core[this.name].updater = ''
|
||||
this.updateLog(this.msgStatic)
|
||||
return this.db.write()
|
||||
return this.ctx.db.write()
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
@ -76,11 +87,11 @@ export default class Application extends EventEmitter {
|
|||
return this._update()
|
||||
.then(() => {
|
||||
this.updating = false
|
||||
return this.db.write()
|
||||
return this.ctx.db.write()
|
||||
})
|
||||
.catch((err) => {
|
||||
this.updating = false
|
||||
return this.db.write()
|
||||
return this.ctx.db.write()
|
||||
.then(function() { return Promise.reject(err) })
|
||||
})
|
||||
}
|
||||
|
@ -96,7 +107,7 @@ export default class Application extends EventEmitter {
|
|||
}
|
||||
|
||||
async _update() {
|
||||
this.db.data.core[this.name].updater = ''
|
||||
this.ctx.db.data.core[this.name].updater = ''
|
||||
let cleanup = true
|
||||
let folder = ''
|
||||
let log = ''
|
||||
|
@ -111,7 +122,7 @@ export default class Application extends EventEmitter {
|
|||
log += this.updateLog(`Found ${latest.version}. `) + '\n'
|
||||
|
||||
// If the versino matches the latest installed, then there's nothing to do
|
||||
if (this.db.data.core[this.name].latestInstalled === latest.version) {
|
||||
if (this.ctx.db.data.core[this.name].latestInstalled === latest.version) {
|
||||
this.updateLog('Already up to date, nothing to do. ')
|
||||
return
|
||||
}
|
||||
|
@ -121,7 +132,7 @@ export default class Application extends EventEmitter {
|
|||
latest.id = latest.version
|
||||
|
||||
// check to see if we already have this version in our database.
|
||||
var found = this.db.get(this.db.data.core[this.name].versions, latest.id)
|
||||
var found = this.ctx.db.get(this.ctx.db.data.core[this.name].versions, latest.id)
|
||||
if (found) {
|
||||
// Check if the existing version found was already installed.
|
||||
if (found.installed) {
|
||||
|
@ -155,18 +166,18 @@ export default class Application extends EventEmitter {
|
|||
} else {
|
||||
// This is a new version, mark it with stable tag of zero.
|
||||
latest.stable = 0
|
||||
this.db.upsertFirst(this.db.data.core[this.name].versions, latest)
|
||||
this.ctx.db.upsertFirst(this.ctx.db.data.core[this.name].versions, latest)
|
||||
}
|
||||
|
||||
// The target file for the archive and the target folder for new our version
|
||||
let target = this.util.getPathFromRoot(`./${this.name}/${latest.version}/file${this.util.getExtension(latest.filename)}`)
|
||||
folder = this.util.getPathFromRoot(`./${this.name}/${latest.version}`)
|
||||
let target = this.ctx.util.getPathFromRoot(`./${this.name}/${latest.version}/file${this.ctx.util.getExtension(latest.filename)}`)
|
||||
folder = this.ctx.util.getPathFromRoot(`./${this.name}/${latest.version}`)
|
||||
|
||||
// Create it in case it does not exist.
|
||||
await this.fs.mkdir(folder, { recursive: true })
|
||||
|
||||
log += this.updateLog(`Downloading ${latest.link} to ${target}. `) + '\n'
|
||||
await this.db.write()
|
||||
await this.ctx.db.write()
|
||||
|
||||
// Download the latest version using the provider in question.
|
||||
await this.provider.downloadVersion(latest, target)
|
||||
|
@ -176,10 +187,10 @@ export default class Application extends EventEmitter {
|
|||
})
|
||||
|
||||
log += '\n' + this.updateLog(`Extracting ${target}. `) + '\n'
|
||||
await this.db.write()
|
||||
await this.ctx.db.write()
|
||||
|
||||
// Download was successful, extract the archived file that we downloaded
|
||||
await this.util.extractFile(target, function(msg) {
|
||||
await this.ctx.util.extractFile(target, function(msg) {
|
||||
log += msg
|
||||
}).catch(function(err) {
|
||||
latest.failtodownload = (latest.failtodownload || 0) + 1
|
||||
|
@ -196,7 +207,7 @@ export default class Application extends EventEmitter {
|
|||
// check if the version we downloaded had index.mjs. If this is
|
||||
// missing then either the extracting or download failed without erroring
|
||||
// or the archived is borked.
|
||||
await this.fs.stat(this.util.getPathFromRoot(`./${this.name}/${latest.version}/index.mjs`))
|
||||
await this.fs.stat(this.ctx.util.getPathFromRoot(`./${this.name}/${latest.version}/index.mjs`))
|
||||
.catch((err) => {
|
||||
latest.failtodownload = (latest.failtodownload || 0) + 1
|
||||
log += this.updateLog('Version did not include or was missing index.mjs. ') + '\n'
|
||||
|
@ -212,16 +223,16 @@ export default class Application extends EventEmitter {
|
|||
// Check if we have a package.json file. If we do, we need to run
|
||||
// npm install. If we don't then this application either has all the
|
||||
// required packages or it doesn't need them to run
|
||||
let packageStat = await this.fs.stat(this.util.getPathFromRoot(`./${this.name}/${latest.version}/package.json`))
|
||||
let packageStat = await this.fs.stat(this.ctx.util.getPathFromRoot(`./${this.name}/${latest.version}/package.json`))
|
||||
.catch(function() { return null })
|
||||
|
||||
if (packageStat) {
|
||||
log += this.updateLog(`running npm install --production. `) + '\n'
|
||||
await this.db.write()
|
||||
await this.ctx.db.write()
|
||||
|
||||
// For some weird reason, --loglevel=notice is required otherwise
|
||||
// we get practically zero log output.
|
||||
await this.util.runCommand(
|
||||
await this.ctx.util.runCommand(
|
||||
'npm.cmd',
|
||||
['install', '--production', '--no-optional', '--no-package-lock', '--no-audit', '--loglevel=notice'],
|
||||
folder,
|
||||
|
@ -255,33 +266,45 @@ export default class Application extends EventEmitter {
|
|||
// If we reached here then everything went swimmingly. Mark the version
|
||||
// as being installed and attach the install log to it.
|
||||
log += this.updateLog(`Finished updating ${this.name} to version ${latest.version}.`) + '\n'
|
||||
this.db.data.core[this.name].latestInstalled = latest.version
|
||||
this.ctx.db.data.core[this.name].latestInstalled = latest.version
|
||||
latest.installed = true
|
||||
latest.log = log
|
||||
}
|
||||
|
||||
registerModule(module) {
|
||||
registerModule(module, version = '') {
|
||||
if (module && typeof(module) === 'function') {
|
||||
return this.registerModule({ start: module })
|
||||
}
|
||||
if (!module || typeof(module) !== 'object' || typeof(module.start) !== 'function') {
|
||||
throw new Error(`Application ${this.name} registerModule was called with a non module missing start function`)
|
||||
throw new Error(`Application ${this.name}${version ? ' version ' + version : '' } registerModule was called with a non module missing start function`)
|
||||
}
|
||||
|
||||
this.module = module
|
||||
}
|
||||
|
||||
async runVersion(version) {
|
||||
let module = this.module
|
||||
this.ctx.db.data.core[this.name].active = version
|
||||
await this.ctx.db.write()
|
||||
|
||||
let errTimeout = new Error(`Application ${this.name} version ${version} timed out (took over ${this.config.waitUntilFail}ms) while running start()`)
|
||||
if (version !== 'static') {
|
||||
let indexPath = this.ctx.util.getPathFromRoot(`./${this.name}/${version}/index.mjs`)
|
||||
await this.fs.stat(indexPath).catch((err) => {
|
||||
return Promise.reject(new Error(`Application ${this.name} version ${version} was missing index.mjs: ${err.message}`))
|
||||
})
|
||||
let module = await import(this.ctx.util.getUrlFromRoot(`./${this.name}/${version}/index.mjs`)).catch((err) => {
|
||||
return Promise.reject(new Error(`Application ${this.name} version ${version} failed to load index.mjs: ${err.message}`))
|
||||
})
|
||||
this.registerModule(module, version)
|
||||
}
|
||||
|
||||
let errTimeout = new Error(`Application ${this.name} version ${version} timed out (took over ${this.config.startWaitUntilFail}ms) while running start()`)
|
||||
|
||||
await new Promise((res, rej) => {
|
||||
setTimeout(() => {
|
||||
rej(errTimeout)
|
||||
}, this.config.waitUntilFail)
|
||||
}, this.config.startWaitUntilFail)
|
||||
|
||||
let startRes = module.start(this.db, this.db.log, this.http, this.config.port)
|
||||
let startRes = this.module.start(this.http, this.config.port, this.ctx)
|
||||
if (startRes && startRes.then) {
|
||||
return startRes.then(res, rej)
|
||||
}
|
||||
|
@ -291,5 +314,22 @@ export default class Application extends EventEmitter {
|
|||
if (!this.http.active) {
|
||||
return Promise.reject(new Error(`Application ${this.name} version ${version} did not call http.createServer()`))
|
||||
}
|
||||
|
||||
let lastErr = null
|
||||
|
||||
for (let i = 0; i < this.config.heartbeatAttempts; i++) {
|
||||
try {
|
||||
await request({ timeout: this.config.heartbeatAttemptsWait }, `http://localhost:${this.config.port}` + this.config.heartbeatPath, null, 0, true)
|
||||
return
|
||||
} catch (err) {
|
||||
lastErr = err
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(new Error(`Application ${this.name} version ${version} failed to start properly: ${lastErr.message}`))
|
||||
}
|
||||
|
||||
closeServer() {
|
||||
return this.http.closeServer()
|
||||
}
|
||||
}
|
|
@ -46,13 +46,13 @@ export function request(config, path, filePath = null, redirects, fastRaw = fals
|
|||
if (config.token) {
|
||||
headers['Authorization'] = `token ${config.token}`
|
||||
}
|
||||
let timeout = fastRaw ? 5000 : config.timeout || 10000
|
||||
let timeout = config.timeout || 10000
|
||||
|
||||
let timedout = false
|
||||
let timer = setTimeout(function() {
|
||||
timedout = true
|
||||
if (req) { req.destroy() }
|
||||
reject(new Error(`Request ${path} timed out out after ${timeout}`))
|
||||
reject(new Error(`Request ${path} timed out after ${timeout}ms`))
|
||||
}, timeout)
|
||||
|
||||
req = h.request({
|
||||
|
|
539
core/core.mjs
539
core/core.mjs
|
@ -1,499 +1,68 @@
|
|||
import fs from 'fs'
|
||||
import { EventEmitter } from 'events'
|
||||
import { request } from './client.mjs'
|
||||
import HttpServer from './http.mjs'
|
||||
import Application from './application.mjs'
|
||||
import Util from './util.mjs'
|
||||
import { Low } from 'lowdb'
|
||||
|
||||
const fsp = fs.promises
|
||||
export default class Core {
|
||||
static providers = new Map()
|
||||
static addProvider(name, provider) {
|
||||
if (!name || typeof(name) !== 'string')
|
||||
throw new Error('addProvider name must be a string')
|
||||
if (typeof(provider) !== 'function')
|
||||
throw new Error(`addProvider ${name} provider must be a class`)
|
||||
|
||||
let test = new provider({})
|
||||
if (typeof(test.checkConfig) !== 'function')
|
||||
throw new Error(`addProvider ${name} provider class missing checkConfig`)
|
||||
if (typeof(test.getLatestVersion) !== 'function')
|
||||
throw new Error(`addProvider ${name} provider class missing getLatestVersion`)
|
||||
|
||||
Core.providers.set(name, provider)
|
||||
}
|
||||
|
||||
constructor(db, util, log, restart) {
|
||||
// some sanity checks
|
||||
if (typeof(restart) !== 'function') throw new Error('restart parameter was not a function')
|
||||
if (!log || typeof(log) !== 'object') throw new Error('log parameter was invalid')
|
||||
if (typeof(log.event) !== 'object') throw new Error('log parameter was invalid')
|
||||
if (typeof(log.info) !== 'function'
|
||||
|| typeof(log.warn) !== 'function'
|
||||
|| typeof(log.error) !== 'function'
|
||||
|| typeof(log.event.info) !== 'function'
|
||||
|| typeof(log.event.warn) !== 'function'
|
||||
|| typeof(log.event.error) !== 'function') throw new Error('log parameter was invalid')
|
||||
if (!util || !(util instanceof Util)) throw new Error('util not instance of Util')
|
||||
if (!db || !(db instanceof Low)) throw new Error('db not instance of Low')
|
||||
|
||||
export default class Core extends EventEmitter{
|
||||
constructor(util, config, db, log, closeCb) {
|
||||
super()
|
||||
process.stdin.resume()
|
||||
this.http = new HttpServer()
|
||||
this.util = util
|
||||
this.config = config
|
||||
this.db = db
|
||||
this.util = util
|
||||
this.log = log
|
||||
this._close = closeCb
|
||||
this._activeCrashHandler = null
|
||||
this.appRunning = false
|
||||
this.manageRunning = false
|
||||
this.monitoring = false
|
||||
this._appUpdating = {
|
||||
fresh: true,
|
||||
updating: false,
|
||||
starting: false,
|
||||
logs: '',
|
||||
}
|
||||
this._manageUpdating = {
|
||||
fresh: true,
|
||||
updating: false,
|
||||
starting: false,
|
||||
logs: '',
|
||||
}
|
||||
|
||||
this.db.set('core.manageActive', null)
|
||||
.set('core.appActive', null)
|
||||
.write().then()
|
||||
this.restart = restart
|
||||
this.applications = []
|
||||
this.applicationMap = new Map()
|
||||
}
|
||||
|
||||
startMonitor() {
|
||||
if (this.monitoring) return
|
||||
this.log.info('[Scheduler] Automatic updater has been turned on. Will check for updates every 3 hours')
|
||||
let updating = false
|
||||
|
||||
this.monitoring = setInterval(async () => {
|
||||
if (updating) return
|
||||
updating = true
|
||||
this.log.info('[Scheduler] Starting automatic check for latest version of app and manage')
|
||||
|
||||
try {
|
||||
await this.installLatestVersion('app')
|
||||
await this.installLatestVersion('manage')
|
||||
} catch(err) {
|
||||
this.log.error(err, 'Error checking for latest versions')
|
||||
this.log.event.error('Error checking for latest versions: ' + err.message)
|
||||
updating = false
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.hasNewVersionAvailable('app') || !this.appRunning) {
|
||||
await this.tryStartProgram('app')
|
||||
}
|
||||
} catch(err) {
|
||||
this.log.error(err, 'Unknown error occured attempting to app')
|
||||
this.log.event.error('Unknown error starting app: ' + err.message)
|
||||
}
|
||||
try {
|
||||
if (this.hasNewVersionAvailable('manage') || !this.manageRunning) {
|
||||
await this.tryStartProgram('manage')
|
||||
}
|
||||
} catch(err) {
|
||||
this.log.error(err, 'Unknown error occured attempting to start manage')
|
||||
this.log.event.error('Unknown error starting manage: ' + err.message)
|
||||
}
|
||||
updating = false
|
||||
}, 1000 * 60 * 60 * 3) // every 3 hours
|
||||
getApplication(name) {
|
||||
return this.applicationMap.get(name)
|
||||
}
|
||||
|
||||
restart() {
|
||||
this._close()
|
||||
}
|
||||
async init() {
|
||||
this.util.verifyConfig(this.db.config)
|
||||
|
||||
status() {
|
||||
return {
|
||||
app: this.appRunning,
|
||||
manage: this.manageRunning,
|
||||
appUpdating: this._appUpdating.updating,
|
||||
manageUpdating: this._manageUpdating.updating,
|
||||
appStarting: this._appUpdating.starting,
|
||||
manageStarting: this._manageUpdating.starting,
|
||||
}
|
||||
}
|
||||
let names = this.util.getAppNames(this.db.config)
|
||||
|
||||
async getLatestVersion(active, name) {
|
||||
// Example: 'https://api.github.com/repos/thething/sc-helloworld/releases'
|
||||
this.logActive(name, active, `Updater: Fetching release info from: https://api.github.com/repos/${this.config[name + 'Repository']}/releases\n`)
|
||||
for (let name of names) {
|
||||
let provConstructor = Core.providers.get(this.db.config[name].provider)
|
||||
let provider = new provConstructor(this.db.config[name])
|
||||
await provider.checkConfig(this.db.config[name])
|
||||
|
||||
let result = await request(this.config, `https://api.github.com/repos/${this.config[name + 'Repository']}/releases`)
|
||||
|
||||
let items = result.body.filter(function(item) {
|
||||
if (!item.assets.length) return false
|
||||
for (let i = 0; i < item.assets.length; i++) {
|
||||
if (item.assets[i].name.endsWith('-sc.zip')) return true
|
||||
}
|
||||
})
|
||||
|
||||
if (items && items.length) {
|
||||
for (let x = 0; x < items.length; x++) {
|
||||
let item = items[x]
|
||||
for (let i = 0; i < item.assets.length; i++) {
|
||||
if (item.assets[i].name.endsWith('-sc.zip')) {
|
||||
if (this.db.get('core.' + name + 'LatestInstalled').value() === item.name) {
|
||||
this.logActive(name, active, `Updater: Latest version already installed, exiting early\n`)
|
||||
return null
|
||||
}
|
||||
this.logActive(name, active, `Updater: Found version ${item.name} with file ${item.assets[i].name}\n`)
|
||||
|
||||
await this.db.set(`core.${name}LatestVersion`, item.name)
|
||||
.write()
|
||||
this.emit('dbupdated', {})
|
||||
|
||||
return {
|
||||
name: item.name,
|
||||
filename: item.assets[i].name,
|
||||
url: item.assets[i].browser_download_url,
|
||||
description: item.body,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
logActive(name, active, logline, doNotPrint = false) {
|
||||
if (!doNotPrint) {
|
||||
this.log.info(`[${name}] ` + logline.replace(/\n/g, ''))
|
||||
}
|
||||
active.logs += logline
|
||||
this.emit(name + 'log', active)
|
||||
}
|
||||
|
||||
getProgramLogs(name) {
|
||||
if (name === 'app' && this._appUpdating.logs) {
|
||||
return this._appUpdating.logs
|
||||
} else if (name === 'manage' && this._manageUpdating.logs) {
|
||||
return this._manageUpdating.logs
|
||||
}
|
||||
|
||||
let latestInstalled = this.db.get('core.' + name + 'LatestInstalled').value()
|
||||
let latestVersion = this.db.get('core.' + name + 'LatestVersion').value()
|
||||
if (latestVersion) {
|
||||
let value = this.db.get(`core_${name}History`).getById(latestVersion).value()
|
||||
if (value) return value.logs
|
||||
}
|
||||
if (latestInstalled) {
|
||||
let value = this.db.get(`core_${name}History`).getById(latestInstalled).value()
|
||||
if (value) return value.logs
|
||||
}
|
||||
return '< no logs found >'
|
||||
}
|
||||
|
||||
async installVersion(name, active, version) {
|
||||
if (fs.existsSync(this.util.getPathFromRoot(`./${name}/` + version.name))) {
|
||||
await this.util.runCommand('rmdir', ['/S', '/Q', `"${this.util.getPathFromRoot(`./${name}/` + version.name)}"`])
|
||||
}
|
||||
if (!fs.existsSync(this.util.getPathFromRoot(`./${name}/`))) {
|
||||
await fsp.mkdir(this.util.getPathFromRoot(`./${name}/`))
|
||||
}
|
||||
try {
|
||||
await fsp.mkdir(this.util.getPathFromRoot(`./${name}/` + version.name))
|
||||
} catch(err) {
|
||||
if (err.code !== 'EEXIST') {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
// await fsp.mkdir(this.util.getPathFromRoot(`./${name}/` + version.name + '/node_modules'))
|
||||
this.logActive(name, active, `Installer: Downloading ${version.name} (${version.url}) to ${version.name + '/' + version.name + '.zip'}\n`)
|
||||
let filePath = this.util.getPathFromRoot(`./${name}/` + version.name + '/' + version.name + '.zip')
|
||||
await request(this.config, version.url, filePath)
|
||||
this.logActive(name, active, `Installer: Downloading finished, starting extraction\n`)
|
||||
await this.util.runCommand(
|
||||
'"C:\\Program Files\\7-Zip\\7z.exe"',
|
||||
['x', `"${filePath}"`],
|
||||
this.util.getPathFromRoot(`./${name}/` + version.name + '/'),
|
||||
this.logActive.bind(this, name, active)
|
||||
)
|
||||
|
||||
if (!fs.existsSync(this.util.getPathFromRoot(`./${name}/` + version.name + '/index.mjs'))) {
|
||||
this.logActive(name, active, `\nInstaller: ERROR: Missing index.mjs in the folder, exiting\n`)
|
||||
throw new Error(`Missing index.mjs in ${this.util.getPathFromRoot(`./${name}/` + version.name + '/index.mjs')}`)
|
||||
}
|
||||
|
||||
this.logActive(name, active, `\nInstaller: Starting npm install\n`)
|
||||
|
||||
await this.util.runCommand(
|
||||
'npm.cmd',
|
||||
['install', '--production', '--no-optional', '--no-package-lock', '--no-audit'],
|
||||
this.util.getPathFromRoot(`./${name}/` + version.name + '/'),
|
||||
this.logActive.bind(this, name, active)
|
||||
)
|
||||
|
||||
await this.db.set(`core.${name}LatestInstalled`, version.name)
|
||||
.write()
|
||||
this.emit('dbupdated', {})
|
||||
|
||||
this.logActive(name, active, `\nInstaller: Successfully installed ${version.name}\n`)
|
||||
}
|
||||
|
||||
getActive(name) {
|
||||
if (name === 'app') {
|
||||
return this._appUpdating
|
||||
} else if (name === 'manage') {
|
||||
return this._manageUpdating
|
||||
} else {
|
||||
throw new Error('Invalid name: ' + name)
|
||||
}
|
||||
}
|
||||
|
||||
async startModule(module, port) {
|
||||
let out = await module.start(this.config, this.db, this.log, this, this.http, port)
|
||||
if (out && out.then) {
|
||||
await out
|
||||
}
|
||||
if (!this.http.getCurrentServer()) {
|
||||
this.log.warn('Module did not call http.createServer')
|
||||
}
|
||||
}
|
||||
|
||||
hasNewVersionAvailable(name) {
|
||||
let newestVersion = this.db.get(`core.${name}LatestInstalled`).value()
|
||||
if (!newestVersion) return false
|
||||
|
||||
let history = this.db.get(`core_${name}History`).getById(newestVersion).value()
|
||||
if (history.installed && history.stable === 0) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
async tryStartProgram(name) {
|
||||
let active = this.getActive(name)
|
||||
|
||||
if (this[name + 'Running'] && !this.hasNewVersionAvailable(name)) {
|
||||
this.log.event.warn('Attempting to start ' + name + ' which is already running')
|
||||
this.log.warn('Attempting to start ' + name + ' which is already running')
|
||||
this.logActive(name, active, `Runner: Attempting to start it but it is already running\n`, true)
|
||||
return
|
||||
}
|
||||
active.starting = true
|
||||
|
||||
if (this[name + 'Running']) {
|
||||
let success = await this.http.closeServer(name)
|
||||
if (!success) {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
this.logActive(name, active, `Runner: Found new version but server could not be shut down, restarting service core\n`)
|
||||
await new Promise(() => {
|
||||
this.log.event.warn('Found new version of ' + name + ' but server could not be shut down gracefully, restarting...', null, () => {
|
||||
process.exit(100)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.logActive(name, active, `Runner: Found new version but server could not be shut down\n`)
|
||||
return
|
||||
}
|
||||
}
|
||||
this[name + 'Running'] = false
|
||||
this.emit('statusupdated', {})
|
||||
}
|
||||
|
||||
let history = this.db.get(`core_${name}History`)
|
||||
.filter('installed')
|
||||
.orderBy('installed', 'desc')
|
||||
.value()
|
||||
this.logActive(name, active, `Runner: Finding available version\n`)
|
||||
|
||||
for (let i = 0; i < history.length; i++) {
|
||||
if ((history[i].stable === -1 && !active.fresh)
|
||||
|| (history[i].stable < -1)) {
|
||||
this.logActive(name, active, `Runner: Skipping version ${history[i].name} due to marked as unstable\n`)
|
||||
continue
|
||||
}
|
||||
|
||||
await this.db.set(`core.${name}Active`, history[i].name)
|
||||
.write()
|
||||
this.emit('dbupdated', {})
|
||||
|
||||
let running = await this.tryStartProgramVersion(name, active, history[i].name)
|
||||
if (running) {
|
||||
history[i].stable = 1
|
||||
} else {
|
||||
if (active.fresh || history[i].stable === -1) {
|
||||
history[i].stable = -2
|
||||
} else {
|
||||
history[i].stable = -1
|
||||
}
|
||||
await this.db.set(`core.${name}Active`, null)
|
||||
.write()
|
||||
this.emit('dbupdated', {})
|
||||
}
|
||||
active.fresh = false
|
||||
|
||||
await this.db.get(`core_${name}History`).updateById(history[i].id, history[i].stable).write()
|
||||
if (history[i].stable > 0) break
|
||||
}
|
||||
|
||||
if (!this.db.get(`core.${name}Active`).value()) {
|
||||
this.logActive(name, active, `Runner: Could not find any available stable version of ${name}\n`)
|
||||
this.log.error('Unable to start ' + name)
|
||||
this.log.event.error('Unable to start ' + name)
|
||||
}
|
||||
|
||||
active.starting = false
|
||||
}
|
||||
|
||||
programCrashed(name, version, active, oldStable) {
|
||||
let newStable = -2
|
||||
console.log('EXITING:', oldStable, active)
|
||||
if (oldStable === 0 && !active.fresh) {
|
||||
newStable = -1
|
||||
}
|
||||
let temp = this.db.get(`core_${name}History`).getById(version).set('stable', newStable )
|
||||
temp.value() // Trigger update on __wrapped__
|
||||
fs.writeFileSync(this.db.adapterFilePath, JSON.stringify(temp.__wrapped__, null, 2))
|
||||
}
|
||||
|
||||
async tryStartProgramVersion(name, active, version) {
|
||||
if (!version) return false
|
||||
this.logActive(name, active, `Runner: Attempting to start ${version}\n`)
|
||||
let indexPath = this.util.getUrlFromRoot(`./${name}/` + version + '/index.mjs')
|
||||
let module
|
||||
|
||||
try {
|
||||
this.logActive(name, active, `Runner: Loading ${indexPath}\n`)
|
||||
module = await import(indexPath)
|
||||
} catch (err) {
|
||||
this.logActive(name, active, `Runner: Error importing module\n`, true)
|
||||
this.logActive(name, active, `${err.stack}\n`, true)
|
||||
this.log.error(err, `Failed to load ${indexPath}`)
|
||||
return false
|
||||
}
|
||||
|
||||
let checkTimeout = null
|
||||
let oldStable = this.db.get(`core_${name}History`).getById(version).value().stable
|
||||
this._activeCrashHandler = this.programCrashed.bind(this, name, version, active, oldStable)
|
||||
process.once('exit', this._activeCrashHandler)
|
||||
try {
|
||||
let port = name === 'app' ? this.config.port : this.config.managePort
|
||||
await new Promise((res, rej) => {
|
||||
checkTimeout = setTimeout(function() {
|
||||
rej(new Error('Program took longer than 60 seconds to resolve promise'))
|
||||
}, 60 * 1000)
|
||||
|
||||
this.logActive(name, active, `Runner: Starting module\n`)
|
||||
|
||||
try {
|
||||
this.http.setContext(name)
|
||||
this.startModule(module, port)
|
||||
.then(res, rej)
|
||||
} catch (err) {
|
||||
rej(err)
|
||||
}
|
||||
})
|
||||
clearTimeout(checkTimeout)
|
||||
|
||||
await this.checkProgramRunning(name, active, port)
|
||||
process.off('exit', this._activeCrashHandler)
|
||||
} catch (err) {
|
||||
clearTimeout(checkTimeout)
|
||||
process.off('exit', this._activeCrashHandler)
|
||||
await this.http.closeServer(name)
|
||||
|
||||
this.logActive(name, active, `Runner: Error starting\n`, true)
|
||||
this.logActive(name, active, `${err.stack}\n`, true)
|
||||
this.log.error(err, `Failed to start ${name}`)
|
||||
return false
|
||||
}
|
||||
this._activeCrashHandler = null
|
||||
|
||||
this.logActive(name, active, `Runner: Successfully started version ${version}\n`)
|
||||
await this.db.set(`core.${name}Active`, version)
|
||||
.write()
|
||||
|
||||
if (name === 'app') {
|
||||
this.appRunning = true
|
||||
} else {
|
||||
this.manageRunning = true
|
||||
}
|
||||
this.emit('statusupdated', {})
|
||||
|
||||
this.logActive(name, active, `Runner: Module is running successfully\n`)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
async checkProgramRunning(name, active, port) {
|
||||
this.logActive(name, active, `Checker: Testing out module port ${port}\n`)
|
||||
let start = new Date()
|
||||
let error = null
|
||||
let success = false
|
||||
|
||||
while (new Date() - start < 10 * 1000) {
|
||||
try {
|
||||
let check = await request(this.config, `http://localhost:${port}`, null, 0, true)
|
||||
success = true
|
||||
break
|
||||
} catch(err) {
|
||||
this.logActive(name, active, `Checker: ${err.message}, retrying in 3 seconds\n`)
|
||||
error = err
|
||||
await new Promise(function(res) { setTimeout(res, 3000)})
|
||||
}
|
||||
}
|
||||
if (success) return true
|
||||
throw error || new Error('Checking server failed')
|
||||
}
|
||||
|
||||
async installLatestVersion(name) {
|
||||
if (!this.config[name + 'Repository']) {
|
||||
if (name === 'app') {
|
||||
this.log.error(name + ' Repository was missing from config')
|
||||
this.log.event.error(name + ' Repository was missing from config')
|
||||
} else {
|
||||
this.log.warn(name + ' Repository was missing from config')
|
||||
this.log.event.warn(name + ' Repository was missing from config')
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let active = this.getActive(name)
|
||||
let oldLogs = active.logs || ''
|
||||
if (oldLogs) {
|
||||
oldLogs += '\n'
|
||||
}
|
||||
active.logs = ''
|
||||
active.updating = true
|
||||
|
||||
this.emit('statusupdated', {})
|
||||
this.logActive(name, active, `Installer: Checking for updates at time: ${new Date().toISOString().replace('T', ' ').split('.')[0]}\n`)
|
||||
|
||||
let version = null
|
||||
let installed = false
|
||||
let found = false
|
||||
try {
|
||||
version = await this.getLatestVersion(active, name)
|
||||
if (version) {
|
||||
let core = this.db.get('core').value()
|
||||
let fromDb = this.db.get(`core_${name}History`).getById(version.name).value()
|
||||
if (!fromDb || !fromDb.installed) {
|
||||
let oldVersion = core[name + 'Current'] || '<none>'
|
||||
this.logActive(name, active, `Installer: Updating from ${oldVersion} to ${version.name}\n`)
|
||||
await this.installVersion(name, active, version)
|
||||
this.logActive(name, active, `Installer: Finished: ${new Date().toISOString().replace('T', ' ').split('.')[0]}\n`)
|
||||
installed = new Date()
|
||||
} else {
|
||||
found = true
|
||||
this.logActive(name, active, `Installer: Version ${version.name} already installed\n`)
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
this.logActive(name, active, '\n', true)
|
||||
this.logActive(name, active, `Installer: Exception occured while updating ${name}\n`, true)
|
||||
this.logActive(name, active, err.stack, true)
|
||||
this.log.error('Error while updating ' + name, err)
|
||||
}
|
||||
active.updating = false
|
||||
if (version && !found) {
|
||||
await this.db.get(`core_${name}History`).upsert({
|
||||
id: version.name,
|
||||
name: version.name,
|
||||
filename: version.filename,
|
||||
url: version.url,
|
||||
description: version.description,
|
||||
logs: active.logs,
|
||||
stable: 0,
|
||||
installed: installed && installed.toISOString(),
|
||||
}).write()
|
||||
}
|
||||
active.logs = oldLogs + active.logs
|
||||
this.emit(name + 'log', active)
|
||||
this.emit('statusupdated', {})
|
||||
}
|
||||
|
||||
async start(name) {
|
||||
var version = this.db.get('core.' + name + 'LatestInstalled').value()
|
||||
if (version) {
|
||||
await this.tryStartProgram(name)
|
||||
}
|
||||
|
||||
await this.installLatestVersion(name)
|
||||
|
||||
if (version !== this.db.get('core.' + name + 'LatestInstalled').value()) {
|
||||
if (!this[name + 'Running'] || this.hasNewVersionAvailable(name)) {
|
||||
await this.tryStartProgram(name)
|
||||
}
|
||||
let application = new Application({
|
||||
db: this.db,
|
||||
util: this.util,
|
||||
log: this.log,
|
||||
core: this,
|
||||
}, provider, name)
|
||||
this.applications.push(application)
|
||||
this.applicationMap.set(name, application)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
499
core/core_old.mjs
Normal file
499
core/core_old.mjs
Normal file
|
@ -0,0 +1,499 @@
|
|||
import fs from 'fs'
|
||||
import { EventEmitter } from 'events'
|
||||
import { request } from './client.mjs'
|
||||
import HttpServer from './http.mjs'
|
||||
|
||||
const fsp = fs.promises
|
||||
|
||||
export default class Core extends EventEmitter{
|
||||
constructor(util, config, db, log, closeCb) {
|
||||
super()
|
||||
process.stdin.resume()
|
||||
this.http = new HttpServer()
|
||||
this.util = util
|
||||
this.config = config
|
||||
this.db = db
|
||||
this.log = log
|
||||
this._close = closeCb
|
||||
this._activeCrashHandler = null
|
||||
this.appRunning = false
|
||||
this.manageRunning = false
|
||||
this.monitoring = false
|
||||
this._appUpdating = {
|
||||
fresh: true,
|
||||
updating: false,
|
||||
starting: false,
|
||||
logs: '',
|
||||
}
|
||||
this._manageUpdating = {
|
||||
fresh: true,
|
||||
updating: false,
|
||||
starting: false,
|
||||
logs: '',
|
||||
}
|
||||
|
||||
this.db.set('core.manageActive', null)
|
||||
.set('core.appActive', null)
|
||||
.write().then()
|
||||
}
|
||||
|
||||
startMonitor() {
|
||||
if (this.monitoring) return
|
||||
this.log.info('[Scheduler] Automatic updater has been turned on. Will check for updates every 3 hours')
|
||||
let updating = false
|
||||
|
||||
this.monitoring = setInterval(async () => {
|
||||
if (updating) return
|
||||
updating = true
|
||||
this.log.info('[Scheduler] Starting automatic check for latest version of app and manage')
|
||||
|
||||
try {
|
||||
await this.installLatestVersion('app')
|
||||
await this.installLatestVersion('manage')
|
||||
} catch(err) {
|
||||
this.log.error(err, 'Error checking for latest versions')
|
||||
this.log.event.error('Error checking for latest versions: ' + err.message)
|
||||
updating = false
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.hasNewVersionAvailable('app') || !this.appRunning) {
|
||||
await this.tryStartProgram('app')
|
||||
}
|
||||
} catch(err) {
|
||||
this.log.error(err, 'Unknown error occured attempting to app')
|
||||
this.log.event.error('Unknown error starting app: ' + err.message)
|
||||
}
|
||||
try {
|
||||
if (this.hasNewVersionAvailable('manage') || !this.manageRunning) {
|
||||
await this.tryStartProgram('manage')
|
||||
}
|
||||
} catch(err) {
|
||||
this.log.error(err, 'Unknown error occured attempting to start manage')
|
||||
this.log.event.error('Unknown error starting manage: ' + err.message)
|
||||
}
|
||||
updating = false
|
||||
}, 1000 * 60 * 60 * 3) // every 3 hours
|
||||
}
|
||||
|
||||
restart() {
|
||||
this._close()
|
||||
}
|
||||
|
||||
status() {
|
||||
return {
|
||||
app: this.appRunning,
|
||||
manage: this.manageRunning,
|
||||
appUpdating: this._appUpdating.updating,
|
||||
manageUpdating: this._manageUpdating.updating,
|
||||
appStarting: this._appUpdating.starting,
|
||||
manageStarting: this._manageUpdating.starting,
|
||||
}
|
||||
}
|
||||
|
||||
async getLatestVersion(active, name) {
|
||||
// Example: 'https://api.github.com/repos/thething/sc-helloworld/releases'
|
||||
this.logActive(name, active, `Updater: Fetching release info from: https://api.github.com/repos/${this.config[name + 'Repository']}/releases\n`)
|
||||
|
||||
let result = await request(this.config, `https://api.github.com/repos/${this.config[name + 'Repository']}/releases`)
|
||||
|
||||
let items = result.body.filter(function(item) {
|
||||
if (!item.assets.length) return false
|
||||
for (let i = 0; i < item.assets.length; i++) {
|
||||
if (item.assets[i].name.endsWith('-sc.zip')) return true
|
||||
}
|
||||
})
|
||||
|
||||
if (items && items.length) {
|
||||
for (let x = 0; x < items.length; x++) {
|
||||
let item = items[x]
|
||||
for (let i = 0; i < item.assets.length; i++) {
|
||||
if (item.assets[i].name.endsWith('-sc.zip')) {
|
||||
if (this.db.get('core.' + name + 'LatestInstalled').value() === item.name) {
|
||||
this.logActive(name, active, `Updater: Latest version already installed, exiting early\n`)
|
||||
return null
|
||||
}
|
||||
this.logActive(name, active, `Updater: Found version ${item.name} with file ${item.assets[i].name}\n`)
|
||||
|
||||
await this.db.set(`core.${name}LatestVersion`, item.name)
|
||||
.write()
|
||||
this.emit('dbupdated', {})
|
||||
|
||||
return {
|
||||
name: item.name,
|
||||
filename: item.assets[i].name,
|
||||
url: item.assets[i].browser_download_url,
|
||||
description: item.body,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
logActive(name, active, logline, doNotPrint = false) {
|
||||
if (!doNotPrint) {
|
||||
this.log.info(`[${name}] ` + logline.replace(/\n/g, ''))
|
||||
}
|
||||
active.logs += logline
|
||||
this.emit(name + 'log', active)
|
||||
}
|
||||
|
||||
getProgramLogs(name) {
|
||||
if (name === 'app' && this._appUpdating.logs) {
|
||||
return this._appUpdating.logs
|
||||
} else if (name === 'manage' && this._manageUpdating.logs) {
|
||||
return this._manageUpdating.logs
|
||||
}
|
||||
|
||||
let latestInstalled = this.db.get('core.' + name + 'LatestInstalled').value()
|
||||
let latestVersion = this.db.get('core.' + name + 'LatestVersion').value()
|
||||
if (latestVersion) {
|
||||
let value = this.db.get(`core_${name}History`).getById(latestVersion).value()
|
||||
if (value) return value.logs
|
||||
}
|
||||
if (latestInstalled) {
|
||||
let value = this.db.get(`core_${name}History`).getById(latestInstalled).value()
|
||||
if (value) return value.logs
|
||||
}
|
||||
return '< no logs found >'
|
||||
}
|
||||
|
||||
async installVersion(name, active, version) {
|
||||
if (fs.existsSync(this.util.getPathFromRoot(`./${name}/` + version.name))) {
|
||||
await this.util.runCommand('rmdir', ['/S', '/Q', `"${this.util.getPathFromRoot(`./${name}/` + version.name)}"`])
|
||||
}
|
||||
if (!fs.existsSync(this.util.getPathFromRoot(`./${name}/`))) {
|
||||
await fsp.mkdir(this.util.getPathFromRoot(`./${name}/`))
|
||||
}
|
||||
try {
|
||||
await fsp.mkdir(this.util.getPathFromRoot(`./${name}/` + version.name))
|
||||
} catch(err) {
|
||||
if (err.code !== 'EEXIST') {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
// await fsp.mkdir(this.util.getPathFromRoot(`./${name}/` + version.name + '/node_modules'))
|
||||
this.logActive(name, active, `Installer: Downloading ${version.name} (${version.url}) to ${version.name + '/' + version.name + '.zip'}\n`)
|
||||
let filePath = this.util.getPathFromRoot(`./${name}/` + version.name + '/' + version.name + '.zip')
|
||||
await request(this.config, version.url, filePath)
|
||||
this.logActive(name, active, `Installer: Downloading finished, starting extraction\n`)
|
||||
await this.util.runCommand(
|
||||
'"C:\\Program Files\\7-Zip\\7z.exe"',
|
||||
['x', `"${filePath}"`],
|
||||
this.util.getPathFromRoot(`./${name}/` + version.name + '/'),
|
||||
this.logActive.bind(this, name, active)
|
||||
)
|
||||
|
||||
if (!fs.existsSync(this.util.getPathFromRoot(`./${name}/` + version.name + '/index.mjs'))) {
|
||||
this.logActive(name, active, `\nInstaller: ERROR: Missing index.mjs in the folder, exiting\n`)
|
||||
throw new Error(`Missing index.mjs in ${this.util.getPathFromRoot(`./${name}/` + version.name + '/index.mjs')}`)
|
||||
}
|
||||
|
||||
this.logActive(name, active, `\nInstaller: Starting npm install\n`)
|
||||
|
||||
await this.util.runCommand(
|
||||
'npm.cmd',
|
||||
['install', '--production', '--no-optional', '--no-package-lock', '--no-audit'],
|
||||
this.util.getPathFromRoot(`./${name}/` + version.name + '/'),
|
||||
this.logActive.bind(this, name, active)
|
||||
)
|
||||
|
||||
await this.db.set(`core.${name}LatestInstalled`, version.name)
|
||||
.write()
|
||||
this.emit('dbupdated', {})
|
||||
|
||||
this.logActive(name, active, `\nInstaller: Successfully installed ${version.name}\n`)
|
||||
}
|
||||
|
||||
getActive(name) {
|
||||
if (name === 'app') {
|
||||
return this._appUpdating
|
||||
} else if (name === 'manage') {
|
||||
return this._manageUpdating
|
||||
} else {
|
||||
throw new Error('Invalid name: ' + name)
|
||||
}
|
||||
}
|
||||
|
||||
async startModule(module, port) {
|
||||
let out = await module.start(this.config, this.db, this.log, this, this.http, port)
|
||||
if (out && out.then) {
|
||||
await out
|
||||
}
|
||||
if (!this.http.getCurrentServer()) {
|
||||
this.log.warn('Module did not call http.createServer')
|
||||
}
|
||||
}
|
||||
|
||||
hasNewVersionAvailable(name) {
|
||||
let newestVersion = this.db.get(`core.${name}LatestInstalled`).value()
|
||||
if (!newestVersion) return false
|
||||
|
||||
let history = this.db.get(`core_${name}History`).getById(newestVersion).value()
|
||||
if (history.installed && history.stable === 0) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
async tryStartProgram(name) {
|
||||
let active = this.getActive(name)
|
||||
|
||||
if (this[name + 'Running'] && !this.hasNewVersionAvailable(name)) {
|
||||
this.log.event.warn('Attempting to start ' + name + ' which is already running')
|
||||
this.log.warn('Attempting to start ' + name + ' which is already running')
|
||||
this.logActive(name, active, `Runner: Attempting to start it but it is already running\n`, true)
|
||||
return
|
||||
}
|
||||
active.starting = true
|
||||
|
||||
if (this[name + 'Running']) {
|
||||
let success = await this.http.closeServer(name)
|
||||
if (!success) {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
this.logActive(name, active, `Runner: Found new version but server could not be shut down, restarting service core\n`)
|
||||
await new Promise(() => {
|
||||
this.log.event.warn('Found new version of ' + name + ' but server could not be shut down gracefully, restarting...', null, () => {
|
||||
process.exit(100)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.logActive(name, active, `Runner: Found new version but server could not be shut down\n`)
|
||||
return
|
||||
}
|
||||
}
|
||||
this[name + 'Running'] = false
|
||||
this.emit('statusupdated', {})
|
||||
}
|
||||
|
||||
let history = this.db.get(`core_${name}History`)
|
||||
.filter('installed')
|
||||
.orderBy('installed', 'desc')
|
||||
.value()
|
||||
this.logActive(name, active, `Runner: Finding available version\n`)
|
||||
|
||||
for (let i = 0; i < history.length; i++) {
|
||||
if ((history[i].stable === -1 && !active.fresh)
|
||||
|| (history[i].stable < -1)) {
|
||||
this.logActive(name, active, `Runner: Skipping version ${history[i].name} due to marked as unstable\n`)
|
||||
continue
|
||||
}
|
||||
|
||||
await this.db.set(`core.${name}Active`, history[i].name)
|
||||
.write()
|
||||
this.emit('dbupdated', {})
|
||||
|
||||
let running = await this.tryStartProgramVersion(name, active, history[i].name)
|
||||
if (running) {
|
||||
history[i].stable = 1
|
||||
} else {
|
||||
if (active.fresh || history[i].stable === -1) {
|
||||
history[i].stable = -2
|
||||
} else {
|
||||
history[i].stable = -1
|
||||
}
|
||||
await this.db.set(`core.${name}Active`, null)
|
||||
.write()
|
||||
this.emit('dbupdated', {})
|
||||
}
|
||||
active.fresh = false
|
||||
|
||||
await this.db.get(`core_${name}History`).updateById(history[i].id, history[i].stable).write()
|
||||
if (history[i].stable > 0) break
|
||||
}
|
||||
|
||||
if (!this.db.get(`core.${name}Active`).value()) {
|
||||
this.logActive(name, active, `Runner: Could not find any available stable version of ${name}\n`)
|
||||
this.log.error('Unable to start ' + name)
|
||||
this.log.event.error('Unable to start ' + name)
|
||||
}
|
||||
|
||||
active.starting = false
|
||||
}
|
||||
|
||||
programCrashed(name, version, active, oldStable) {
|
||||
let newStable = -2
|
||||
console.log('EXITING:', oldStable, active)
|
||||
if (oldStable === 0 && !active.fresh) {
|
||||
newStable = -1
|
||||
}
|
||||
let temp = this.db.get(`core_${name}History`).getById(version).set('stable', newStable )
|
||||
temp.value() // Trigger update on __wrapped__
|
||||
fs.writeFileSync(this.db.adapterFilePath, JSON.stringify(temp.__wrapped__, null, 2))
|
||||
}
|
||||
|
||||
async tryStartProgramVersion(name, active, version) {
|
||||
if (!version) return false
|
||||
this.logActive(name, active, `Runner: Attempting to start ${version}\n`)
|
||||
let indexPath = this.util.getUrlFromRoot(`./${name}/` + version + '/index.mjs')
|
||||
let module
|
||||
|
||||
try {
|
||||
this.logActive(name, active, `Runner: Loading ${indexPath}\n`)
|
||||
module = await import(indexPath)
|
||||
} catch (err) {
|
||||
this.logActive(name, active, `Runner: Error importing module\n`, true)
|
||||
this.logActive(name, active, `${err.stack}\n`, true)
|
||||
this.log.error(err, `Failed to load ${indexPath}`)
|
||||
return false
|
||||
}
|
||||
|
||||
let checkTimeout = null
|
||||
let oldStable = this.db.get(`core_${name}History`).getById(version).value().stable
|
||||
this._activeCrashHandler = this.programCrashed.bind(this, name, version, active, oldStable)
|
||||
process.once('exit', this._activeCrashHandler)
|
||||
try {
|
||||
let port = name === 'app' ? this.config.port : this.config.managePort
|
||||
await new Promise((res, rej) => {
|
||||
checkTimeout = setTimeout(function() {
|
||||
rej(new Error('Program took longer than 60 seconds to resolve promise'))
|
||||
}, 60 * 1000)
|
||||
|
||||
this.logActive(name, active, `Runner: Starting module\n`)
|
||||
|
||||
try {
|
||||
this.http.setContext(name)
|
||||
this.startModule(module, port)
|
||||
.then(res, rej)
|
||||
} catch (err) {
|
||||
rej(err)
|
||||
}
|
||||
})
|
||||
clearTimeout(checkTimeout)
|
||||
|
||||
await this.checkProgramRunning(name, active, port)
|
||||
process.off('exit', this._activeCrashHandler)
|
||||
} catch (err) {
|
||||
clearTimeout(checkTimeout)
|
||||
process.off('exit', this._activeCrashHandler)
|
||||
await this.http.closeServer(name)
|
||||
|
||||
this.logActive(name, active, `Runner: Error starting\n`, true)
|
||||
this.logActive(name, active, `${err.stack}\n`, true)
|
||||
this.log.error(err, `Failed to start ${name}`)
|
||||
return false
|
||||
}
|
||||
this._activeCrashHandler = null
|
||||
|
||||
this.logActive(name, active, `Runner: Successfully started version ${version}\n`)
|
||||
await this.db.set(`core.${name}Active`, version)
|
||||
.write()
|
||||
|
||||
if (name === 'app') {
|
||||
this.appRunning = true
|
||||
} else {
|
||||
this.manageRunning = true
|
||||
}
|
||||
this.emit('statusupdated', {})
|
||||
|
||||
this.logActive(name, active, `Runner: Module is running successfully\n`)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
async checkProgramRunning(name, active, port) {
|
||||
this.logActive(name, active, `Checker: Testing out module port ${port}\n`)
|
||||
let start = new Date()
|
||||
let error = null
|
||||
let success = false
|
||||
|
||||
while (new Date() - start < 10 * 1000) {
|
||||
try {
|
||||
let check = await request(this.config, `http://localhost:${port}`, null, 0, true)
|
||||
success = true
|
||||
break
|
||||
} catch(err) {
|
||||
this.logActive(name, active, `Checker: ${err.message}, retrying in 3 seconds\n`)
|
||||
error = err
|
||||
await new Promise(function(res) { setTimeout(res, 3000)})
|
||||
}
|
||||
}
|
||||
if (success) return true
|
||||
throw error || new Error('Checking server failed')
|
||||
}
|
||||
|
||||
async installLatestVersion(name) {
|
||||
if (!this.config[name + 'Repository']) {
|
||||
if (name === 'app') {
|
||||
this.log.error(name + ' Repository was missing from config')
|
||||
this.log.event.error(name + ' Repository was missing from config')
|
||||
} else {
|
||||
this.log.warn(name + ' Repository was missing from config')
|
||||
this.log.event.warn(name + ' Repository was missing from config')
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let active = this.getActive(name)
|
||||
let oldLogs = active.logs || ''
|
||||
if (oldLogs) {
|
||||
oldLogs += '\n'
|
||||
}
|
||||
active.logs = ''
|
||||
active.updating = true
|
||||
|
||||
this.emit('statusupdated', {})
|
||||
this.logActive(name, active, `Installer: Checking for updates at time: ${new Date().toISOString().replace('T', ' ').split('.')[0]}\n`)
|
||||
|
||||
let version = null
|
||||
let installed = false
|
||||
let found = false
|
||||
try {
|
||||
version = await this.getLatestVersion(active, name)
|
||||
if (version) {
|
||||
let core = this.db.get('core').value()
|
||||
let fromDb = this.db.get(`core_${name}History`).getById(version.name).value()
|
||||
if (!fromDb || !fromDb.installed) {
|
||||
let oldVersion = core[name + 'Current'] || '<none>'
|
||||
this.logActive(name, active, `Installer: Updating from ${oldVersion} to ${version.name}\n`)
|
||||
await this.installVersion(name, active, version)
|
||||
this.logActive(name, active, `Installer: Finished: ${new Date().toISOString().replace('T', ' ').split('.')[0]}\n`)
|
||||
installed = new Date()
|
||||
} else {
|
||||
found = true
|
||||
this.logActive(name, active, `Installer: Version ${version.name} already installed\n`)
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
this.logActive(name, active, '\n', true)
|
||||
this.logActive(name, active, `Installer: Exception occured while updating ${name}\n`, true)
|
||||
this.logActive(name, active, err.stack, true)
|
||||
this.log.error('Error while updating ' + name, err)
|
||||
}
|
||||
active.updating = false
|
||||
if (version && !found) {
|
||||
await this.db.get(`core_${name}History`).upsert({
|
||||
id: version.name,
|
||||
name: version.name,
|
||||
filename: version.filename,
|
||||
url: version.url,
|
||||
description: version.description,
|
||||
logs: active.logs,
|
||||
stable: 0,
|
||||
installed: installed && installed.toISOString(),
|
||||
}).write()
|
||||
}
|
||||
active.logs = oldLogs + active.logs
|
||||
this.emit(name + 'log', active)
|
||||
this.emit('statusupdated', {})
|
||||
}
|
||||
|
||||
async start(name) {
|
||||
var version = this.db.get('core.' + name + 'LatestInstalled').value()
|
||||
if (version) {
|
||||
await this.tryStartProgram(name)
|
||||
}
|
||||
|
||||
await this.installLatestVersion(name)
|
||||
|
||||
if (version !== this.db.get('core.' + name + 'LatestInstalled').value()) {
|
||||
if (!this[name + 'Running'] || this.hasNewVersionAvailable(name)) {
|
||||
await this.tryStartProgram(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
import { setTimeout } from 'timers/promises'
|
||||
import { Low, JSONFile, Memory } from 'lowdb'
|
||||
import { type } from 'os'
|
||||
import { defaults, isObject } from './defaults.mjs'
|
||||
|
||||
export default function GetDB(config, log, orgFilename = 'db.json') {
|
||||
|
@ -87,9 +86,9 @@ export default function GetDB(config, log, orgFilename = 'db.json') {
|
|||
db.addApplication = function(name) {
|
||||
db.data.core[name] ||= {}
|
||||
defaults(db.data.core[name], {
|
||||
active: null,
|
||||
latestInstalled: null,
|
||||
latestVersion: null,
|
||||
active: '',
|
||||
latestInstalled: '',
|
||||
latestVersion: '',
|
||||
updater: '',
|
||||
versions: [],
|
||||
})
|
||||
|
|
|
@ -24,6 +24,17 @@ export default class HttpServer {
|
|||
})
|
||||
})
|
||||
|
||||
server.listenAsync = (port, host) => {
|
||||
return new Promise((res, rej) => {
|
||||
server.once('error', rej)
|
||||
|
||||
server.listen(port, host || '0.0.0.0', () => {
|
||||
server.off('error', rej)
|
||||
res()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
this.active = server
|
||||
return server
|
||||
}
|
||||
|
@ -38,11 +49,13 @@ export default class HttpServer {
|
|||
this.sockets.clear()
|
||||
|
||||
this.active.close(err => {
|
||||
if (err) return rej(err)
|
||||
if (err) {
|
||||
if (err.code !== 'ERR_SERVER_NOT_RUNNING') return rej(err)
|
||||
}
|
||||
this.active = null
|
||||
|
||||
// Waiting 1 second for it to close down
|
||||
setTimeout(function() {res() }, 1000)
|
||||
setTimeout(function() {res() }, 100)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
77
core/log.mjs
77
core/log.mjs
|
@ -1,15 +1,24 @@
|
|||
import nodewindows from 'node-windows'
|
||||
// import nodewindows from 'node-windows'
|
||||
import bunyan from 'bunyan-lite'
|
||||
import { setTimeout } from 'timers/promises'
|
||||
|
||||
export default function getLog(name) {
|
||||
export default function getLog(name, streams = null, opts = {}) {
|
||||
let settings
|
||||
let ringbuffer = new bunyan.RingBuffer({ limit: 100 })
|
||||
let ringbufferwarn = new bunyan.RingBuffer({ limit: 100 })
|
||||
|
||||
if (streams) {
|
||||
streams.forEach(function(stream) {
|
||||
if (stream.stream === 'process.stdout') {
|
||||
stream.stream = process.stdout
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
settings = {
|
||||
"name": "service-core",
|
||||
"streams": [{
|
||||
"name": name,
|
||||
"streams": streams || [{
|
||||
path: 'log.log',
|
||||
level: 'info',
|
||||
}
|
||||
|
@ -17,8 +26,8 @@ export default function getLog(name) {
|
|||
}
|
||||
} else {
|
||||
settings = {
|
||||
"name": "service-core",
|
||||
"streams": [{
|
||||
"name": name,
|
||||
"streams": streams || [{
|
||||
"stream": process.stdout,
|
||||
"level": "debug"
|
||||
}
|
||||
|
@ -53,16 +62,64 @@ export default function getLog(name) {
|
|||
level: 'info',
|
||||
})
|
||||
|
||||
let eventManager = null
|
||||
let eventManagerLoading = false
|
||||
|
||||
async function safeLoadEvent(level, message, code) {
|
||||
if (eventManager === false) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
if (!eventManager) {
|
||||
if (eventManagerLoading) {
|
||||
for (let i = 0; i < 10 && eventManagerLoading; i++) {
|
||||
await setTimeout(50)
|
||||
}
|
||||
if (eventManagerLoading) {
|
||||
eventManager = false
|
||||
}
|
||||
return safeLoadEvent(level, message, code)
|
||||
}
|
||||
|
||||
eventManagerLoading = true
|
||||
|
||||
let prom
|
||||
if (opts.import) {
|
||||
prom = opts.import('node-windows')
|
||||
} else {
|
||||
prom = import('node-windows')
|
||||
}
|
||||
await prom.then(
|
||||
function(res) { eventManager = new res.default.EventLogger(name) },
|
||||
function() { eventManager = false },
|
||||
)
|
||||
eventManagerLoading = false
|
||||
return safeLoadEvent(level, message, code)
|
||||
}
|
||||
|
||||
return new Promise(function(res) {
|
||||
try {
|
||||
eventManager[level](message, code, function() { res() })
|
||||
} catch {
|
||||
res()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Create our logger.
|
||||
logger = bunyan.createLogger(settings)
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
logger.event = new nodewindows.EventLogger(name)
|
||||
logger.event = {
|
||||
info: safeLoadEvent.bind(this, 'info'),
|
||||
warn: safeLoadEvent.bind(this, 'warn'),
|
||||
error: safeLoadEvent.bind(this, 'error'),
|
||||
}
|
||||
} else {
|
||||
logger.event = {
|
||||
info: function() {},
|
||||
warn: function() {},
|
||||
error: function() {},
|
||||
info: function() { return Promise.resolve() },
|
||||
warn: function() { return Promise.resolve() },
|
||||
error: function() { return Promise.resolve() },
|
||||
}
|
||||
}
|
||||
logger.ringbuffer = ringbuffer
|
||||
|
|
|
@ -26,15 +26,58 @@ export default class Util {
|
|||
}
|
||||
|
||||
getAppNames(config) {
|
||||
const validLevels = [
|
||||
'fatal',
|
||||
'error',
|
||||
'warn',
|
||||
'info',
|
||||
'debug',
|
||||
'trace',
|
||||
]
|
||||
let out = []
|
||||
let keys = Object.keys(config)
|
||||
for (let key of keys) {
|
||||
if (typeof(config[key]) !== 'object' || config[key] == null) continue
|
||||
if (typeof(config[key].port) !== 'number' || !config[key].port) continue
|
||||
if (typeof(config[key].provider) !== 'string' || !config[key].provider) continue
|
||||
if (config[key].https != null && typeof(config[key].https) !== 'boolean') continue
|
||||
if (config[key].updateEvery != null && (typeof(config[key].updateEvery) !== 'number' || config[key].updateEvery < 1)) continue
|
||||
if (config[key].waitUntilFail != null && (typeof(config[key].waitUntilFail) !== 'number' || config[key].waitUntilFail < 10)) continue
|
||||
if (typeof(config[key]) !== 'object' || config[key] == null)
|
||||
continue
|
||||
if (typeof(config[key].port) !== 'number' || !config[key].port)
|
||||
continue
|
||||
if (typeof(config[key].provider) !== 'string' || !config[key].provider)
|
||||
continue
|
||||
if (config[key].https != null && typeof(config[key].https) !== 'boolean')
|
||||
continue
|
||||
if (config[key].updateEvery != null && (typeof(config[key].updateEvery) !== 'number' || config[key].updateEvery < 0))
|
||||
continue
|
||||
if (config[key].startWaitUntilFail != null && (typeof(config[key].startWaitUntilFail) !== 'number' || config[key].startWaitUntilFail < 10))
|
||||
continue
|
||||
if (config[key].heartbeatTimeout != null && (typeof(config[key].heartbeatTimeout) !== 'number' || config[key].heartbeatTimeout < 10))
|
||||
continue
|
||||
if (config[key].heartbeatAttempts != null && (typeof(config[key].heartbeatAttempts) !== 'number' || config[key].heartbeatAttempts < 1))
|
||||
continue
|
||||
if (config[key].heartbeatAttemptsWait != null && (typeof(config[key].heartbeatAttemptsWait) !== 'number' || config[key].heartbeatAttemptsWait < 10))
|
||||
continue
|
||||
if (config[key].heartbeatPath != null && (typeof(config[key].heartbeatPath) !== 'string' || config[key].heartbeatPath[0] !== '/'))
|
||||
continue
|
||||
if (config[key].log != null) {
|
||||
if (!Array.isArray(config[key].log))
|
||||
continue
|
||||
let valid = true
|
||||
for (let stream of config[key].log) {
|
||||
if (!stream || typeof(stream) !== 'object' || Array.isArray(stream)) {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
if (typeof(stream.level) !== 'string' || !stream.level || !validLevels.includes(stream.level)) {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
if ((typeof(stream.path) !== 'string' || !stream.path) && stream.stream !== 'process.stdout') {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!valid)
|
||||
continue
|
||||
}
|
||||
|
||||
out.push(key)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"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:spec": "eltro \"test/**/*.test.mjs\" -r list",
|
||||
"test:watch": "npm-watch test"
|
||||
},
|
||||
"watch": {
|
||||
|
@ -13,7 +14,10 @@
|
|||
"patterns": [
|
||||
"{core,test}/*"
|
||||
],
|
||||
"ignore": "test/testapp",
|
||||
"ignore": [
|
||||
"test/testapp",
|
||||
"test/testnoexisting"
|
||||
],
|
||||
"extensions": "js,mjs",
|
||||
"quiet": true,
|
||||
"inherit": true
|
||||
|
|
|
@ -1,38 +1,38 @@
|
|||
import { Eltro as t, assert, stub } from 'eltro'
|
||||
import fs from 'fs/promises'
|
||||
import lowdb from '../core/db.mjs'
|
||||
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)
|
||||
const logger = {
|
||||
info: stub(),
|
||||
warn: stub(),
|
||||
error: stub(),
|
||||
}
|
||||
|
||||
|
||||
t.skip().timeout(10000).describe('Application update integration test', function() {
|
||||
let db
|
||||
let ctx
|
||||
let app
|
||||
let provider
|
||||
|
||||
t.before(function() {
|
||||
return lowdb({ test: { } }, logger, util.getPathFromRoot('./db_test.json')).then(function(res) {
|
||||
db = res
|
||||
provider = new GitProvider({ url: 'https://git.nfp.is/api/v1/repos/thething/sc-helloworld/releases' })
|
||||
app = new Application(util, db, provider, 'testapp')
|
||||
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 })
|
||||
})
|
||||
return provider.getLatestVersion()
|
||||
}).then(function(version) {
|
||||
return fs.rm(`./test/testapp/${version.version}`, { force: true, recursive: true })
|
||||
})
|
||||
})
|
||||
|
||||
t.after(function() {
|
||||
if (db.data.core.testapp.versions.length) {
|
||||
return fs.rm(`./test/testapp/${db.data.core.testapp.versions[0].id}`, { force: true, recursive: true })
|
||||
}
|
||||
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(){
|
||||
|
@ -40,15 +40,15 @@ t.skip().timeout(10000).describe('Application update integration test', function
|
|||
await app.update()
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
if (db.data.core.testapp.versions.length) {
|
||||
console.log(db.data.core.testapp.versions[0].log)
|
||||
if (ctx.db.data.core.testapp.versions.length) {
|
||||
console.log(ctx.db.data.core.testapp.versions[0].log)
|
||||
}
|
||||
throw err
|
||||
}
|
||||
|
||||
assert.ok(db.data.core.testapp.latestInstalled)
|
||||
await fs.stat(util.getPathFromRoot(`./testapp/${db.data.core.testapp.latestInstalled}/index.mjs`))
|
||||
await fs.stat(util.getPathFromRoot(`./testapp/${db.data.core.testapp.latestInstalled}/package.json`))
|
||||
await fs.stat(util.getPathFromRoot(`./testapp/${db.data.core.testapp.latestInstalled}/node_modules`))
|
||||
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`))
|
||||
})
|
||||
})
|
|
@ -1,58 +1,59 @@
|
|||
import { Eltro as t, assert, stub } from 'eltro'
|
||||
import fs from 'fs/promises'
|
||||
import lowdb from '../core/db.mjs'
|
||||
import Application from '../core/application.mjs'
|
||||
import Util from '../core/util.mjs'
|
||||
import lowdb from '../core/db.mjs'
|
||||
import StaticProvider from '../core/providers/static.mjs'
|
||||
import { createFakeContext } from './helpers.mjs'
|
||||
|
||||
const util = new Util(import.meta.url)
|
||||
|
||||
const logger = {
|
||||
info: stub(),
|
||||
warn: stub(),
|
||||
error: stub(),
|
||||
}
|
||||
function createProvider() {
|
||||
return {
|
||||
getLatestVersion: stub(),
|
||||
downloadVersion: stub(),
|
||||
}
|
||||
}
|
||||
|
||||
t.timeout(250).describe('#runVersion()', function() {
|
||||
t.describe('#runVersion("static")', function() {
|
||||
const assertPort = 22345
|
||||
let db
|
||||
let ctx
|
||||
let app
|
||||
|
||||
const defaultHandler = function(db, log, http, port) {
|
||||
const server = http.createServer(function (req, res) {
|
||||
const defaultHandler = function(orgHandler) {
|
||||
let handler = orgHandler || function (req, res) {
|
||||
res.writeHead(204); res.end(JSON.stringify({ a: 1 }))
|
||||
})
|
||||
}
|
||||
return function(http, port, ctx) {
|
||||
const server = http.createServer(handler)
|
||||
|
||||
return new Promise(function(res, rej) {
|
||||
server.listen(port, '0.0.0.0', function(err) {
|
||||
if (err) return rej(err)
|
||||
res()
|
||||
})
|
||||
})
|
||||
return server.listenAsync(port)
|
||||
}
|
||||
}
|
||||
|
||||
t.before(function() {
|
||||
return fs.mkdir(util.getPathFromRoot('./testnoexisting'), { recursive: true })
|
||||
})
|
||||
|
||||
t.after(function() {
|
||||
return fs.rm(util.getPathFromRoot('./testnoexisting'), { force: true, recursive: true })
|
||||
})
|
||||
|
||||
t.beforeEach(function() {
|
||||
return lowdb({ test: { } }, logger, null).then(function(res) {
|
||||
db = res
|
||||
let provider = new StaticProvider()
|
||||
app = new Application(util, db, provider, 'testapp')
|
||||
app.config.port = assertPort
|
||||
app.registerModule(defaultHandler)
|
||||
})
|
||||
return createFakeContext()
|
||||
.then(function(res) {
|
||||
ctx = res
|
||||
let provider = new StaticProvider()
|
||||
app = new Application(ctx, provider, 'testapp')
|
||||
app.config.port = assertPort
|
||||
app.registerModule(defaultHandler())
|
||||
})
|
||||
})
|
||||
|
||||
t.afterEach(function() {
|
||||
return app.http.closeServer()
|
||||
})
|
||||
|
||||
t.test('should throw if http is not called', async function() {
|
||||
app.registerModule(function(checkDb, checkLog, checkHttp, checkPort) {
|
||||
assert.strictEqual(checkDb, db)
|
||||
assert.strictEqual(checkLog, db.log)
|
||||
app.registerModule(function(checkHttp, checkPort, checkCtx) {
|
||||
assert.strictEqual(checkHttp, app.http)
|
||||
assert.strictEqual(checkPort, assertPort)
|
||||
assert.strictEqual(checkCtx.db, ctx.db)
|
||||
assert.strictEqual(checkCtx.log, ctx.log)
|
||||
assert.strictEqual(checkCtx.app, app)
|
||||
})
|
||||
|
||||
let err = await assert.isRejected(app.runVersion('static'))
|
||||
|
@ -62,10 +63,11 @@ t.timeout(250).describe('#runVersion()', function() {
|
|||
assert.match(err.message, /static/)
|
||||
assert.match(err.message, new RegExp(app.name))
|
||||
assert.match(err.message, /call/i)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.active, 'static')
|
||||
})
|
||||
|
||||
t.test('should throw if it timeouts waiting for promise to succeed', async function() {
|
||||
app.config.waitUntilFail = 50
|
||||
app.config.startWaitUntilFail = 50
|
||||
app.registerModule(function() { return new Promise(function() {}) })
|
||||
|
||||
let err = await assert.isRejected(app.runVersion('static'))
|
||||
|
@ -75,18 +77,203 @@ t.timeout(250).describe('#runVersion()', function() {
|
|||
assert.match(err.message, /static/)
|
||||
assert.match(err.message, /50ms/)
|
||||
assert.match(err.message, new RegExp(app.name))
|
||||
assert.strictEqual(ctx.db.data.core.testapp.active, 'static')
|
||||
})
|
||||
|
||||
t.test('should otherwise succeed if it finished within the time limit', async function() {
|
||||
app.config.waitUntilFail = 250
|
||||
app.registerModule(function(db, log, http, port) {
|
||||
const handler = defaultHandler()
|
||||
app.config.startWaitUntilFail = 250
|
||||
app.registerModule(function(http, port, ctx) {
|
||||
return new Promise(function(res) {
|
||||
setTimeout(res, 25)
|
||||
}).then(function() {
|
||||
return defaultHandler(db, log, http, port)
|
||||
return handler(http, port, ctx)
|
||||
})
|
||||
})
|
||||
|
||||
await app.runVersion('static')
|
||||
|
||||
assert.strictEqual(ctx.db.data.core.testapp.active, 'static')
|
||||
})
|
||||
|
||||
t.test('should fail if run succeeds but heartbeat errors', async function() {
|
||||
let called = 0
|
||||
const handler = function(req, res) {
|
||||
called++
|
||||
res.statusCode = 400
|
||||
res.end(JSON.stringify({ a: 1 }))
|
||||
}
|
||||
app.config.heartbeatAttempts = 3
|
||||
app.registerModule(defaultHandler(handler))
|
||||
|
||||
let err = await assert.isRejected(app.runVersion('static'))
|
||||
assert.match(err.message, /app/i)
|
||||
assert.match(err.message, /failed/i)
|
||||
assert.match(err.message, /static/i)
|
||||
assert.match(err.message, /testapp/i)
|
||||
assert.match(err.message, /400/i)
|
||||
assert.strictEqual(called, 3)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.active, 'static')
|
||||
})
|
||||
|
||||
t.test('should fail if run succeeds but heartbeat times out', async function() {
|
||||
let called = 0
|
||||
const handler = function(req, res) {
|
||||
called++
|
||||
}
|
||||
app.config.heartbeatAttempts = 2
|
||||
app.config.heartbeatAttemptsWait = 30
|
||||
app.registerModule(defaultHandler(handler))
|
||||
|
||||
let start = performance.now()
|
||||
let err = await assert.isRejected(app.runVersion('static'))
|
||||
let end = performance.now()
|
||||
assert.match(err.message, /app/i)
|
||||
assert.match(err.message, /failed/i)
|
||||
assert.match(err.message, /static/i)
|
||||
assert.match(err.message, /testapp/i)
|
||||
assert.match(err.message, /time/i)
|
||||
assert.match(err.message, /out/i)
|
||||
assert.match(err.message, /30ms/i)
|
||||
assert.ok(end - start > app.config.heartbeatAttempts * app.config.heartbeatAttemptsWait)
|
||||
assert.strictEqual(called, 2)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.active, 'static')
|
||||
})
|
||||
|
||||
t.test('should call with correct path', async function() {
|
||||
const assertPath = '/test/something'
|
||||
const handler = function(req, res) {
|
||||
if (req.url === assertPath) {
|
||||
res.writeHead(204); res.end(JSON.stringify({ a: 1 }))
|
||||
} else {
|
||||
res.statusCode = 400
|
||||
res.end(JSON.stringify({ a: 1 }))
|
||||
}
|
||||
}
|
||||
app.config.heartbeatAttempts = 3
|
||||
app.registerModule(defaultHandler(handler))
|
||||
|
||||
let err = await assert.isRejected(app.runVersion('static'))
|
||||
assert.match(err.message, /app/i)
|
||||
assert.match(err.message, /failed/i)
|
||||
assert.match(err.message, /static/i)
|
||||
assert.match(err.message, /testapp/i)
|
||||
assert.match(err.message, /400/i)
|
||||
|
||||
await app.http.closeServer()
|
||||
app.registerModule(defaultHandler(handler))
|
||||
|
||||
app.config.heartbeatPath = assertPath
|
||||
await app.runVersion('static')
|
||||
|
||||
assert.strictEqual(ctx.db.data.core.testapp.active, 'static')
|
||||
})
|
||||
})
|
||||
|
||||
t.skip().describe('#runVersion("version")', function() {
|
||||
const assertConfig = util.getPathFromRoot('./db_test_applicationrun.json')
|
||||
const assertPort = 22345
|
||||
let ctx
|
||||
let app
|
||||
|
||||
t.before(function() {
|
||||
return fs.rm(util.getPathFromRoot('./testnoexisting'), { force: true, recursive: true })
|
||||
.then(function() {
|
||||
return fs.mkdir(util.getPathFromRoot('./testnoexisting'), { recursive: true })
|
||||
})
|
||||
})
|
||||
|
||||
t.after(function() {
|
||||
return fs.rm(util.getPathFromRoot('./testnoexisting'), { force: true, recursive: true })
|
||||
})
|
||||
|
||||
t.beforeEach(function() {
|
||||
return createFakeContext({ }, util, assertConfig)
|
||||
.then(function(res) {
|
||||
ctx = res
|
||||
let provider = new StaticProvider()
|
||||
app = new Application(ctx, provider, 'testnoexisting')
|
||||
app.config.port = assertPort
|
||||
return app.ctx.db.write()
|
||||
})
|
||||
})
|
||||
|
||||
t.afterEach(function() {
|
||||
return Promise.all([
|
||||
fs.rm(assertConfig),
|
||||
app.http.closeServer(),
|
||||
])
|
||||
})
|
||||
|
||||
t.test('when version is specified, should check if index.mjs exists', async function() {
|
||||
const assertNotError = new Error('AI DO')
|
||||
const assertTarget = util.getPathFromRoot('./testnoexisting/v100/index.mjs')
|
||||
let stubFsStat = stub()
|
||||
let provider = new StaticProvider()
|
||||
app = new Application(ctx, provider, 'testnoexisting', {
|
||||
fs: { stat: stubFsStat }
|
||||
})
|
||||
app.config.port = assertPort
|
||||
stubFsStat.rejects(assertNotError)
|
||||
|
||||
let err = await assert.isRejected(app.runVersion('v100'))
|
||||
assert.notStrictEqual(err, assertNotError)
|
||||
assert.match(err.message, new RegExp(assertNotError.message))
|
||||
assert.match(err.message, /index\.mjs/i)
|
||||
assert.match(err.message, /testnoexisting/i)
|
||||
assert.match(err.message, /v100/i)
|
||||
assert.match(err.message, /missing/i)
|
||||
assert.strictEqual(stubFsStat.firstCall[0], assertTarget)
|
||||
|
||||
assert.strictEqual(app.ctx.db.data.core.testnoexisting.active, 'v100')
|
||||
let checkDb = await lowdb({}, ctx.log, assertConfig)
|
||||
assert.strictEqual(checkDb.data.core.testnoexisting.active, 'v100')
|
||||
})
|
||||
|
||||
t.test('when version is specified and file exists, should attempt to load module', async function() {
|
||||
const assertError = new Error('Parallel Days')
|
||||
await fs.mkdir(util.getPathFromRoot('./testnoexisting/v99'), { recursive: true })
|
||||
await fs.writeFile(util.getPathFromRoot('./testnoexisting/v99/index.mjs'), `throw new Error('${assertError.message}')`)
|
||||
|
||||
let err = await assert.isRejected(app.runVersion('v99'))
|
||||
assert.notStrictEqual(err, assertError)
|
||||
assert.match(err.message, new RegExp(assertError.message))
|
||||
assert.match(err.message, /testnoexisting/i)
|
||||
assert.match(err.message, /v99/i)
|
||||
assert.match(err.message, /index\.mjs/i)
|
||||
|
||||
assert.strictEqual(app.ctx.db.data.core.testnoexisting.active, 'v99')
|
||||
let checkDb = await lowdb({}, ctx.log, assertConfig)
|
||||
assert.strictEqual(checkDb.data.core.testnoexisting.active, 'v99')
|
||||
})
|
||||
|
||||
t.test('when version is specified and file exists, should check if it has start', async function() {
|
||||
await fs.mkdir(util.getPathFromRoot('./testnoexisting/v98'), { recursive: true })
|
||||
await fs.writeFile(util.getPathFromRoot('./testnoexisting/v98/index.mjs'), ``)
|
||||
|
||||
let err = await assert.isRejected(app.runVersion('v98'))
|
||||
assert.match(err.message, /testnoexisting/i)
|
||||
assert.match(err.message, /v98/i)
|
||||
assert.match(err.message, /start/i)
|
||||
|
||||
assert.strictEqual(app.ctx.db.data.core.testnoexisting.active, 'v98')
|
||||
let checkDb = await lowdb({}, ctx.log, assertConfig)
|
||||
assert.strictEqual(checkDb.data.core.testnoexisting.active, 'v98')
|
||||
})
|
||||
|
||||
t.test('when version is specified and file exists and everything is okay, should work normally', async function() {
|
||||
await fs.mkdir(util.getPathFromRoot('./testnoexisting/v97'), { recursive: true })
|
||||
await fs.copyFile(
|
||||
util.getPathFromRoot('./exampleindex.mjs'),
|
||||
util.getPathFromRoot('./testnoexisting/v97/index.mjs')
|
||||
)
|
||||
|
||||
app.ctx.log.info.reset()
|
||||
app.ctx.log.event.info.reset()
|
||||
|
||||
await app.runVersion('v97')
|
||||
|
||||
assert.ok(app.ctx.log.info.called)
|
||||
assert.ok(app.ctx.log.event.info.called)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { setTimeout, setImmediate } from 'timers/promises'
|
||||
import { Eltro as t, assert, stub } from 'eltro'
|
||||
import fs from 'fs/promises'
|
||||
import lowdb from '../core/db.mjs'
|
||||
import Application from '../core/application.mjs'
|
||||
import Util from '../core/util.mjs'
|
||||
import StaticProvider from '../core/providers/static.mjs'
|
||||
import { createFakeContext } from './helpers.mjs'
|
||||
|
||||
const util = new Util(import.meta.url)
|
||||
|
||||
|
@ -21,41 +21,46 @@ function createProvider() {
|
|||
}
|
||||
|
||||
t.describe('constructor()', function() {
|
||||
let db
|
||||
let ctx
|
||||
|
||||
t.beforeEach(function() {
|
||||
return lowdb({ test: { } }, logger, null).then(function(res) {
|
||||
db = res
|
||||
})
|
||||
return createFakeContext()
|
||||
.then(function(res) { ctx = res })
|
||||
})
|
||||
|
||||
t.test('should auto-create application', function() {
|
||||
assert.notOk(db.data.core.test)
|
||||
assert.notOk(ctx.db.data.core.test)
|
||||
|
||||
new Application(util, db, {}, 'test')
|
||||
new Application(ctx, {}, 'test')
|
||||
|
||||
assert.ok(db.data.core.test)
|
||||
assert.ok(db.data.core.test.versions)
|
||||
assert.strictEqual(db.data.core.test.active, null)
|
||||
assert.strictEqual(db.data.core.test.latestInstalled, null)
|
||||
assert.strictEqual(db.data.core.test.latestVersion, null)
|
||||
assert.ok(ctx.db.data.core.test)
|
||||
assert.ok(ctx.db.data.core.test.versions)
|
||||
assert.strictEqual(ctx.db.data.core.test.active, '')
|
||||
assert.strictEqual(ctx.db.data.core.test.latestInstalled, '')
|
||||
assert.strictEqual(ctx.db.data.core.test.latestVersion, '')
|
||||
})
|
||||
|
||||
t.test('should keep config and other of itself', function() {
|
||||
const assertTest = { a: 1 }
|
||||
const assertName = 'test'
|
||||
db.config = {
|
||||
ctx.db.config = {
|
||||
test: assertTest,
|
||||
app: { b: 2},
|
||||
manage: { c: 3 },
|
||||
}
|
||||
|
||||
let app = new Application(util, db, {}, assertName)
|
||||
assert.strictEqual(app.config, assertTest)
|
||||
let app = new Application(ctx, {}, assertName)
|
||||
assert.notStrictEqual(app.config, assertTest)
|
||||
assert.strictEqual(app.config.a, assertTest.a)
|
||||
assert.strictEqual(app.config.updateEvery, 180)
|
||||
assert.strictEqual(app.config.waitUntilFail, 60 * 1000)
|
||||
assert.strictEqual(app.db, db)
|
||||
assert.strictEqual(app.util, util)
|
||||
assert.strictEqual(app.config.startWaitUntilFail, 60 * 1000)
|
||||
assert.strictEqual(app.config.heartbeatTimeout, 3 * 1000)
|
||||
assert.strictEqual(app.config.heartbeatAttempts, 5)
|
||||
assert.strictEqual(app.config.heartbeatAttemptsWait, 2 * 1000)
|
||||
assert.strictEqual(app.config.heartbeatPath, '/')
|
||||
assert.strictEqual(app.ctx.db, ctx.db)
|
||||
assert.strictEqual(app.ctx.app, app)
|
||||
assert.strictEqual(app.ctx.util, ctx.util)
|
||||
assert.strictEqual(app.name, assertName)
|
||||
assert.strictEqual(app.fresh, true)
|
||||
assert.strictEqual(app.running, false)
|
||||
|
@ -66,38 +71,37 @@ t.describe('constructor()', function() {
|
|||
})
|
||||
|
||||
t.test('should create http instance correctly', function() {
|
||||
db.config = {
|
||||
ctx.db.config = {
|
||||
testapp: { a: 1, https: true },
|
||||
app: { b: 2},
|
||||
manage: { c: 3 },
|
||||
}
|
||||
|
||||
let app = new Application(util, db, {}, 'testapp')
|
||||
let app = new Application(ctx, {}, 'testapp')
|
||||
assert.ok(app.http)
|
||||
assert.ok(app.http.ishttps)
|
||||
})
|
||||
|
||||
t.test('should keep provider', function() {
|
||||
const assertProvider = { a: 1 }
|
||||
let app = new Application(util, db, assertProvider, 'test')
|
||||
let app = new Application(ctx, assertProvider, 'test')
|
||||
assert.strictEqual(app.provider, assertProvider)
|
||||
})
|
||||
})
|
||||
|
||||
t.timeout(250).describe('#startAutoupdater()', function() {
|
||||
let db
|
||||
let ctx
|
||||
|
||||
t.beforeEach(function() {
|
||||
return lowdb({ test: { }, testapp: { } }, logger, null).then(function(res) {
|
||||
db = res
|
||||
})
|
||||
return createFakeContext()
|
||||
.then(function(res) { ctx = res })
|
||||
})
|
||||
|
||||
t.test('should do nothing if provider is static', async function() {
|
||||
const stubInterval = stub()
|
||||
stubInterval.throws(new Error('should not be seen'))
|
||||
let provider = new StaticProvider()
|
||||
let app = new Application(util, db, provider, 'teststatic', { setInterval: stubInterval })
|
||||
let app = new Application(ctx, provider, 'teststatic', { setInterval: stubInterval })
|
||||
|
||||
app.startAutoupdater()
|
||||
})
|
||||
|
@ -108,9 +112,11 @@ t.timeout(250).describe('#startAutoupdater()', function() {
|
|||
const stubUnref = stub()
|
||||
stubInterval.returns({ unref: stubUnref })
|
||||
|
||||
db.config.test.updateEvery = assertTimeMinutes
|
||||
ctx.db.config.test = {
|
||||
updateEvery: assertTimeMinutes,
|
||||
}
|
||||
|
||||
let app = new Application(util, db, {}, 'test', { setInterval: stubInterval })
|
||||
let app = new Application(ctx, {}, 'test', { setInterval: stubInterval })
|
||||
|
||||
assert.strictEqual(stubInterval.called, false)
|
||||
assert.strictEqual(stubUnref.called, false)
|
||||
|
@ -127,7 +133,7 @@ t.timeout(250).describe('#startAutoupdater()', function() {
|
|||
const stubInterval = stub()
|
||||
stubInterval.returns({ unref: function() {} })
|
||||
|
||||
let app = new Application(util, db, {}, 'test', { setInterval: stubInterval })
|
||||
let app = new Application(ctx, {}, 'test', { setInterval: stubInterval })
|
||||
|
||||
assert.strictEqual(stubInterval.called, false)
|
||||
|
||||
|
@ -147,7 +153,7 @@ t.timeout(250).describe('#startAutoupdater()', function() {
|
|||
return Promise.resolve()
|
||||
})
|
||||
|
||||
let app = new Application(util, db, {}, 'test', { setInterval: stubInterval })
|
||||
let app = new Application(ctx, {}, 'test', { setInterval: stubInterval })
|
||||
app.update = stubUpdate
|
||||
app.startAutoupdater()
|
||||
|
||||
|
@ -156,12 +162,12 @@ t.timeout(250).describe('#startAutoupdater()', function() {
|
|||
|
||||
stubInterval.firstCall[0]()
|
||||
|
||||
while (db.data.core.test.updater === '') {
|
||||
while (ctx.db.data.core.test.updater === '') {
|
||||
await setTimeout(10)
|
||||
}
|
||||
|
||||
assert.match(db.data.core.test.updater, /auto/i)
|
||||
assert.match(db.data.core.test.updater, /update/i)
|
||||
assert.match(ctx.db.data.core.test.updater, /auto/i)
|
||||
assert.match(ctx.db.data.core.test.updater, /update/i)
|
||||
})
|
||||
|
||||
t.test('should add any errors to last in db update check on errors when updating', async function() {
|
||||
|
@ -169,28 +175,55 @@ t.timeout(250).describe('#startAutoupdater()', function() {
|
|||
const assertErrorMessage = 'Ai Do'
|
||||
stubInterval.returns({ unref: function() {} })
|
||||
|
||||
let app = new Application(util, db, {}, 'test', { setInterval: stubInterval })
|
||||
let app = new Application(ctx, {}, 'test', { setInterval: stubInterval })
|
||||
app.update = function() {
|
||||
return Promise.reject(new Error(assertErrorMessage))
|
||||
}
|
||||
app.startAutoupdater()
|
||||
|
||||
assert.strictEqual(db.data.core.test.updater, '')
|
||||
assert.strictEqual(ctx.db.data.core.test.updater, '')
|
||||
|
||||
stubInterval.firstCall[0]()
|
||||
|
||||
while (db.data.core.test.updater === '') {
|
||||
while (ctx.db.data.core.test.updater === '') {
|
||||
await setTimeout(10)
|
||||
}
|
||||
|
||||
assert.match(db.data.core.test.updater, /auto/i)
|
||||
assert.match(db.data.core.test.updater, /update/i)
|
||||
assert.match(db.data.core.test.updater, new RegExp(assertErrorMessage))
|
||||
assert.match(ctx.db.data.core.test.updater, /auto/i)
|
||||
assert.match(ctx.db.data.core.test.updater, /update/i)
|
||||
assert.match(ctx.db.data.core.test.updater, new RegExp(assertErrorMessage))
|
||||
})
|
||||
})
|
||||
|
||||
t.timeout(250).describe('#closeServer()', function() {
|
||||
let app
|
||||
let stubCloseServer
|
||||
|
||||
t.beforeEach(function() {
|
||||
return createFakeContext()
|
||||
.then(function(res) {
|
||||
let provider = createProvider()
|
||||
app = new Application(res, provider, 'testapp')
|
||||
app.http.closeServer = stubCloseServer = stub().resolves()
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should call closeServer correctly', async function() {
|
||||
const assertError = new Error('Moonlight Fiesta')
|
||||
stubCloseServer.rejects(assertError)
|
||||
|
||||
let err = await assert.isRejected(app.closeServer())
|
||||
|
||||
assert.strictEqual(err, assertError)
|
||||
})
|
||||
|
||||
t.test('otherwise should work fine', async function() {
|
||||
await app.closeServer()
|
||||
})
|
||||
})
|
||||
|
||||
t.timeout(250).describe('#update()', function() {
|
||||
let db
|
||||
let ctx
|
||||
let app
|
||||
let provider
|
||||
let stubExtract
|
||||
|
@ -201,30 +234,27 @@ t.timeout(250).describe('#update()', function() {
|
|||
let stubFsStat
|
||||
|
||||
t.beforeEach(function() {
|
||||
util.extractFile = stubExtract = stub()
|
||||
util.runCommand = stubRunCommand = stub()
|
||||
return createFakeContext()
|
||||
.then(function(res) {
|
||||
ctx = res
|
||||
ctx.util.extractFile = stubExtract = stub().resolves()
|
||||
ctx.util.runCommand = stubRunCommand = stub().resolves()
|
||||
ctx.db.write = stubWrite = stub().resolves()
|
||||
|
||||
stubExtract.resolves()
|
||||
stubRunCommand.resolves()
|
||||
|
||||
return lowdb({ test: { } }, logger, null).then(function(res) {
|
||||
db = res
|
||||
db.write = stubWrite = stub()
|
||||
stubWrite.resolves()
|
||||
provider = createProvider()
|
||||
app = new Application(util, db, provider, 'testapp', {
|
||||
fs: {
|
||||
mkdir: stubFsMkdir = stub(),
|
||||
rm: stubFsRm = stub(),
|
||||
stat: stubFsStat = stub(),
|
||||
},
|
||||
provider = createProvider()
|
||||
app = new Application(ctx, provider, 'testapp', {
|
||||
fs: {
|
||||
mkdir: stubFsMkdir = stub(),
|
||||
rm: stubFsRm = stub(),
|
||||
stat: stubFsStat = stub(),
|
||||
},
|
||||
})
|
||||
stubFsMkdir.resolves()
|
||||
stubFsRm.resolves()
|
||||
stubFsStat.resolves({})
|
||||
provider.downloadVersion.resolves()
|
||||
provider.getLatestVersion.resolves({ version: '123456789', link: 'httplinkhere', filename: 'test.7z' })
|
||||
})
|
||||
stubFsMkdir.resolves()
|
||||
stubFsRm.resolves()
|
||||
stubFsStat.resolves({})
|
||||
provider.downloadVersion.resolves()
|
||||
provider.getLatestVersion.resolves({ version: '123456789', link: 'httplinkhere', filename: 'test.7z' })
|
||||
})
|
||||
})
|
||||
|
||||
t.afterEach(function() {
|
||||
|
@ -233,33 +263,35 @@ t.timeout(250).describe('#update()', function() {
|
|||
|
||||
t.test('should do nothing if provider is static', async function() {
|
||||
provider = new StaticProvider()
|
||||
app = new Application(util, db, provider, 'teststatic')
|
||||
app = new Application(ctx, provider, 'teststatic')
|
||||
|
||||
stubWrite.reset()
|
||||
|
||||
await app.update()
|
||||
|
||||
assert.match(db.data.core.teststatic.updater, /static/i)
|
||||
assert.match(db.data.core.teststatic.updater, /nothing/i)
|
||||
let old = db.data.core.teststatic.updater
|
||||
assert.match(ctx.db.data.core.teststatic.updater, /static/i)
|
||||
assert.match(ctx.db.data.core.teststatic.updater, /nothing/i)
|
||||
let old = ctx.db.data.core.teststatic.updater
|
||||
assert.ok(stubWrite.called)
|
||||
assert.strictEqual(stubWrite.callCount, 1)
|
||||
|
||||
await app.update()
|
||||
assert.strictEqual(db.data.core.teststatic.updater, old)
|
||||
assert.strictEqual(ctx.db.data.core.teststatic.updater, old)
|
||||
assert.strictEqual(stubWrite.callCount, 1)
|
||||
|
||||
db.data.core.teststatic.updater = 'asdf'
|
||||
ctx.db.data.core.teststatic.updater = 'asdf'
|
||||
|
||||
await app.update()
|
||||
assert.strictEqual(db.data.core.teststatic.updater, old)
|
||||
assert.strictEqual(ctx.db.data.core.teststatic.updater, old)
|
||||
assert.strictEqual(stubWrite.callCount, 2)
|
||||
|
||||
await app.update()
|
||||
assert.strictEqual(db.data.core.teststatic.updater, old)
|
||||
assert.strictEqual(ctx.db.data.core.teststatic.updater, old)
|
||||
assert.strictEqual(stubWrite.callCount, 2)
|
||||
})
|
||||
|
||||
t.test('multiple calls should be safe', async function() {
|
||||
db.data.core.testapp.updater = ''
|
||||
ctx.db.data.core.testapp.updater = ''
|
||||
|
||||
provider.getLatestVersion.returnWith(function() {
|
||||
return new Promise(function() {})
|
||||
|
@ -282,7 +314,7 @@ t.timeout(250).describe('#update()', function() {
|
|||
t.test('should check for latest version', async function() {
|
||||
const assertError = new Error('Ore wa Subete wo Shihaisuru')
|
||||
provider.getLatestVersion.rejects(assertError)
|
||||
db.data.core.testapp.updater = ''
|
||||
ctx.db.data.core.testapp.updater = ''
|
||||
|
||||
let err = await assert.isRejected(app.update())
|
||||
|
||||
|
@ -291,10 +323,10 @@ t.timeout(250).describe('#update()', function() {
|
|||
|
||||
assert.ok(stubWrite.called)
|
||||
assert.ok(stubWrite.callCount >= 1)
|
||||
assert.match(db.data.core.testapp.updater, /check/i)
|
||||
assert.match(db.data.core.testapp.updater, /version/i)
|
||||
assert.match(db.data.core.testapp.updater, new RegExp(new Date().toISOString().split('T')[0]))
|
||||
assert.match(db.data.core.testapp.updater, new RegExp(assertError.message))
|
||||
assert.match(ctx.db.data.core.testapp.updater, /check/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /version/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, new RegExp(new Date().toISOString().split('T')[0]))
|
||||
assert.match(ctx.db.data.core.testapp.updater, new RegExp(assertError.message))
|
||||
})
|
||||
|
||||
t.test('should call provider download latest correctly if new 7zip version', async function() {
|
||||
|
@ -302,27 +334,27 @@ t.timeout(250).describe('#update()', function() {
|
|||
const assertLink = 'All of you'
|
||||
const assertVersion = { version: '123456789', link: assertLink, filename: 'test.7z' }
|
||||
const assertTarget = util.getPathFromRoot('./testapp/123456789/file.7z')
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 0)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 0)
|
||||
|
||||
provider.getLatestVersion.resolves(assertVersion)
|
||||
provider.downloadVersion.rejects(assertError)
|
||||
db.data.core.testapp.updater = ''
|
||||
ctx.db.data.core.testapp.updater = ''
|
||||
|
||||
let err = await assert.isRejected(app.update())
|
||||
|
||||
assert.strictEqual(app.updating, false)
|
||||
assert.strictEqual(err, assertError)
|
||||
assert.match(db.data.core.testapp.updater, /found/i)
|
||||
assert.match(db.data.core.testapp.updater, new RegExp(assertVersion.version))
|
||||
assert.match(db.data.core.testapp.updater, /downloading/i)
|
||||
assert.match(db.data.core.testapp.updater, new RegExp(assertLink))
|
||||
assert.match(db.data.core.testapp.updater, new RegExp(assertTarget.replace(/\\/g, '\\\\')))
|
||||
assert.match(ctx.db.data.core.testapp.updater, /found/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, new RegExp(assertVersion.version))
|
||||
assert.match(ctx.db.data.core.testapp.updater, /downloading/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, new RegExp(assertLink))
|
||||
assert.match(ctx.db.data.core.testapp.updater, new RegExp(assertTarget.replace(/\\/g, '\\\\')))
|
||||
assert.strictEqual(provider.downloadVersion.firstCall[0], assertVersion)
|
||||
assert.strictEqual(provider.downloadVersion.firstCall[1], assertTarget)
|
||||
assert.match(db.data.core.testapp.updater, new RegExp(assertError.message))
|
||||
assert.match(ctx.db.data.core.testapp.updater, new RegExp(assertError.message))
|
||||
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.ok(assertVersion.log)
|
||||
assert.match(assertVersion.log, /\. \n/)
|
||||
assert.match(assertVersion.log, new RegExp(assertError.message))
|
||||
|
@ -341,8 +373,8 @@ t.timeout(250).describe('#update()', function() {
|
|||
|
||||
assert.strictEqual(app.updating, false)
|
||||
assert.strictEqual(err, assertError)
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.strictEqual(assertVersion.failtodownload, 2)
|
||||
})
|
||||
|
||||
|
@ -351,27 +383,27 @@ t.timeout(250).describe('#update()', function() {
|
|||
const assertLink = 'All of you'
|
||||
const assertVersion = { version: '123456789', link: assertLink, filename: 'test.7z' }
|
||||
const assertTarget = util.getPathFromRoot('./testapp/123456789/file.7z')
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 0)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 0)
|
||||
|
||||
provider.getLatestVersion.resolves(assertVersion)
|
||||
provider.downloadVersion.rejects(assertError)
|
||||
db.data.core.testapp.updater = ''
|
||||
ctx.db.data.core.testapp.updater = ''
|
||||
|
||||
let err = await assert.isRejected(app.update())
|
||||
|
||||
assert.strictEqual(app.updating, false)
|
||||
assert.strictEqual(err, assertError)
|
||||
assert.match(db.data.core.testapp.updater, /found/i)
|
||||
assert.match(db.data.core.testapp.updater, new RegExp(assertVersion.version))
|
||||
assert.match(db.data.core.testapp.updater, /downloading/i)
|
||||
assert.match(db.data.core.testapp.updater, new RegExp(assertLink))
|
||||
assert.match(db.data.core.testapp.updater, new RegExp(assertTarget.replace(/\\/g, '\\\\')))
|
||||
assert.match(ctx.db.data.core.testapp.updater, /found/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, new RegExp(assertVersion.version))
|
||||
assert.match(ctx.db.data.core.testapp.updater, /downloading/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, new RegExp(assertLink))
|
||||
assert.match(ctx.db.data.core.testapp.updater, new RegExp(assertTarget.replace(/\\/g, '\\\\')))
|
||||
assert.strictEqual(provider.downloadVersion.firstCall[0], assertVersion)
|
||||
assert.strictEqual(provider.downloadVersion.firstCall[1], assertTarget)
|
||||
assert.match(db.data.core.testapp.updater, new RegExp(assertError.message))
|
||||
assert.match(ctx.db.data.core.testapp.updater, new RegExp(assertError.message))
|
||||
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.ok(assertVersion.log)
|
||||
assert.match(assertVersion.log, /\. \n/)
|
||||
assert.match(assertVersion.log, new RegExp(assertError.message))
|
||||
|
@ -389,8 +421,8 @@ t.timeout(250).describe('#update()', function() {
|
|||
|
||||
assert.strictEqual(app.updating, false)
|
||||
assert.strictEqual(err, assertError)
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.strictEqual(assertVersion.failtodownload, 2)
|
||||
})
|
||||
|
||||
|
@ -404,7 +436,7 @@ t.timeout(250).describe('#update()', function() {
|
|||
stream(assertExtractText)
|
||||
return Promise.reject(assertError)
|
||||
})
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 0)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 0)
|
||||
|
||||
let err = await assert.isRejected(app.update())
|
||||
|
||||
|
@ -413,8 +445,8 @@ t.timeout(250).describe('#update()', function() {
|
|||
assert.strictEqual(app.updating, false)
|
||||
assert.strictEqual(stubExtract.firstCall[0], assertTarget)
|
||||
|
||||
assert.match(db.data.core.testapp.updater, new RegExp('extracting[^.]+file\.7z', 'i'))
|
||||
assert.match(db.data.core.testapp.updater, new RegExp(assertError.message))
|
||||
assert.match(ctx.db.data.core.testapp.updater, new RegExp('extracting[^.]+file\.7z', 'i'))
|
||||
assert.match(ctx.db.data.core.testapp.updater, new RegExp(assertError.message))
|
||||
|
||||
assert.ok(stubFsRm.called)
|
||||
assert.strictEqual(stubFsRm.firstCall[0], assertFolder)
|
||||
|
@ -423,8 +455,8 @@ t.timeout(250).describe('#update()', function() {
|
|||
|
||||
assert.ok(stubWrite.called)
|
||||
assert.ok(stubWrite.callCount >= 3)
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 1)
|
||||
let version = db.data.core.testapp.versions[0]
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 1)
|
||||
let version = ctx.db.data.core.testapp.versions[0]
|
||||
assert.ok(version.log)
|
||||
assert.match(version.log, /\. \n/)
|
||||
assert.match(version.log, new RegExp('extracting[^.]+file\.7z', 'i'))
|
||||
|
@ -439,15 +471,15 @@ t.timeout(250).describe('#update()', function() {
|
|||
|
||||
assert.strictEqual(app.updating, false)
|
||||
assert.strictEqual(err, assertError)
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(db.data.core.testapp.versions[0], version)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions[0], version)
|
||||
assert.strictEqual(version.failtodownload, 2)
|
||||
})
|
||||
|
||||
t.test('should call fs remove the archieve file', async function() {
|
||||
const assertError = new Error('Tiny Kizuna')
|
||||
const assertTarget = util.getPathFromRoot('./testapp/123456789/file.7z')
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 0)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 0)
|
||||
stubFsRm.rejects(assertError)
|
||||
|
||||
await app.update()
|
||||
|
@ -464,7 +496,7 @@ t.timeout(250).describe('#update()', function() {
|
|||
const assertTarget = util.getPathFromRoot('./testapp/123456789/index.mjs')
|
||||
stubRunCommand.rejects(new Error('should not be seen'))
|
||||
stubFsStat.rejects(assertError)
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 0)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 0)
|
||||
|
||||
let err = await assert.isRejected(app.update())
|
||||
|
||||
|
@ -472,11 +504,11 @@ t.timeout(250).describe('#update()', function() {
|
|||
assert.ok(stubExtract.called)
|
||||
assert.strictEqual(err, assertError)
|
||||
assert.strictEqual(stubFsStat.firstCall[0], assertTarget)
|
||||
assert.match(db.data.core.testapp.updater, /index\.mjs/i)
|
||||
assert.match(db.data.core.testapp.updater, /missing/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /index\.mjs/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /missing/i)
|
||||
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 1)
|
||||
let version = db.data.core.testapp.versions[0]
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 1)
|
||||
let version = ctx.db.data.core.testapp.versions[0]
|
||||
assert.ok(version.log)
|
||||
assert.match(version.log, /index\.mjs/i)
|
||||
assert.match(version.log, /missing/i)
|
||||
|
@ -489,8 +521,8 @@ t.timeout(250).describe('#update()', function() {
|
|||
|
||||
assert.strictEqual(app.updating, false)
|
||||
assert.strictEqual(err, assertError)
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(db.data.core.testapp.versions[0], version)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions[0], version)
|
||||
assert.strictEqual(version.failtodownload, 2)
|
||||
})
|
||||
|
||||
|
@ -504,7 +536,7 @@ t.timeout(250).describe('#update()', function() {
|
|||
}
|
||||
return Promise.resolve({})
|
||||
})
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 0)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 0)
|
||||
|
||||
await app.update()
|
||||
|
||||
|
@ -514,16 +546,16 @@ t.timeout(250).describe('#update()', function() {
|
|||
assert.strictEqual(stubFsStat.secondCall[0], assertTarget)
|
||||
assert.ok(stubExtract.called)
|
||||
assert.notOk(stubRunCommand.called)
|
||||
assert.match(db.data.core.testapp.updater, /package\.json/i)
|
||||
assert.match(db.data.core.testapp.updater, /contain/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /package\.json/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /contain/i)
|
||||
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 1)
|
||||
let version = db.data.core.testapp.versions[0]
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 1)
|
||||
let version = ctx.db.data.core.testapp.versions[0]
|
||||
assert.ok(version.log)
|
||||
assert.match(version.log, /package\.json/i)
|
||||
assert.match(version.log, /contain/i)
|
||||
assert.ok(version.log.endsWith('\n'))
|
||||
assert.ok(db.data.core.testapp.latestInstalled)
|
||||
assert.ok(ctx.db.data.core.testapp.latestInstalled)
|
||||
assert.match(version.log, /finished/i)
|
||||
assert.match(version.log, /updating/i)
|
||||
|
||||
|
@ -533,10 +565,10 @@ t.timeout(250).describe('#update()', function() {
|
|||
|
||||
await app.update()
|
||||
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 1)
|
||||
assert.notOk(provider.downloadVersion.called)
|
||||
assert.match(db.data.core.testapp.updater, /already/i)
|
||||
assert.match(db.data.core.testapp.updater, /nothing/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /already/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /nothing/i)
|
||||
})
|
||||
|
||||
t.test('should otherwise call npm install correctly', async function() {
|
||||
|
@ -547,7 +579,7 @@ t.timeout(250).describe('#update()', function() {
|
|||
const assertTarget = util.getPathFromRoot('./testapp/123456789')
|
||||
const assertTargetCheck = util.getPathFromRoot('./testapp/123456789/')
|
||||
provider.getLatestVersion.resolves(assertVersion)
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 0)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 0)
|
||||
|
||||
stubExtract.returnWith(function(target, stream) {
|
||||
stream(assertExtractText)
|
||||
|
@ -578,10 +610,10 @@ t.timeout(250).describe('#update()', function() {
|
|||
|
||||
assert.ok(stubWrite.called)
|
||||
assert.ok(stubWrite.callCount >= 4)
|
||||
assert.match(db.data.core.testapp.updater, /npm/i)
|
||||
assert.match(db.data.core.testapp.updater, /install/i)
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /npm/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /install/i)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.ok(assertVersion.log)
|
||||
assert.match(assertVersion.log, /\. \n/)
|
||||
assert.match(assertVersion.log, new RegExp(assertExtractText))
|
||||
|
@ -598,23 +630,23 @@ t.timeout(250).describe('#update()', function() {
|
|||
|
||||
assert.strictEqual(app.updating, false)
|
||||
assert.strictEqual(err, assertError)
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.strictEqual(assertVersion.failtoinstall, 2)
|
||||
})
|
||||
|
||||
t.test('should update latest installed correctly', async function() {
|
||||
const assertVersion = { version: '123456789', link: 'httplinkhere', filename: 'test.7z' }
|
||||
provider.getLatestVersion.resolves(assertVersion)
|
||||
assert.notStrictEqual(db.data.core.testapp.latestInstalled, assertVersion.version)
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 0)
|
||||
assert.notStrictEqual(ctx.db.data.core.testapp.latestInstalled, assertVersion.version)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 0)
|
||||
|
||||
await app.update()
|
||||
|
||||
assert.strictEqual(app.updating, false)
|
||||
assert.strictEqual(db.data.core.testapp.latestInstalled, assertVersion.version)
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.latestInstalled, assertVersion.version)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.ok(assertVersion.log)
|
||||
assert.ok(stubWrite.callCount >= 4)
|
||||
assert.strictEqual(assertVersion.installed, true)
|
||||
|
@ -635,18 +667,18 @@ t.timeout(250).describe('#update()', function() {
|
|||
const assertVersion = { version: '123456789', link: 'httplinkhere', filename: 'test.7z', log: oldLog, stable: assertStable }
|
||||
|
||||
assertVersion.id = assertVersion.version
|
||||
db.upsert(db.data.core.testapp.versions, assertVersion)
|
||||
ctx.db.upsert(ctx.db.data.core.testapp.versions, assertVersion)
|
||||
|
||||
provider.getLatestVersion.resolves({ version: assertVersion.version, link: assertNewLink, filename: assertNewFilename })
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 1)
|
||||
|
||||
await app.update()
|
||||
|
||||
assert.ok(assertVersion.log)
|
||||
assert.ok(stubWrite.callCount >= 4)
|
||||
assert.strictEqual(app.updating, false)
|
||||
assert.strictEqual(db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions.length, 1)
|
||||
assert.strictEqual(ctx.db.data.core.testapp.versions[0], assertVersion)
|
||||
assert.strictEqual(assertVersion.stable, assertStable)
|
||||
assert.ok(assertVersion.log)
|
||||
assert.ok(assertVersion.log.startsWith(oldLog))
|
||||
|
@ -664,16 +696,16 @@ t.timeout(250).describe('#update()', function() {
|
|||
const assertVersion = { version: '999.888.777.666', filename: 'test.7z' }
|
||||
provider.getLatestVersion.resolves(assertVersion)
|
||||
provider.downloadVersion.rejects(assertError)
|
||||
db.data.core.testapp.updater = ''
|
||||
db.data.core.testapp.latestInstalled = assertVersion.version
|
||||
ctx.db.data.core.testapp.updater = ''
|
||||
ctx.db.data.core.testapp.latestInstalled = assertVersion.version
|
||||
|
||||
await app.update()
|
||||
|
||||
assert.ok(stubWrite.callCount >= 1)
|
||||
assert.strictEqual(app.updating, false)
|
||||
assert.notOk(provider.downloadVersion.called)
|
||||
assert.match(db.data.core.testapp.updater, /already/i)
|
||||
assert.match(db.data.core.testapp.updater, /nothing/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /already/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /nothing/i)
|
||||
})
|
||||
|
||||
t.test('should do nothing if installed version is found', async function() {
|
||||
|
@ -681,20 +713,20 @@ t.timeout(250).describe('#update()', function() {
|
|||
const assertVersion = { version: '999.888.777.666', filename: 'test.7z' }
|
||||
provider.getLatestVersion.resolves(assertVersion)
|
||||
provider.downloadVersion.rejects(assertError)
|
||||
db.data.core.testapp.updater = ''
|
||||
ctx.db.data.core.testapp.updater = ''
|
||||
|
||||
db.upsert(db.data.core.testapp.versions, { id: '111.111.111.111', installed: true })
|
||||
db.upsert(db.data.core.testapp.versions, { id: '222.222.222.222', installed: true })
|
||||
db.upsert(db.data.core.testapp.versions, { id: '999.888.777.666', installed: true })
|
||||
db.upsert(db.data.core.testapp.versions, { id: '333.333.333.333', installed: true })
|
||||
ctx.db.upsert(ctx.db.data.core.testapp.versions, { id: '111.111.111.111', installed: true })
|
||||
ctx.db.upsert(ctx.db.data.core.testapp.versions, { id: '222.222.222.222', installed: true })
|
||||
ctx.db.upsert(ctx.db.data.core.testapp.versions, { id: '999.888.777.666', installed: true })
|
||||
ctx.db.upsert(ctx.db.data.core.testapp.versions, { id: '333.333.333.333', installed: true })
|
||||
|
||||
await app.update()
|
||||
|
||||
assert.ok(stubWrite.callCount >= 1)
|
||||
assert.strictEqual(app.updating, false)
|
||||
assert.notOk(provider.downloadVersion.called)
|
||||
assert.match(db.data.core.testapp.updater, /already/i)
|
||||
assert.match(db.data.core.testapp.updater, /nothing/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /already/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /nothing/i)
|
||||
})
|
||||
|
||||
t.test('should do nothing it exists and failtodownload is higher than 3', async function() {
|
||||
|
@ -702,17 +734,17 @@ t.timeout(250).describe('#update()', function() {
|
|||
const assertVersion = { version: '999.888.777.666', filename: 'test.7z' }
|
||||
provider.getLatestVersion.resolves(assertVersion)
|
||||
provider.downloadVersion.rejects(assertError)
|
||||
db.data.core.testapp.updater = ''
|
||||
db.upsert(db.data.core.testapp.versions, { id: '999.888.777.666', version: '999.888.777.666', link: 'httplinkhere', filename: 'test.7z', failtodownload: 4 })
|
||||
ctx.db.data.core.testapp.updater = ''
|
||||
ctx.db.upsert(ctx.db.data.core.testapp.versions, { id: '999.888.777.666', version: '999.888.777.666', link: 'httplinkhere', filename: 'test.7z', failtodownload: 4 })
|
||||
|
||||
await app.update()
|
||||
|
||||
assert.ok(stubWrite.callCount >= 1)
|
||||
assert.strictEqual(app.updating, false)
|
||||
assert.notOk(provider.downloadVersion.called)
|
||||
assert.match(db.data.core.testapp.updater, /many/i)
|
||||
assert.match(db.data.core.testapp.updater, /fail/i)
|
||||
assert.match(db.data.core.testapp.updater, /skip/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /many/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /fail/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /skip/i)
|
||||
})
|
||||
|
||||
t.test('should do nothing it exists and failtoinstall is higher than 3', async function() {
|
||||
|
@ -720,31 +752,32 @@ t.timeout(250).describe('#update()', function() {
|
|||
const assertVersion = { version: '999.888.777.666', filename: 'test.7z' }
|
||||
provider.getLatestVersion.resolves(assertVersion)
|
||||
provider.downloadVersion.rejects(assertError)
|
||||
db.data.core.testapp.updater = ''
|
||||
db.upsert(db.data.core.testapp.versions, { id: '999.888.777.666', version: '999.888.777.666', link: 'httplinkhere', filename: 'test.7z', failtoinstall: 4 })
|
||||
ctx.db.data.core.testapp.updater = ''
|
||||
ctx.db.upsert(ctx.db.data.core.testapp.versions, { id: '999.888.777.666', version: '999.888.777.666', link: 'httplinkhere', filename: 'test.7z', failtoinstall: 4 })
|
||||
|
||||
await app.update()
|
||||
|
||||
assert.ok(stubWrite.callCount >= 1)
|
||||
assert.strictEqual(app.updating, false)
|
||||
assert.notOk(provider.downloadVersion.called)
|
||||
assert.match(db.data.core.testapp.updater, /many/i)
|
||||
assert.match(db.data.core.testapp.updater, /fail/i)
|
||||
assert.match(db.data.core.testapp.updater, /skip/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /many/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /fail/i)
|
||||
assert.match(ctx.db.data.core.testapp.updater, /skip/i)
|
||||
})
|
||||
})
|
||||
|
||||
t.timeout(250).describe('#registerModule()', function() {
|
||||
const assertAppName = 'testappregister'
|
||||
let db
|
||||
let ctx
|
||||
let app
|
||||
|
||||
t.beforeEach(function() {
|
||||
return lowdb({ test: { } }, logger, null).then(function(res) {
|
||||
db = res
|
||||
let provider = new StaticProvider()
|
||||
app = new Application(util, db, provider, assertAppName)
|
||||
})
|
||||
return createFakeContext()
|
||||
.then(function(res) {
|
||||
ctx = res
|
||||
let provider = new StaticProvider()
|
||||
app = new Application(ctx, provider, assertAppName)
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should fail if not an object with start', function() {
|
||||
|
|
365
test/core.test.mjs
Normal file
365
test/core.test.mjs
Normal file
|
@ -0,0 +1,365 @@
|
|||
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, createFakeLog(), 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)
|
||||
})
|
||||
})
|
|
@ -118,9 +118,9 @@ t.test('Should support adding an application with defaults', async function() {
|
|||
|
||||
assert.ok(db.data.core.app)
|
||||
assert.ok(db.data.core.app.versions)
|
||||
assert.strictEqual(db.data.core.app.active, null)
|
||||
assert.strictEqual(db.data.core.app.latestInstalled, null)
|
||||
assert.strictEqual(db.data.core.app.latestVersion, null)
|
||||
assert.strictEqual(db.data.core.app.active, '')
|
||||
assert.strictEqual(db.data.core.app.latestInstalled, '')
|
||||
assert.strictEqual(db.data.core.app.latestVersion, '')
|
||||
|
||||
assert.notOk(db.data.core.herpderp)
|
||||
|
||||
|
@ -128,9 +128,9 @@ t.test('Should support adding an application with defaults', async function() {
|
|||
|
||||
assert.ok(db.data.core.herpderp)
|
||||
assert.ok(db.data.core.herpderp.versions)
|
||||
assert.strictEqual(db.data.core.herpderp.active, null)
|
||||
assert.strictEqual(db.data.core.herpderp.latestInstalled, null)
|
||||
assert.strictEqual(db.data.core.herpderp.latestVersion, null)
|
||||
assert.strictEqual(db.data.core.herpderp.active, '')
|
||||
assert.strictEqual(db.data.core.herpderp.latestInstalled, '')
|
||||
assert.strictEqual(db.data.core.herpderp.latestVersion, '')
|
||||
})
|
||||
|
||||
t.test('Should support reading from db', async function() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export function start(db, log, core, http, port) {
|
||||
export function start(http, port, ctx) {
|
||||
const server = http.createServer(function (req, res) {
|
||||
res.writeHead(200);
|
||||
res.end(JSON.stringify({ version: 'exampleindex' }))
|
||||
|
@ -9,8 +9,8 @@ export function start(db, log, core, http, port) {
|
|||
if (err) {
|
||||
return rej(err)
|
||||
}
|
||||
log.event.info(`Server is listening on ${port} serving package ${staticPackage}`)
|
||||
log.info(`Server is listening on ${port} serving package ${staticPackage}`)
|
||||
ctx.log.event.info(`Server is listening on ${port} serving exampleindex`)
|
||||
ctx.log.info(`Server is listening on ${port} serving exampleindex`)
|
||||
res()
|
||||
})
|
||||
})
|
||||
|
|
29
test/helpers.mjs
Normal file
29
test/helpers.mjs
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { stub } from 'eltro'
|
||||
import lowdb from '../core/db.mjs'
|
||||
import Util from '../core/util.mjs'
|
||||
|
||||
export function createFakeLog() {
|
||||
return {
|
||||
info: stub(),
|
||||
warn: stub(),
|
||||
error: stub(),
|
||||
event: {
|
||||
info: stub(),
|
||||
warn: stub(),
|
||||
error: stub(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createFakeContext(config = { }, util = new Util(import.meta.url), filename = null) {
|
||||
const log = createFakeLog()
|
||||
|
||||
return lowdb(config, log, filename).then(function(res) {
|
||||
return {
|
||||
db: res,
|
||||
util: util,
|
||||
log: log,
|
||||
core: { },
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import { Eltro as t, assert, stub } from 'eltro'
|
||||
import http from 'http'
|
||||
import https from 'https'
|
||||
import { setTimeout } from 'timers/promises'
|
||||
import { request } from '../core/client.mjs'
|
||||
import HttpServer from '../core/http.mjs'
|
||||
|
||||
|
@ -22,9 +23,7 @@ t.describe('Sockets', function() {
|
|||
let http = new HttpServer()
|
||||
|
||||
t.after(function() {
|
||||
http.closeServer().then(function() { }, function(err) {
|
||||
console.error(err)
|
||||
})
|
||||
return http.closeServer()
|
||||
})
|
||||
|
||||
t.test('should keep track of sockets through its lifetime', function(cb) {
|
||||
|
@ -41,7 +40,7 @@ t.describe('Sockets', function() {
|
|||
Promise.resolve()
|
||||
.then(async function() {
|
||||
await new Promise(function(res, rej) {
|
||||
server.listen(port, function(err) { if (err) rej(err); res()})
|
||||
server.listen(port, function() { res()})
|
||||
})
|
||||
|
||||
assert.strictEqual(actives.length, 0)
|
||||
|
@ -50,16 +49,18 @@ t.describe('Sockets', function() {
|
|||
request({}, prefix).then(function() {}, cb)
|
||||
request({}, prefix).then(async function() {
|
||||
while (http.sockets.size > 0) {
|
||||
await new Promise(function(res) { setTimeout(res, 10) })
|
||||
await setTimeout(10)
|
||||
}
|
||||
assert.strictEqual(http.sockets.size, 0)
|
||||
cb()
|
||||
}, cb)
|
||||
|
||||
while (actives.length < 2) {
|
||||
await new Promise(function(res) { setTimeout(res, 10) })
|
||||
await setTimeout(10)
|
||||
}
|
||||
assert.strictEqual(http.sockets.size, 2)
|
||||
|
||||
assert.ok(http.active)
|
||||
actives[0].statusCode = 200
|
||||
actives[0].end('{}')
|
||||
actives[1].statusCode = 200
|
||||
|
@ -68,13 +69,17 @@ t.describe('Sockets', function() {
|
|||
})
|
||||
})
|
||||
|
||||
t.describe('Close', function() {
|
||||
t.describe('closeServer()', function() {
|
||||
let http = new HttpServer()
|
||||
|
||||
t.after(function() {
|
||||
http.closeServer().then(function() { }, function(err) {
|
||||
console.error(err)
|
||||
})
|
||||
return http.closeServer()
|
||||
})
|
||||
|
||||
t.test('should not fail if server is not listening', function() {
|
||||
http.createServer(function() { })
|
||||
|
||||
return http.closeServer()
|
||||
})
|
||||
|
||||
t.test('should support forcefully closing them on server close', function(cb) {
|
||||
|
@ -90,7 +95,7 @@ t.describe('Close', function() {
|
|||
Promise.resolve()
|
||||
.then(async function() {
|
||||
await new Promise(function(res, rej) {
|
||||
server.listen(port, function(err) { if (err) rej(err); res()})
|
||||
server.listen(port, function() { res()})
|
||||
})
|
||||
|
||||
assert.strictEqual(http.sockets.size, 0)
|
||||
|
@ -105,13 +110,15 @@ t.describe('Close', function() {
|
|||
)
|
||||
|
||||
while (http.sockets.size < 2) {
|
||||
await new Promise(function(res) { setTimeout(res, 10) })
|
||||
await setTimeout(10)
|
||||
}
|
||||
|
||||
assert.ok(http.active)
|
||||
|
||||
http.closeServer().then(function() { }, cb)
|
||||
|
||||
while (requestErrors.length < 2) {
|
||||
await new Promise(function(res) { setTimeout(res, 10) })
|
||||
await setTimeout(10)
|
||||
}
|
||||
assert.strictEqual(http.sockets.size, 0)
|
||||
assert.strictEqual(requestErrors.length, 2)
|
||||
|
@ -122,12 +129,39 @@ t.describe('Close', function() {
|
|||
assert.strictEqual(requestErrors[1].code, 'ECONNRESET')
|
||||
|
||||
while (requestErrors.length < 2) {
|
||||
await new Promise(function(res) { setTimeout(res, 10) })
|
||||
await setTimeout(10)
|
||||
}
|
||||
while (http.active) {
|
||||
await new Promise(function(res) { setTimeout(res, 10) })
|
||||
await setTimeout(10)
|
||||
}
|
||||
})
|
||||
.then(function() { cb()}, cb)
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('listenAsync()', function() {
|
||||
let httpFirst = new HttpServer()
|
||||
let httpSecond = new HttpServer()
|
||||
|
||||
t.after(function() {
|
||||
return Promise.all([
|
||||
httpFirst.closeServer(),
|
||||
httpSecond.closeServer(),
|
||||
])
|
||||
})
|
||||
|
||||
t.test('should reject successfully if port is busy', async function() {
|
||||
let serverFirst = httpFirst.createServer(function() { })
|
||||
let serverSecond = httpSecond.createServer(function() { })
|
||||
|
||||
await serverFirst.listenAsync(port)
|
||||
|
||||
await setTimeout(10)
|
||||
|
||||
let err = await assert.isRejected(serverSecond.listenAsync(port))
|
||||
assert.strictEqual(err.code, 'EADDRINUSE')
|
||||
|
||||
assert.ok(serverFirst.listening)
|
||||
assert.notOk(serverSecond.listening)
|
||||
})
|
||||
})
|
||||
|
|
286
test/log.test.mjs
Normal file
286
test/log.test.mjs
Normal file
|
@ -0,0 +1,286 @@
|
|||
import { Eltro as t, assert, stub } from 'eltro'
|
||||
import getLog from '../core/log.mjs'
|
||||
|
||||
t.describe('#constructor', function() {
|
||||
t.afterEach(function() {
|
||||
process.env.NODE_ENV = null
|
||||
})
|
||||
|
||||
t.test('should add name', function() {
|
||||
const assertName = 'Stray Cat'
|
||||
let logger = getLog(assertName)
|
||||
assert.strictEqual(logger.fields.name, assertName)
|
||||
|
||||
process.env.NODE_ENV = 'production'
|
||||
logger = getLog(assertName)
|
||||
assert.strictEqual(logger.fields.name, assertName)
|
||||
})
|
||||
|
||||
t.test('should add default stdout streams in normal environment', function() {
|
||||
let logger = getLog('app', null)
|
||||
assert.strictEqual(logger.streams.length, 4)
|
||||
assert.strictEqual(logger.streams[0].stream, process.stdout)
|
||||
assert.strictEqual(logger.streams[0].level, 20)
|
||||
})
|
||||
|
||||
t.test('should add default file log stream in production environment', function() {
|
||||
process.env.NODE_ENV = 'production'
|
||||
|
||||
let logger = getLog('app', null)
|
||||
assert.strictEqual(logger.streams.length, 4)
|
||||
assert.strictEqual(logger.streams[0].path, 'log.log')
|
||||
assert.strictEqual(logger.streams[0].level, 30)
|
||||
})
|
||||
|
||||
t.test('should not add default stream if empty array', function() {
|
||||
let logger = getLog('app', [])
|
||||
assert.strictEqual(logger.streams.length, 3)
|
||||
|
||||
process.env.NODE_ENV = 'production'
|
||||
logger = getLog('app', [])
|
||||
assert.strictEqual(logger.streams.length, 3)
|
||||
})
|
||||
|
||||
t.test('should replace process.stdout with actual process', function() {
|
||||
let logger = getLog('app', [{ stream: 'process.stdout', level: 'info' }])
|
||||
assert.strictEqual(logger.streams.length, 4)
|
||||
assert.strictEqual(logger.streams[0].stream, process.stdout)
|
||||
|
||||
process.env.NODE_ENV = 'production'
|
||||
logger = getLog('app', [{ stream: 'process.stdout', level: 'info' }])
|
||||
assert.strictEqual(logger.streams.length, 4)
|
||||
assert.strictEqual(logger.streams[0].stream, process.stdout)
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('ringbuffer', function() {
|
||||
let logger
|
||||
|
||||
t.beforeEach(function() {
|
||||
logger = getLog('app', [])
|
||||
})
|
||||
|
||||
t.test('should have ringbuffer for info', function() {
|
||||
const assertMessage = 'Oitachi'
|
||||
|
||||
assert.strictEqual(logger.ringbuffer.records.length, 0)
|
||||
logger.info(assertMessage)
|
||||
assert.strictEqual(logger.ringbuffer.records.length, 1)
|
||||
assert.strictEqual(logger.ringbuffer.records[0].level, 30)
|
||||
assert.strictEqual(logger.ringbuffer.records[0].msg, assertMessage)
|
||||
logger.debug(assertMessage)
|
||||
assert.strictEqual(logger.ringbuffer.records.length, 1)
|
||||
logger.warn(assertMessage)
|
||||
assert.strictEqual(logger.ringbuffer.records.length, 2)
|
||||
assert.strictEqual(logger.ringbuffer.records[1].level, 40)
|
||||
assert.strictEqual(logger.ringbuffer.records[1].msg, assertMessage)
|
||||
})
|
||||
|
||||
t.test('should keep it limited to max 100 records', function() {
|
||||
const assertPrefix = 'In memory of Keiten'
|
||||
|
||||
for (let i = 1; i <= 101; i++) {
|
||||
logger.info(assertPrefix + i)
|
||||
}
|
||||
assert.strictEqual(logger.ringbuffer.records.length, 100)
|
||||
assert.strictEqual(logger.ringbuffer.records[0].msg, assertPrefix + '2')
|
||||
|
||||
logger.info(assertPrefix)
|
||||
|
||||
assert.strictEqual(logger.ringbuffer.records.length, 100)
|
||||
assert.strictEqual(logger.ringbuffer.records[0].msg, assertPrefix + '3')
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('ringbufferwarn', function() {
|
||||
let logger
|
||||
|
||||
t.beforeEach(function() {
|
||||
logger = getLog('app', [])
|
||||
})
|
||||
|
||||
t.test('should have ringbufferwarn for info', function() {
|
||||
const assertMessage = 'Oitachi'
|
||||
|
||||
assert.strictEqual(logger.ringbufferwarn.records.length, 0)
|
||||
logger.warn(assertMessage)
|
||||
assert.strictEqual(logger.ringbufferwarn.records.length, 1)
|
||||
assert.strictEqual(logger.ringbufferwarn.records[0].level, 40)
|
||||
assert.strictEqual(logger.ringbufferwarn.records[0].msg, assertMessage)
|
||||
logger.info(assertMessage)
|
||||
assert.strictEqual(logger.ringbufferwarn.records.length, 1)
|
||||
logger.error(assertMessage)
|
||||
assert.strictEqual(logger.ringbufferwarn.records.length, 2)
|
||||
assert.strictEqual(logger.ringbufferwarn.records[1].level, 50)
|
||||
assert.strictEqual(logger.ringbufferwarn.records[1].msg, assertMessage)
|
||||
})
|
||||
|
||||
t.test('should keep it limited to max 100 records', function() {
|
||||
const assertPrefix = 'In memory of Keiten'
|
||||
|
||||
for (let i = 1; i <= 101; i++) {
|
||||
logger.warn(assertPrefix + i)
|
||||
}
|
||||
assert.strictEqual(logger.ringbufferwarn.records.length, 100)
|
||||
assert.strictEqual(logger.ringbufferwarn.records[0].msg, assertPrefix + '2')
|
||||
|
||||
logger.warn(assertPrefix)
|
||||
|
||||
assert.strictEqual(logger.ringbufferwarn.records.length, 100)
|
||||
assert.strictEqual(logger.ringbufferwarn.records[0].msg, assertPrefix + '3')
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('event', function() {
|
||||
t.test('should call import if not in production', async function() {
|
||||
let stubImport = stub()
|
||||
stubImport.rejects(new Error('should not be seen'))
|
||||
|
||||
let logger = getLog('app', [], { import: stubImport })
|
||||
|
||||
let first = new Promise(function(res, rej) {
|
||||
setImmediate(function() { logger.event.warn('text message here').then(res, rej) })
|
||||
})
|
||||
let second = new Promise(function(res, rej) {
|
||||
setImmediate(function() { logger.event.warn('new message here').then(res, rej) })
|
||||
})
|
||||
|
||||
await Promise.all([
|
||||
first, second,
|
||||
])
|
||||
|
||||
assert.notOk(stubImport.called)
|
||||
})
|
||||
|
||||
t.test('should call import correctly if in production and fail only once', async function() {
|
||||
process.env.NODE_ENV = 'production'
|
||||
|
||||
let stubImport = stub()
|
||||
stubImport.rejects(new Error('should not be seen'))
|
||||
|
||||
let logger = getLog('app', [], { import: stubImport })
|
||||
|
||||
let first = new Promise(function(res, rej) {
|
||||
setImmediate(function() { try { logger.event.warn('first').then(res, rej) } catch (err) { rej(err) } })
|
||||
})
|
||||
let second = new Promise(function(res, rej) {
|
||||
setImmediate(function() { try { logger.event.warn('second').then(res, rej) } catch (err) { rej(err) } })
|
||||
})
|
||||
|
||||
await Promise.all([
|
||||
first, second,
|
||||
])
|
||||
|
||||
assert.ok(stubImport.called)
|
||||
assert.strictEqual(stubImport.callCount, 1)
|
||||
assert.strictEqual(stubImport.firstCall[0], 'node-windows')
|
||||
|
||||
await new Promise(function(res, rej) {
|
||||
setImmediate(function() { try { logger.event.warn('third').then(res, rej) } catch (err) { rej(err) } })
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should call event on imported object correctly', async function() {
|
||||
const assertName = 'It is going to be The Special'
|
||||
let checkName = ''
|
||||
let stubInfo = stub().returnWith(function(msg, code, cb) { setTimeout(cb, 20) })
|
||||
let stubWarn = stub().returnWith(function(msg, code, cb) { setTimeout(cb, 20) })
|
||||
let stubError = stub().returnWith(function(msg, code, cb) { setTimeout(cb, 20) })
|
||||
process.env.NODE_ENV = 'production'
|
||||
|
||||
let stubImport = stub().resolves({
|
||||
default: {
|
||||
EventLogger: function(name) {
|
||||
checkName = name
|
||||
this.info = stubInfo
|
||||
this.warn = stubWarn
|
||||
this.error = stubError
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
let logger = getLog(assertName, [], { import: stubImport })
|
||||
|
||||
let first = new Promise(function(res, rej) {
|
||||
setImmediate(function() { try { logger.event.info('first', 1010).then(res, rej) } catch (err) { rej(err) } })
|
||||
})
|
||||
let second = new Promise(function(res, rej) {
|
||||
setImmediate(function() { try { logger.event.warn('second', 1020).then(res, rej) } catch (err) { rej(err) } })
|
||||
})
|
||||
|
||||
await Promise.all([
|
||||
first, second,
|
||||
])
|
||||
|
||||
assert.strictEqual(checkName, assertName)
|
||||
assert.ok(stubInfo.called)
|
||||
assert.strictEqual(stubInfo.firstCall[0], 'first')
|
||||
assert.strictEqual(stubInfo.firstCall[1], 1010)
|
||||
assert.ok(stubWarn.called)
|
||||
assert.strictEqual(stubWarn.firstCall[0], 'second')
|
||||
assert.strictEqual(stubWarn.firstCall[1], 1020)
|
||||
assert.notOk(stubError.called)
|
||||
|
||||
await new Promise(function(res, rej) {
|
||||
setImmediate(function() { try { logger.event.error('third', 1030).then(res, rej) } catch (err) { rej(err) } })
|
||||
})
|
||||
|
||||
assert.ok(stubError.called)
|
||||
assert.strictEqual(stubError.firstCall[0], 'third')
|
||||
assert.strictEqual(stubError.firstCall[1], 1030)
|
||||
})
|
||||
|
||||
t.test('should work even if it were to throw', async function() {
|
||||
const assertName = 'It is going to be The Special'
|
||||
let checkName = ''
|
||||
let stubInfo = stub().returnWith(function() { throw new Error('not to be seen') })
|
||||
let stubWarn = stub().returnWith(function() { throw new Error('not to be seen') })
|
||||
let stubError = stub().returnWith(function() { throw new Error('not to be seen') })
|
||||
process.env.NODE_ENV = 'production'
|
||||
|
||||
let stubImport = stub().resolves({
|
||||
default: {
|
||||
EventLogger: function(name) {
|
||||
checkName = name
|
||||
this.info = stubInfo
|
||||
this.warn = stubWarn
|
||||
this.error = stubError
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
let logger = getLog(assertName, [], { import: stubImport })
|
||||
|
||||
let first = new Promise(function(res, rej) {
|
||||
setImmediate(function() { try { logger.event.info().then(res, rej) } catch (err) { rej(err) } })
|
||||
})
|
||||
let second = new Promise(function(res, rej) {
|
||||
setImmediate(function() { try { logger.event.warn().then(res, rej) } catch (err) { rej(err) } })
|
||||
})
|
||||
|
||||
await Promise.all([
|
||||
first, second,
|
||||
])
|
||||
|
||||
assert.strictEqual(checkName, assertName)
|
||||
assert.ok(stubInfo.called)
|
||||
assert.ok(stubWarn.called)
|
||||
assert.notOk(stubError.called)
|
||||
|
||||
await new Promise(function(res, rej) {
|
||||
setImmediate(function() { try { logger.event.error().then(res, rej) } catch (err) { rej(err) } })
|
||||
})
|
||||
|
||||
assert.ok(stubError.called)
|
||||
})
|
||||
|
||||
t.test('should work without stub', async function() {
|
||||
let res = await import('node-windows').catch(function() {})
|
||||
if (!res) { return }
|
||||
|
||||
process.env.NODE_ENV = 'production'
|
||||
let logger = getLog('service-core-unit-test', [])
|
||||
|
||||
await logger.event.info('Hello from service-core log.event unit test')
|
||||
})
|
||||
})
|
|
@ -1,6 +1,7 @@
|
|||
import { Eltro as t, assert} from 'eltro'
|
||||
import fs from 'fs/promises'
|
||||
import Util from '../core/util.mjs'
|
||||
import { defaults } from '../core/defaults.mjs'
|
||||
|
||||
const isWindows = process.platform === 'win32'
|
||||
|
||||
|
@ -108,43 +109,162 @@ t.describe('#getApplications()', function() {
|
|||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 1234, port: 1234 } }), [])
|
||||
})
|
||||
|
||||
function getBase(extra = {}) {
|
||||
return defaults({ app: extra }, { app: { provider: 'asdf', port: 1234, } })
|
||||
}
|
||||
|
||||
t.test('should fail to find if https is defined but not a boolean', function() {
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, } }), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, https: null } }), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, https: false } }), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, https: true } }), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, https: 'asdf' } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, https: '1234' } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, https: 0 } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, https: [] } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, https: {} } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase()), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ https: null })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ https: false })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ https: true })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ https: 'asdf' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ https: '1234' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ https: 0 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ https: [] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ https: {} })), [])
|
||||
})
|
||||
|
||||
t.test('should fail to find if updateEvery is defined but not a valid number', function() {
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, } }), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, updateEvery: null } }), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, updateEvery: 5 } }), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, updateEvery: 1000 } }), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, updateEvery: 'asdf' } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, updateEvery: '1234' } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, updateEvery: 0 } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, updateEvery: -5 } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, updateEvery: [] } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, updateEvery: {} } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase()), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ updateEvery: null })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ updateEvery: 5 })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ updateEvery: 1000 })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ updateEvery: 'asdf' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ updateEvery: '1234' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ updateEvery: 0 })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ updateEvery: -1 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ updateEvery: -5 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ updateEvery: [] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ updateEvery: {} })), [])
|
||||
})
|
||||
|
||||
t.test('should fail to find if waitUntilFail is defined but not a valid number', function() {
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, } }), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, waitUntilFail: null } }), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, waitUntilFail: 5 } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, waitUntilFail: 15 } }), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, waitUntilFail: 1000 } }), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, waitUntilFail: 'asdf' } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, waitUntilFail: '1234' } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, waitUntilFail: 0 } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, waitUntilFail: -5 } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, waitUntilFail: [] } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 1234, waitUntilFail: {} } }), [])
|
||||
t.test('should fail to find if startWaitUntilFail is defined but not a valid number', function() {
|
||||
assert.deepStrictEqual(util.getAppNames(getBase()), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ startWaitUntilFail: null })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ startWaitUntilFail: 5 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ startWaitUntilFail: 15 })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ startWaitUntilFail: 1000 })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ startWaitUntilFail: 'asdf' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ startWaitUntilFail: '1234' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ startWaitUntilFail: 0 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ startWaitUntilFail: -5 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ startWaitUntilFail: [] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ startWaitUntilFail: {} })), [])
|
||||
})
|
||||
|
||||
t.test('should fail to find if heartbeatTimeout is defined but not a valid number', function() {
|
||||
assert.deepStrictEqual(util.getAppNames(getBase()), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatTimeout: null })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatTimeout: 5 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatTimeout: 15 })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatTimeout: 1000 })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatTimeout: 'asdf' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatTimeout: '1234' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatTimeout: 0 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatTimeout: -5 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatTimeout: [] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatTimeout: {} })), [])
|
||||
})
|
||||
|
||||
t.test('should fail to find if heartbeatAttempts is defined but not a valid number', function() {
|
||||
assert.deepStrictEqual(util.getAppNames(getBase()), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttempts: null })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttempts: 1 })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttempts: 15 })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttempts: 1000 })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttempts: 'asdf' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttempts: '1234' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttempts: 0 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttempts: -5 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttempts: [] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttempts: {} })), [])
|
||||
})
|
||||
|
||||
t.test('should fail to find if heartbeatAttemptsWait is defined but not a valid number', function() {
|
||||
assert.deepStrictEqual(util.getAppNames(getBase()), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttemptsWait: null })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttemptsWait: 5 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttemptsWait: 15 })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttemptsWait: 1000 })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttemptsWait: 'asdf' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttemptsWait: '1234' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttemptsWait: 0 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttemptsWait: -5 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttemptsWait: [] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatAttemptsWait: {} })), [])
|
||||
})
|
||||
|
||||
t.test('should fail to find if heartbeatPath is defined but not a valid string', function() {
|
||||
assert.deepStrictEqual(util.getAppNames(getBase()), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatPath: null })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatPath: 5 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatPath: 15 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatPath: 1000 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatPath: 'asdf' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatPath: '1234' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatPath: '/asdf' })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatPath: '/1234' })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatPath: 0 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatPath: -5 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatPath: [] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ heartbeatPath: {} })), [])
|
||||
})
|
||||
|
||||
t.test('should fail to find if log is defined but not an array', function() {
|
||||
assert.deepStrictEqual(util.getAppNames(getBase()), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: null })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: 5 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: 'asdf' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: '1234' })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: 0 })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [] })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: {} })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: { length:1 } })), [])
|
||||
})
|
||||
|
||||
t.test('should fail to find if log has an item but level and either stream or path ', function() {
|
||||
assert.deepStrictEqual(util.getAppNames(getBase()), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: null })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [null] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [5] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [15] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [1000] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: ['asdf'] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: ['1234'] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: ['/asdf'] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: ['/1234'] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [0] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [-5] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [[]] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{}] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: null, path: 'log' }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 5, path: 'log' }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 0, path: 'log' }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: [], path: 'log' }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: {}, path: 'log' }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: '', path: 'log' }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'asdf', path: 'log' }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'fatal', path: 'log' }] })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'error', path: 'log' }] })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'warn', path: 'log' }] })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'info', path: 'log' }] })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'debug', path: 'log' }] })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'trace', path: 'log' }] })), ['app'])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'info', path: '' }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'info', path: null }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'info', path: 5 }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'info', path: 0 }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'info', path: [] }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'info', path: {} }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'info', stream: '' }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'info', stream: null }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'info', stream: 5 }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'info', stream: 0 }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'info', stream: [] }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'info', stream: {} }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'info', stream: 'asdf' }] })), [])
|
||||
assert.deepStrictEqual(util.getAppNames(getBase({ log: [{ level: 'info', stream: 'process.stdout' }] })), ['app'])
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -227,6 +347,14 @@ t.describe('#verifyConfig()', function() {
|
|||
t.describe('#extractFile()', function() {
|
||||
var util = new Util(import.meta.url)
|
||||
|
||||
t.beforeEach(function() {
|
||||
return Promise.all([
|
||||
fs.rm('./test/testapp/example.tar', { force: true }),
|
||||
fs.rm('./test/testapp/file1.txt', { force: true }),
|
||||
fs.rm('./test/testapp/file2.txt', { force: true }),
|
||||
])
|
||||
})
|
||||
|
||||
t.afterEach(function() {
|
||||
return Promise.all([
|
||||
fs.rm('./test/testapp/example.tar', { force: true }),
|
||||
|
|
Loading…
Reference in a new issue