Lots more development, git provider finished implementing. Started on creating application
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
This commit is contained in:
parent
0948885671
commit
726ac81eb3
12 changed files with 771 additions and 37 deletions
5
core/application.mjs
Normal file
5
core/application.mjs
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default class Application extends EventEmitter {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import http from 'http'
|
||||
import https from 'https'
|
||||
import stream from 'stream/promises'
|
||||
import fs from 'fs'
|
||||
import url from 'url'
|
||||
|
||||
function resolveRelative(from, to) {
|
||||
const resolvedUrl = new URL(to, new URL(from, 'resolve://'));
|
||||
|
@ -23,7 +23,7 @@ export function request(config, path, filePath = null, redirects, fastRaw = fals
|
|||
}
|
||||
let parsed
|
||||
try {
|
||||
parsed = new url.URL(path)
|
||||
parsed = new URL(path)
|
||||
} catch {
|
||||
return Promise.reject(new Error('URL was empty or invalid'))
|
||||
}
|
||||
|
@ -43,8 +43,8 @@ export function request(config, path, filePath = null, redirects, fastRaw = fals
|
|||
'User-Agent': 'TheThing/service-core',
|
||||
Accept: 'application/vnd.github.v3+json'
|
||||
}
|
||||
if (config.githubAuthToken && path.indexOf('api.github.com') >= 0) {
|
||||
headers['Authorization'] = `token ${config.githubAuthToken}`
|
||||
if (config.token) {
|
||||
headers['Authorization'] = `token ${config.token}`
|
||||
}
|
||||
let timeout = fastRaw ? 5000 : config.timeout || 10000
|
||||
|
||||
|
@ -66,38 +66,70 @@ export function request(config, path, filePath = null, redirects, fastRaw = fals
|
|||
if (timedout) { return }
|
||||
clearTimeout(timer)
|
||||
|
||||
const ac = new AbortController()
|
||||
|
||||
let output = ''
|
||||
if (filePath) {
|
||||
let file = fs.createWriteStream(filePath)
|
||||
res.pipe(file)
|
||||
stream.pipeline(res, fs.createWriteStream(filePath), { signal: ac.signal })
|
||||
.then(function() {
|
||||
resolve({
|
||||
statusCode: res.statusCode,
|
||||
status: res.statusCode,
|
||||
statusMessage: res.statusMessage,
|
||||
headers: res.headers,
|
||||
body: null
|
||||
})
|
||||
}, function(err) {
|
||||
if (err.code === 'ABORT_ERR') return
|
||||
reject(err)
|
||||
})
|
||||
// let file = fs.createWriteStream(filePath)
|
||||
// res.pipe(file)
|
||||
} else {
|
||||
res.on('data', function(chunk) {
|
||||
output += chunk
|
||||
})
|
||||
}
|
||||
res.on('end', function() {
|
||||
let err = null
|
||||
|
||||
if (res.statusCode >= 300 && res.statusCode < 400) {
|
||||
if (newRedirects > 5) {
|
||||
return reject(new Error(`Too many redirects (last one was ${res.headers.location})`))
|
||||
err = new Error(`Too many redirects (last one was ${res.headers.location})`)
|
||||
}
|
||||
if (!res.headers.location) {
|
||||
return reject(new Error('Redirect returned no path in location header'))
|
||||
else if (!res.headers.location) {
|
||||
err = new Error('Redirect returned no path in location header')
|
||||
}
|
||||
if (res.headers.location.startsWith('http')) {
|
||||
else if (res.headers.location.startsWith('http')) {
|
||||
ac.abort()
|
||||
return resolve(request(config, res.headers.location, filePath, newRedirects, fastRaw))
|
||||
} else {
|
||||
ac.abort()
|
||||
return resolve(request(config, resolveRelative(path, res.headers.location), filePath, newRedirects, fastRaw))
|
||||
}
|
||||
} else if (res.statusCode >= 400) {
|
||||
return reject(new Error(`HTTP Error ${res.statusCode}: ${output}`))
|
||||
err = new Error(`HTTP Error ${res.statusCode}: ${output}`)
|
||||
}
|
||||
|
||||
if (err) {
|
||||
ac.abort()
|
||||
if (!filePath) return reject(err)
|
||||
|
||||
// Do some cleanup in case we were in the middle of downloading file
|
||||
return fs.rm(filePath, function() {
|
||||
reject(err)
|
||||
})
|
||||
}
|
||||
// Let the pipeline do the resolving so it can finish flusing before calling resolve
|
||||
if (!filePath) {
|
||||
resolve({
|
||||
statusCode: res.statusCode,
|
||||
status: res.statusCode,
|
||||
statusMessage: res.statusMessage,
|
||||
headers: res.headers,
|
||||
body: output
|
||||
})
|
||||
}
|
||||
resolve({
|
||||
statusCode: res.statusCode,
|
||||
status: res.statusCode,
|
||||
statusMessage: res.statusMessage,
|
||||
headers: res.headers,
|
||||
body: output
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -122,7 +154,10 @@ export function request(config, path, filePath = null, redirects, fastRaw = fals
|
|||
try {
|
||||
res.body = JSON.parse(res.body)
|
||||
} catch(e) {
|
||||
throw new Error(`Error parsing body ${res.body}: ${e.message}`)
|
||||
if (res.body.indexOf('<!DOCTYPE') < 100 || res.body.indexOf('<html') < 100) {
|
||||
return Promise.reject(new Error('Error parsing body, expected JSON but got HTML instead: ' + res.body))
|
||||
}
|
||||
return Promise.reject(new Error(`Error parsing body ${res.body}: ${e.message}`))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,15 @@ import { Low, JSONFile } from 'lowdb'
|
|||
import { type } from 'os'
|
||||
import { defaults, isObject } from './defaults.mjs'
|
||||
|
||||
export default function GetDB(util, log, filename = 'db.json') {
|
||||
export default function GetDB(config, util, log, filename = 'db.json') {
|
||||
let fullpath = util.getPathFromRoot('./' + filename)
|
||||
const adapter = new JSONFile(fullpath)
|
||||
const db = new Low(adapter)
|
||||
|
||||
db.id = 'id'
|
||||
|
||||
db.config = config
|
||||
|
||||
db.createId = function(collection) {
|
||||
if (collection.length) {
|
||||
return (collection[collection.length - 1].id || 0) + 1
|
||||
|
|
64
core/providers/git.mjs
Normal file
64
core/providers/git.mjs
Normal file
|
@ -0,0 +1,64 @@
|
|||
import { request } from '../client.mjs'
|
||||
|
||||
export default class GitProvider {
|
||||
constructor(config, requester = request) {
|
||||
this.config = config
|
||||
this.requestConfig = GitProvider.getRequestConfig(config)
|
||||
this.requester = requester
|
||||
}
|
||||
|
||||
async getLatestVersion() {
|
||||
let res = await this.requester(this.requestConfig, this.config.url)
|
||||
.catch(function(err) {
|
||||
return Promise.reject(new Error('Failed to get release information, got ' + err.message))
|
||||
})
|
||||
|
||||
if (!Array.isArray(res.body)) {
|
||||
return Promise.reject(new Error('Body was not a valid git repository release data: ' + JSON.stringify(res.body)))
|
||||
}
|
||||
|
||||
let checked = 0
|
||||
|
||||
for (let item of res.body) {
|
||||
if (!Array.isArray(item.assets)) continue
|
||||
|
||||
checked++
|
||||
|
||||
for (let asset of item.assets) {
|
||||
if (!asset.name.endsWith('-sc.7z')
|
||||
&& !asset.name.endsWith('-sc.zip')) continue
|
||||
|
||||
return {
|
||||
version: item.name,
|
||||
link: asset.browser_download_url,
|
||||
filename: asset.name,
|
||||
log: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.reject(new Error(`No valid service-core release was found, checked ${checked} releases.`))
|
||||
}
|
||||
|
||||
downloadVersion(version, target){
|
||||
return this.requester(this.requestConfig, version.link, target)
|
||||
}
|
||||
|
||||
static getRequestConfig(config) {
|
||||
if (config.token) {
|
||||
return { token: config.token }
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
async checkConfig() {
|
||||
if (!this.config.url) return Promise.reject(new Error('url was missing in git provider'))
|
||||
if (typeof(this.config.url) !== 'string') return Promise.reject(new Error('url was not a valid url'))
|
||||
try { new URL(this.config.url) }
|
||||
catch (err) { return Promise.reject(new Error('url was not a valid url: ' + err.message)) }
|
||||
|
||||
return this.getLatestVersion()
|
||||
.catch(function(err) {
|
||||
return Promise.reject(new Error(`Error fetching latest release: ${err.message}`))
|
||||
})
|
||||
}
|
||||
}
|
0
core/updater.mjs
Normal file
0
core/updater.mjs
Normal file
|
@ -16,6 +16,41 @@ export default class Util {
|
|||
return path.join(this._root_import_meta_url,'../', add)
|
||||
}
|
||||
|
||||
getExtension(filename) {
|
||||
let extension = path.extname(filename)
|
||||
if (filename.indexOf('.tar.') > 0) {
|
||||
return '.tar' + extension
|
||||
}
|
||||
return extension
|
||||
}
|
||||
|
||||
getAppNames(config) {
|
||||
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
|
||||
|
||||
out.push(key)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
get7zipExecutable() {
|
||||
const util = new Util(import.meta.url)
|
||||
if (process.platform === 'win32') {
|
||||
return util.getPathFromRoot('../bin/7za.exe')
|
||||
}
|
||||
return util.getPathFromRoot('../bin/7zas')
|
||||
}
|
||||
|
||||
verifyConfig(config) {
|
||||
if (!config.name) throw new Error('name is missing in config.json')
|
||||
if (this.getAppNames(config).length === 0) throw new Error('no application was found in config')
|
||||
if (config.debugPort != null && (typeof(config.debugPort) !== 'number' || !config.debugPort)) throw new Error('debugPort in config not a valid number')
|
||||
}
|
||||
|
||||
runCommand(command, options = [], folder = null, stream = function() {}) {
|
||||
return new Promise(function(res, rej) {
|
||||
stream(`[Command] ${folder ? folder : ''}${command} ${options.join(' ')}\n`)
|
||||
|
|
13
package.json
13
package.json
|
@ -5,7 +5,18 @@
|
|||
"main": "lib.mjs",
|
||||
"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": "eltro test/**/*.test.mjs -r dot",
|
||||
"test:watch": "npm-watch test"
|
||||
},
|
||||
"watch": {
|
||||
"test": {
|
||||
"patterns": [
|
||||
"{core,test}/*"
|
||||
],
|
||||
"extensions": "js,mjs",
|
||||
"quiet": true,
|
||||
"inherit": true
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import { Eltro as t, assert} from 'eltro'
|
||||
import fs from 'fs/promises'
|
||||
import http from 'http'
|
||||
import Util from '../core/util.mjs'
|
||||
import { request } from '../core/client.mjs'
|
||||
|
||||
const util = new Util(import.meta.url)
|
||||
const testTargetFile = util.getPathFromRoot('./filetest.html')
|
||||
const port = 61412
|
||||
const defaultHandler = function(req, res) {
|
||||
res.statusCode = 200
|
||||
|
@ -24,6 +28,11 @@ t.before(function(cb) {
|
|||
server.listen(port, cb)
|
||||
})
|
||||
|
||||
t.after(function() {
|
||||
return fs.rm(testTargetFile)
|
||||
.catch(function() { })
|
||||
})
|
||||
|
||||
t.describe('Basics', function() {
|
||||
t.beforeEach(function() {
|
||||
handler = defaultHandler
|
||||
|
@ -57,10 +66,6 @@ t.describe('Basics', function() {
|
|||
})
|
||||
|
||||
t.describe('Request', function() {
|
||||
t.beforeEach(function() {
|
||||
handler = defaultHandler
|
||||
})
|
||||
|
||||
t.test('should work normally', async function() {
|
||||
let res = await request({}, prefix)
|
||||
assert.deepEqual(res.body, {a:1})
|
||||
|
@ -86,6 +91,35 @@ t.describe('Request', function() {
|
|||
assert.strictEqual(counter, 3)
|
||||
})
|
||||
|
||||
t.test('should fail gracefully if HTML is received', async function() {
|
||||
handler = function(req, res) {
|
||||
res.statusCode = 200
|
||||
res.end(`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>`);
|
||||
}
|
||||
let err = await assert.isRejected(request({}, prefix))
|
||||
assert.match(err.message, /html/i)
|
||||
assert.notMatch(err.message, /Unexpected token/i)
|
||||
|
||||
handler = function(req, res) {
|
||||
res.statusCode = 200
|
||||
res.end(`<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>`);
|
||||
}
|
||||
err = await assert.isRejected(request({}, prefix))
|
||||
assert.match(err.message, /html/i)
|
||||
assert.notMatch(err.message, /Unexpected token/i)
|
||||
})
|
||||
|
||||
t.test('should fail if infinite redirect', async function() {
|
||||
const assertRelativeLocation = 'some/text/here'
|
||||
const assertLocation = prefix + assertRelativeLocation
|
||||
|
@ -159,6 +193,89 @@ t.describe('Request', function() {
|
|||
})
|
||||
})
|
||||
|
||||
t.describe('Request download', function() {
|
||||
const assertBody = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
t.beforeEach(function() {
|
||||
handler = defaultHandler
|
||||
})
|
||||
|
||||
t.afterEach(function() {
|
||||
return fs.rm(testTargetFile)
|
||||
.catch(function() { })
|
||||
})
|
||||
|
||||
t.test('should support downloading file successfully', async function() {
|
||||
handler = function(req, res) {
|
||||
res.statusCode = 200
|
||||
res.end(assertBody);
|
||||
}
|
||||
await request({}, prefix, testTargetFile)
|
||||
|
||||
let stat = await fs.stat(testTargetFile)
|
||||
assert.notStrictEqual(stat.size, 0)
|
||||
assert.strictEqual(stat.size, assertBody.length)
|
||||
let contents = await (await fs.readFile(testTargetFile)).toString()
|
||||
assert.strictEqual(contents, assertBody)
|
||||
})
|
||||
|
||||
t.test('should support downloading file after redirect successfully', async function() {
|
||||
let counter = 0
|
||||
handler = function(req, res) {
|
||||
if (counter < 3) {
|
||||
res.statusCode = 302
|
||||
res.setHeader('Location', encodeURI(prefix))
|
||||
res.end();
|
||||
counter++
|
||||
return
|
||||
}
|
||||
res.statusCode = 200
|
||||
res.end(assertBody);
|
||||
}
|
||||
await request({}, prefix, testTargetFile)
|
||||
|
||||
assert.strictEqual(counter, 3)
|
||||
let stat = await fs.stat(testTargetFile)
|
||||
assert.notStrictEqual(stat.size, 0)
|
||||
assert.strictEqual(stat.size, assertBody.length)
|
||||
let contents = await (await fs.readFile(testTargetFile)).toString()
|
||||
assert.strictEqual(contents, assertBody)
|
||||
})
|
||||
|
||||
t.test('should not create file if download redirects too many times', async function() {
|
||||
handler = function(req, res) {
|
||||
res.statusCode = 302
|
||||
res.setHeader('Location', encodeURI(prefix))
|
||||
res.end(assertBody);
|
||||
}
|
||||
let err = await assert.isRejected(request({}, prefix, testTargetFile))
|
||||
|
||||
assert.match(err.message, /redirects/)
|
||||
let errStat = await assert.isRejected(fs.stat(testTargetFile))
|
||||
assert.strictEqual(errStat.code, 'ENOENT')
|
||||
})
|
||||
|
||||
t.test('should not create file if download fails', async function() {
|
||||
handler = function(req, res) {
|
||||
res.statusCode = 404
|
||||
res.end('{}');
|
||||
}
|
||||
let err = await assert.isRejected(request({}, prefix, testTargetFile))
|
||||
|
||||
assert.match(err.message, /HTTP/)
|
||||
assert.match(err.message, /404/)
|
||||
|
||||
let errStat = await assert.isRejected(fs.stat(testTargetFile))
|
||||
assert.strictEqual(errStat.code, 'ENOENT')
|
||||
})
|
||||
})
|
||||
|
||||
t.after(function(cb) {
|
||||
server.close(cb)
|
||||
})
|
||||
|
|
|
@ -23,7 +23,7 @@ t.afterEach(function() {
|
|||
t.test('Should auto create file with some defaults', async function() {
|
||||
await assert.isRejected(fs.stat('./test/db_test.json'))
|
||||
|
||||
let db = await lowdb(util, logger, 'db_test.json')
|
||||
let db = await lowdb({}, util, logger, 'db_test.json')
|
||||
|
||||
let stat = await fs.stat('./test/db_test.json')
|
||||
assert.ok(stat.size > 0)
|
||||
|
@ -34,11 +34,19 @@ t.test('Should auto create file with some defaults', async function() {
|
|||
assert.notOk(db.data.core.manager)
|
||||
})
|
||||
|
||||
t.test('Should map config to config', async function() {
|
||||
const assertConfig = { a: 1 }
|
||||
|
||||
let db = await lowdb(assertConfig, util, logger, 'db_test.json')
|
||||
|
||||
assert.strictEqual(db.config, assertConfig)
|
||||
})
|
||||
|
||||
t.test('Should apply defaults to existing file', async function() {
|
||||
await assert.isRejected(fs.stat('./test/db_test.json'))
|
||||
await fs.writeFile('./test/db_test.json', '{"test":true}')
|
||||
|
||||
let db = await lowdb(util, logger, 'db_test.json')
|
||||
let db = await lowdb({}, util, logger, 'db_test.json')
|
||||
|
||||
let stat = await fs.stat('./test/db_test.json')
|
||||
assert.ok(stat.size > 0)
|
||||
|
@ -55,7 +63,7 @@ t.test('Should not fail if db is invalid', async function() {
|
|||
await assert.isRejected(fs.stat('./test/db_test.json'))
|
||||
await fs.writeFile('./test/db_test.json', '[]')
|
||||
|
||||
let db = await lowdb(util, logger, 'db_test.json')
|
||||
let db = await lowdb({}, util, logger, 'db_test.json')
|
||||
|
||||
let stat = await fs.stat('./test/db_test.json')
|
||||
assert.ok(stat.size > 0)
|
||||
|
@ -72,7 +80,7 @@ t.test('Should not fail if db is invalid', async function() {
|
|||
t.test('Should support adding an application with defaults', async function() {
|
||||
await assert.isRejected(fs.stat('./test/db_test.json'))
|
||||
|
||||
let db = await lowdb(util, logger, 'db_test.json')
|
||||
let db = await lowdb({}, util, logger, 'db_test.json')
|
||||
|
||||
let stat = await fs.stat('./test/db_test.json')
|
||||
assert.ok(stat.size > 0)
|
||||
|
@ -105,7 +113,7 @@ t.test('Should support reading from db', async function() {
|
|||
const assertValue = { a: 1 }
|
||||
await assert.isRejected(fs.stat('./test/db_test.json'))
|
||||
|
||||
let db = await lowdb(util, logger, 'db_test.json')
|
||||
let db = await lowdb({}, util, logger, 'db_test.json')
|
||||
|
||||
|
||||
db.data.test = assertValue
|
||||
|
@ -113,20 +121,20 @@ t.test('Should support reading from db', async function() {
|
|||
|
||||
assert.strictEqual(db.data.test, assertValue)
|
||||
|
||||
let dbSecondary = await lowdb(util, logger, 'db_test.json')
|
||||
let dbSecondary = await lowdb({}, util, logger, 'db_test.json')
|
||||
assert.notStrictEqual(dbSecondary.data.test, assertValue)
|
||||
assert.deepStrictEqual(dbSecondary.data.test, assertValue)
|
||||
})
|
||||
|
||||
t.test('should throw if unable to write to file', function() {
|
||||
return assert.isRejected(lowdb(util, logger, '../test'))
|
||||
return assert.isRejected(lowdb({}, util, logger, '../test'))
|
||||
})
|
||||
|
||||
t.test('should have basic database-like functions defined', async function() {
|
||||
const assertItem1 = { a: 1 }
|
||||
const assertItem2 = { a: 2 }
|
||||
const assertItem3 = { a: 3 }
|
||||
let db = await lowdb(util, logger, 'db_test.json')
|
||||
let db = await lowdb({}, util, logger, 'db_test.json')
|
||||
assert.strictEqual(db.id, 'id')
|
||||
|
||||
db.data.myarr = []
|
||||
|
@ -144,7 +152,7 @@ t.test('should have basic database-like functions defined', async function() {
|
|||
|
||||
await db.write()
|
||||
|
||||
let dbSec = await lowdb(util, logger, 'db_test.json')
|
||||
let dbSec = await lowdb({}, util, logger, 'db_test.json')
|
||||
|
||||
assert.strictEqual(dbSec.data.myarr.length, 2)
|
||||
assert.notStrictEqual(dbSec.get(dbSec.data.myarr, assertItem1.id), assertItem1)
|
||||
|
@ -185,7 +193,7 @@ t.test('should have basic database-like functions with string-like name of colle
|
|||
const assertItem1 = { a: 1 }
|
||||
const assertItem2 = { a: 2 }
|
||||
const assertItem3 = { a: 3 }
|
||||
let db = await lowdb(util, logger, 'db_test.json')
|
||||
let db = await lowdb({}, util, logger, 'db_test.json')
|
||||
assert.strictEqual(db.id, 'id')
|
||||
|
||||
db.data.myarr = []
|
||||
|
@ -203,7 +211,7 @@ t.test('should have basic database-like functions with string-like name of colle
|
|||
|
||||
await db.write()
|
||||
|
||||
let dbSec = await lowdb(util, logger, 'db_test.json')
|
||||
let dbSec = await lowdb({}, util, logger, 'db_test.json')
|
||||
|
||||
assert.strictEqual(dbSec.data.myarr.length, 2)
|
||||
assert.notStrictEqual(dbSec.get('myarr', assertItem1.id), assertItem1)
|
||||
|
|
97
test/providers/git.integration.test.mjs
Normal file
97
test/providers/git.integration.test.mjs
Normal file
|
@ -0,0 +1,97 @@
|
|||
import { Eltro as t, assert, stub } from 'eltro'
|
||||
import Util from '../../core/util.mjs'
|
||||
import fs from 'fs/promises'
|
||||
import GitProvider from '../../core/providers/git.mjs'
|
||||
|
||||
t.timeout(5000).describe('Git integration', function() {
|
||||
t.after(function() {
|
||||
return fs.rm('./test/providers/file.7z')
|
||||
.catch(function() { })
|
||||
})
|
||||
|
||||
t.describe('#getLatestVersion()', function() {
|
||||
t.test('should return latest version in a valid repository', async function() {
|
||||
let provider = new GitProvider({ url: 'https://git.nfp.is/api/v1/repos/thething/sc-helloworld/releases' })
|
||||
let version = await provider.getLatestVersion()
|
||||
assert.ok(version)
|
||||
assert.ok(version.version)
|
||||
assert.ok(version.link)
|
||||
assert.match(version.link, /\/attachments\//)
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('#checkConfig()', function() {
|
||||
t.test('should fail if link does not return json repository object', async function() {
|
||||
let err = await assert.isRejected(new GitProvider({ url: 'http://git.nfp.is/api/v1/repos/thething/ProgramQueuer' }).checkConfig())
|
||||
assert.match(err.message, /valid/i)
|
||||
assert.match(err.message, /repository/i)
|
||||
err = await assert.isRejected(new GitProvider({ url: 'http://git.nfp.is/api/v1/orgs/nfp/repos' }).checkConfig())
|
||||
assert.match(err.message, /service-core/i)
|
||||
assert.match(err.message, /release/i)
|
||||
})
|
||||
|
||||
t.test('should fail if link returns no active release repository with assets', async function() {
|
||||
let err = await assert.isRejected(new GitProvider({ url: 'https://git.nfp.is/api/v1/repos/thething/eltro/releases' }).checkConfig())
|
||||
assert.match(err.message, /service-core/i)
|
||||
assert.match(err.message, /release/i)
|
||||
})
|
||||
|
||||
t.test('should fail on private repositories if token missing', async function() {
|
||||
let err = await assert.isRejected(new GitProvider({ url: 'https://git.nfp.is/api/v1/repos/TheThing/privateexample/releases' }).checkConfig())
|
||||
assert.match(err.message, /fail/i)
|
||||
assert.match(err.message, /404/i)
|
||||
assert.match(err.message, /release/i)
|
||||
})
|
||||
|
||||
t.test('should otherwise succeed', function() {
|
||||
return new GitProvider({ url: 'https://git.nfp.is/api/v1/repos/TheThing/sc-manager/releases' })
|
||||
.checkConfig()
|
||||
})
|
||||
|
||||
let test = t
|
||||
if (!process.env.gittesttoken) {
|
||||
test = test.skip()
|
||||
}
|
||||
test.test('should succeed on private repo if token is provided', function() {
|
||||
return new GitProvider({
|
||||
token: process.env.gittesttoken.trim(),
|
||||
url: 'https://git.nfp.is/api/v1/repos/TheThing/privateexample/releases',
|
||||
}).checkConfig()
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('#downloadVersion()', function() {
|
||||
const util = new Util(import.meta.url)
|
||||
|
||||
let test = t
|
||||
if (!process.env.gittesttoken) {
|
||||
test = test.skip()
|
||||
}
|
||||
test.test('should successfully download release', async function() {
|
||||
let provider = new GitProvider({
|
||||
token: process.env.gittesttoken.trim(),
|
||||
url: 'https://git.nfp.is/api/v1/repos/TheThing/privateexample/releases',
|
||||
})
|
||||
await provider.checkConfig()
|
||||
let version = await provider.getLatestVersion()
|
||||
assert.ok(version.version)
|
||||
assert.ok(version.filename)
|
||||
assert.ok(version.link)
|
||||
|
||||
let path = util.getPathFromRoot('./file.7z')
|
||||
await provider.downloadVersion(version, path)
|
||||
|
||||
let stat = await fs.stat(path)
|
||||
assert.ok(stat.size > 0)
|
||||
stat = await fs.stat('./test/providers/file.7z')
|
||||
assert.ok(stat.size > 0)
|
||||
|
||||
let output = ''
|
||||
await util.runCommand(util.get7zipExecutable(), ['l', 'file.7z'], util.getPathFromRoot('./'), function(chunk) {
|
||||
output += chunk + '\n'
|
||||
})
|
||||
assert.ok(output.indexOf('file1.txt'))
|
||||
assert.ok(output.indexOf('file2.txt'))
|
||||
})
|
||||
})
|
||||
})
|
210
test/providers/git.test.mjs
Normal file
210
test/providers/git.test.mjs
Normal file
|
@ -0,0 +1,210 @@
|
|||
import { Eltro as t, assert, stub } from 'eltro'
|
||||
import GitProvider from '../../core/providers/git.mjs'
|
||||
|
||||
t.describe('#getLatestVersion()', function() {
|
||||
t.test('should return correct name and link in result', async function() {
|
||||
const assertName = 'Karen'
|
||||
const assertLink = 'Over The Future'
|
||||
const assertFilename = 'test-sc.7z'
|
||||
let stubber = stub()
|
||||
let provider = new GitProvider({}, stubber)
|
||||
|
||||
stubber.resolves({ body: [
|
||||
{ name: assertName, assets: [{ name: assertFilename, browser_download_url: assertLink }] },
|
||||
]})
|
||||
|
||||
let version = await provider.getLatestVersion()
|
||||
assert.strictEqual(version.version, assertName)
|
||||
assert.strictEqual(version.link, assertLink)
|
||||
assert.strictEqual(version.filename, assertFilename)
|
||||
assert.strictEqual(version.log, '')
|
||||
})
|
||||
|
||||
t.test('should support multiple extension', async function() {
|
||||
const assertName = 'Karen'
|
||||
const assertLink = 'My Wings'
|
||||
const assertFilename = 'test-sc.zip'
|
||||
let stubber = stub()
|
||||
let provider = new GitProvider({}, stubber)
|
||||
|
||||
stubber.resolves({ body: [
|
||||
{ name: assertName, assets: [{ name: assertFilename, browser_download_url: assertLink }] },
|
||||
]})
|
||||
|
||||
let version = await provider.getLatestVersion()
|
||||
assert.strictEqual(version.version, assertName)
|
||||
assert.strictEqual(version.link, assertLink)
|
||||
assert.strictEqual(version.filename, assertFilename)
|
||||
assert.strictEqual(version.log, '')
|
||||
})
|
||||
|
||||
t.test('should skip versions with no assets', async function() {
|
||||
const assertName = 'name1'
|
||||
const assertLink = 'somelink'
|
||||
const assertFilename = 'something-sc.7z'
|
||||
let stubber = stub()
|
||||
let provider = new GitProvider({}, stubber)
|
||||
|
||||
stubber.resolves({ body: [
|
||||
{ name: 'test', assets: [] },
|
||||
{ name: assertName, assets: [{ name: assertFilename, browser_download_url: assertLink }] },
|
||||
]})
|
||||
|
||||
let version = await provider.getLatestVersion()
|
||||
assert.strictEqual(version.version, assertName)
|
||||
assert.strictEqual(version.link, assertLink)
|
||||
assert.strictEqual(version.filename, assertFilename)
|
||||
})
|
||||
|
||||
t.test('should skip versions with non-sc filename', async function() {
|
||||
const assertName = 'name2'
|
||||
const assertLink = 'somelink2'
|
||||
const assertFilename = 'something-sc.7z'
|
||||
let stubber = stub()
|
||||
let provider = new GitProvider({}, stubber)
|
||||
|
||||
stubber.resolves({ body: [
|
||||
{ name: 'test', assets: [{ name: 'nope.7z', browser_download_url: 'nope' }] },
|
||||
{ name: assertName, assets: [{ name: assertFilename, browser_download_url: assertLink }] },
|
||||
]})
|
||||
|
||||
let version = await provider.getLatestVersion()
|
||||
assert.strictEqual(version.version, assertName)
|
||||
assert.strictEqual(version.link, assertLink)
|
||||
assert.strictEqual(version.filename, assertFilename)
|
||||
})
|
||||
|
||||
t.test('should skip assets with non-sc filename', async function() {
|
||||
const assertName = 'name3'
|
||||
const assertLink = 'somelink3'
|
||||
const assertFilename = 'something-sc.7z'
|
||||
let stubber = stub()
|
||||
let provider = new GitProvider({}, stubber)
|
||||
|
||||
stubber.resolves({ body: [
|
||||
{ name: 'test', assets: [{ name: 'nope.7z', browser_download_url: 'nope' }] },
|
||||
{ name: assertName, assets: [
|
||||
{ name: 'nope.7z', browser_download_url: 'nope' },
|
||||
{ name: assertFilename, browser_download_url: assertLink },
|
||||
] },
|
||||
]})
|
||||
|
||||
let version = await provider.getLatestVersion()
|
||||
assert.strictEqual(version.version, assertName)
|
||||
assert.strictEqual(version.link, assertLink)
|
||||
assert.strictEqual(version.filename, assertFilename)
|
||||
})
|
||||
|
||||
t.test('should otherwise reject', async function() {
|
||||
let stubber = stub()
|
||||
let provider = new GitProvider({}, stubber)
|
||||
|
||||
stubber.resolves({ body: [
|
||||
{ name: 'test', assets: [{ name: 'nope.7z', browser_download_url: 'nope' }] },
|
||||
{ name: 'test2', assets: [{ name: 'nope2.7z', browser_download_url: 'nope' },] },
|
||||
{ name: 'test3', assets: [{ name: 'nope2.7z', browser_download_url: 'nope' },] },
|
||||
]})
|
||||
|
||||
let err = await assert.isRejected(provider.getLatestVersion())
|
||||
assert.match(err.message, /release/)
|
||||
assert.match(err.message, /found/)
|
||||
})
|
||||
|
||||
t.test('should reject if not valid body', async function() {
|
||||
let provider = new GitProvider({}, stub())
|
||||
|
||||
provider.requester.resolves({ body: { } })
|
||||
|
||||
let err = await assert.isRejected(provider.getLatestVersion())
|
||||
assert.match(err.message, /valid/)
|
||||
assert.match(err.message, /repository/)
|
||||
assert.match(err.message, /release/)
|
||||
})
|
||||
|
||||
t.test('should wrap rejected errors', async function() {
|
||||
const assertError = new Error('Oosegoto')
|
||||
let provider = new GitProvider({}, stub())
|
||||
|
||||
provider.requester.rejects(assertError)
|
||||
|
||||
let err = await assert.isRejected(provider.getLatestVersion())
|
||||
assert.notStrictEqual(err, assertError)
|
||||
assert.match(err.message, /failed/i)
|
||||
assert.match(err.message, /release/i)
|
||||
assert.match(err.message, new RegExp(assertError.message))
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('#downloadVersion()', function() {
|
||||
t.test('should call requester with correct url and info', async function() {
|
||||
const assertError = new Error('Moeyuru Monmon Mousou Zou')
|
||||
const assertConfig = { a: 1 }
|
||||
const assertUrl = 'http://test'
|
||||
const assertTarget = '/some/path/here'
|
||||
|
||||
let provider = new GitProvider({}, stub())
|
||||
provider.requestConfig = assertConfig
|
||||
provider.requester.rejects(assertError)
|
||||
|
||||
let err = await assert.isRejected(provider.downloadVersion({
|
||||
link: assertUrl,
|
||||
}, assertTarget))
|
||||
assert.strictEqual(err, assertError)
|
||||
assert.strictEqual(provider.requester.firstCall[0], assertConfig)
|
||||
assert.strictEqual(provider.requester.firstCall[1], assertUrl)
|
||||
assert.strictEqual(provider.requester.firstCall[2], assertTarget)
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('#getRequestConfig()', function() {
|
||||
t.test('should default return empty object', function() {
|
||||
assert.deepStrictEqual(GitProvider.getRequestConfig({}), {})
|
||||
assert.deepStrictEqual(GitProvider.getRequestConfig({ asdf: '1234' }), {})
|
||||
assert.deepStrictEqual(GitProvider.getRequestConfig({ url: 'http://test' }), {})
|
||||
assert.deepStrictEqual(GitProvider.getRequestConfig({ a: 1 }), {})
|
||||
})
|
||||
|
||||
t.test('should return token if token is defined', function() {
|
||||
const assertToken = 'asdf1234'
|
||||
assert.deepStrictEqual(GitProvider.getRequestConfig({ token: assertToken }), { token: assertToken })
|
||||
assert.deepStrictEqual(GitProvider.getRequestConfig({ token: assertToken, asdf: '1234' }), { token: assertToken })
|
||||
assert.deepStrictEqual(GitProvider.getRequestConfig({ token: assertToken, url: 'http://test' }), { token: assertToken })
|
||||
assert.deepStrictEqual(GitProvider.getRequestConfig({ token: assertToken, a: 1 }), { token: assertToken })
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('#checkConfig()', function() {
|
||||
t.test('should fail if missing url in config', async function() {
|
||||
let err = await assert.isRejected(new GitProvider({}).checkConfig())
|
||||
assert.match(err.message, /url/i)
|
||||
assert.match(err.message, /missing/i)
|
||||
})
|
||||
|
||||
t.test('should fail if url is something else than an url', async function() {
|
||||
let tests = [[], {}, 1234, 'asdf', 'http://']
|
||||
|
||||
let errors = await Promise.all(tests.map(function(check) {
|
||||
return assert.isRejected(new GitProvider({ url: check }).checkConfig(), `Promise fulfilled with url = ${JSON.stringify(check)}`)
|
||||
}))
|
||||
for (let err of errors) {
|
||||
assert.match(err.message, /url/i)
|
||||
assert.match(err.message, /valid/i)
|
||||
}
|
||||
})
|
||||
|
||||
t.test('should call requester with correct config and url', async function() {
|
||||
const assertError = new Error('Toki wo Koeta Yoru')
|
||||
const assertRequestConfig = { a: 1 }
|
||||
const assertUrl = 'http://test'
|
||||
|
||||
let provider = new GitProvider({ url: assertUrl }, stub())
|
||||
provider.requestConfig = assertRequestConfig
|
||||
provider.requester.rejects(assertError)
|
||||
|
||||
let err = await assert.isRejected(provider.checkConfig())
|
||||
assert.notStrictEqual(err, assertError)
|
||||
assert.match(err.message, new RegExp(assertError.message))
|
||||
assert.strict(provider.requester.firstCall[0], assertRequestConfig)
|
||||
assert.strict(provider.requester.firstCall[1], assertUrl)
|
||||
})
|
||||
})
|
|
@ -31,6 +31,156 @@ t.describe('#getUrlFromRoot()', function() {
|
|||
var util = new Util(import.meta.url)
|
||||
|
||||
let data = await import(util.getUrlFromRoot('template.mjs'))
|
||||
assert.deepEqual(data.default, { a: 1 })
|
||||
assert.deepStrictEqual(data.default, { a: 1 })
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('#get7zipExecutable()', function() {
|
||||
var util = new Util(import.meta.url)
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
console.log('Adding 7zip windows exe path test')
|
||||
|
||||
t.test('should return windows executable path', function() {
|
||||
assert.ok(util.get7zipExecutable().endsWith('\\service-core\\bin\\7za.exe'), `${util.get7zipExecutable()} should end with 7za.exe`)
|
||||
})
|
||||
} else {
|
||||
console.log('Adding 7zip linux exe path test')
|
||||
|
||||
t.test('should return linux executable path', function() {
|
||||
assert.ok(util.get7zipExecutable().endsWith('/service-core/bin/7zas'), `${util.get7zipExecutable()} should end with 7zas`)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.describe('#getExtension()', function() {
|
||||
var util = new Util(import.meta.url)
|
||||
|
||||
t.test('should return correct extension on basic extension types', function() {
|
||||
assert.strictEqual(util.getExtension('file.7z'), '.7z')
|
||||
assert.strictEqual(util.getExtension('file.zip'), '.zip')
|
||||
assert.strictEqual(util.getExtension('file.tar'), '.tar')
|
||||
assert.strictEqual(util.getExtension('file.doc'), '.doc')
|
||||
assert.strictEqual(util.getExtension('file.rar'), '.rar')
|
||||
assert.strictEqual(util.getExtension('file.test'), '.test')
|
||||
})
|
||||
|
||||
t.test('should support that annoying tar extension', function() {
|
||||
assert.strictEqual(util.getExtension('file.tar.test'), '.tar.test')
|
||||
assert.strictEqual(util.getExtension('file.tar.gz'), '.tar.gz')
|
||||
assert.strictEqual(util.getExtension('file.tar.xz'), '.tar.xz')
|
||||
assert.strictEqual(util.getExtension('file.tar.bz2'), '.tar.bz2')
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('#getApplications()', function() {
|
||||
var util = new Util(import.meta.url)
|
||||
|
||||
t.test('should fail to find if not a valid object', function() {
|
||||
assert.deepStrictEqual(util.getAppNames({ app: [] }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: 1234 }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: '124124' }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: '' }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: {} }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: null }), [])
|
||||
})
|
||||
|
||||
t.test('should return the name of the key if an object with port and provider is found', function() {
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { port: 1234, provider: 'asdf' } }), ['app'])
|
||||
})
|
||||
|
||||
t.test('should fail to find if port is missing or port not a number', function() {
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: null } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 'asdf' } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: '1234' } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: 0 } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: [] } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 'asdf', port: {} } }), [])
|
||||
})
|
||||
|
||||
t.test('should fail to find if provider is missing or not a string', function() {
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { port: 1234 } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: '', port: 1234 } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: null, port: 1234 } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: [], port: 1234 } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: {}, port: 1234 } }), [])
|
||||
assert.deepStrictEqual(util.getAppNames({ app: { provider: 1234, port: 1234 } }), [])
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('#verifyConfig()', function() {
|
||||
var util = new Util(import.meta.url)
|
||||
let input
|
||||
|
||||
t.beforeEach(function() {
|
||||
input = {
|
||||
name: 'test',
|
||||
title: 'Test',
|
||||
description: 'Some description',
|
||||
app: { port: 1234, provider: 'asdf' },
|
||||
}
|
||||
})
|
||||
|
||||
let testMissing = ['name']
|
||||
|
||||
testMissing.forEach(function(check) {
|
||||
t.test(`should fail on missing ${check}`, function() {
|
||||
delete input[check]
|
||||
|
||||
assert.throws(function() {
|
||||
util.verifyConfig(input)
|
||||
}, function(err) {
|
||||
assert.match(err.message, new RegExp(check))
|
||||
return true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
let testOptional = ['title', 'description']
|
||||
testOptional.forEach(function(check) {
|
||||
t.test(`should succeed even if ${check} is missing`, function() {
|
||||
delete input[check]
|
||||
util.verifyConfig(input)
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should succeed if debug port is specified or null', function() {
|
||||
input.debugPort = 1234
|
||||
util.verifyConfig(input)
|
||||
input.debugPort = null
|
||||
util.verifyConfig(input)
|
||||
})
|
||||
|
||||
t.test('should fail if debug port is invalid', function() {
|
||||
let checks = [
|
||||
[],
|
||||
{},
|
||||
0,
|
||||
'asdf',
|
||||
'1234'
|
||||
]
|
||||
checks.forEach(function(check) {
|
||||
input.debugPort = check
|
||||
|
||||
assert.throws(function() {
|
||||
util.verifyConfig(input)
|
||||
}, function(err) {
|
||||
assert.match(err.message, /debugPort/)
|
||||
assert.match(err.message, /number/)
|
||||
return true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should fail if no objects in config', function() {
|
||||
delete input.app
|
||||
assert.throws(function() {
|
||||
util.verifyConfig(input)
|
||||
}, function(err) {
|
||||
assert.match(err.message, /no/)
|
||||
assert.match(err.message, /app/)
|
||||
return true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue