From 8da4eaf4a30ff5e6c61e94309a0411859e2945c4 Mon Sep 17 00:00:00 2001 From: Girish Ramakrishnan Date: Thu, 3 Jun 2021 12:20:44 -0700 Subject: [PATCH] fix tests --- runTests | 2 +- src/apphealthmonitor.js | 3 + src/apptask.js | 2 - src/backupdb.js | 7 +- src/backups.js | 2 +- src/reverseproxy.js | 4 +- src/scheduler.js | 4 +- src/settings.js | 2 +- src/system.js | 8 +- src/test/accesscontrol-test.js | 20 -- src/test/acme2-test.js | 20 +- src/test/appstore-test.js | 42 +---- src/test/apptask-test.js | 123 +------------ src/test/backups-test.js | 244 +++++++++++-------------- src/test/common.js | 132 ++++++++++++- src/test/database-test.js | 6 +- src/test/dns-test.js | 40 +--- src/test/dockerproxy-test.js | 2 +- src/test/eventlog-test.js | 29 ++- src/test/network-test.js | 27 +-- src/test/notifications-test.js | 29 +-- src/test/reverseproxy-test.js | 75 +++----- src/test/system-test.js | 21 +-- src/test/tasks-test.js | 21 +-- src/test/translation-test.js | 6 +- src/test/updatechecker-test.js | 325 +++++++++++---------------------- src/updatechecker.js | 17 +- 27 files changed, 474 insertions(+), 739 deletions(-) delete mode 100644 src/test/accesscontrol-test.js diff --git a/runTests b/runTests index 8f12d2556..6dedb5ed0 100755 --- a/runTests +++ b/runTests @@ -23,7 +23,7 @@ mkdir -p ${DATA_DIR} cd ${DATA_DIR} mkdir -p appsdata mkdir -p boxdata/mail boxdata/certs boxdata/mail/dkim/localhost boxdata/mail/dkim/foobar.com -mkdir -p platformdata/addons/mail/banner platformdata/nginx/cert platformdata/nginx/applications platformdata/collectd/collectd.conf.d platformdata/addons platformdata/logrotate.d platformdata/backup platformdata/logs/tasks platformdata/sftp/ssh platformdata/firewall +mkdir -p platformdata/addons/mail/banner platformdata/nginx/cert platformdata/nginx/applications platformdata/collectd/collectd.conf.d platformdata/addons platformdata/logrotate.d platformdata/backup platformdata/logs/tasks platformdata/sftp/ssh platformdata/firewall platformdata/update sudo mkdir -p /mnt/cloudron-test-music /media/cloudron-test-music # volume test # translations diff --git a/src/apphealthmonitor.js b/src/apphealthmonitor.js index dd937bbef..a50783d05 100644 --- a/src/apphealthmonitor.js +++ b/src/apphealthmonitor.js @@ -6,6 +6,7 @@ const appdb = require('./appdb.js'), async = require('async'), auditSource = require('./auditsource.js'), BoxError = require('./boxerror.js'), + constants = require('./constants.js'), debug = require('debug')('box:apphealthmonitor'), docker = require('./docker.js'), eventlog = require('./eventlog.js'), @@ -187,6 +188,8 @@ function run(intervalSecs, callback) { assert.strictEqual(typeof intervalSecs, 'number'); assert.strictEqual(typeof callback, 'function'); + if (constants.TEST) return; + if (!gStartTime) gStartTime = new Date(); async.series([ diff --git a/src/apptask.js b/src/apptask.js index 2bbf38d6f..7aa1ffcd6 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -6,8 +6,6 @@ exports = module.exports = { run, // exported for testing - _configureReverseProxy: configureReverseProxy, - _unconfigureReverseProxy: unconfigureReverseProxy, _createAppDir: createAppDir, _deleteAppDir: deleteAppDir, _verifyManifest: verifyManifest, diff --git a/src/backupdb.js b/src/backupdb.js index e012a4767..878122a5d 100644 --- a/src/backupdb.js +++ b/src/backupdb.js @@ -1,12 +1,11 @@ 'use strict'; -var assert = require('assert'), +const assert = require('assert'), BoxError = require('./boxerror.js'), database = require('./database.js'), - safe = require('safetydance'), - util = require('util'); + safe = require('safetydance'); -var BACKUPS_FIELDS = [ 'id', 'identifier', 'creationTime', 'packageVersion', 'type', 'dependsOn', 'state', 'manifestJson', 'format', 'preserveSecs', 'encryptionVersion' ]; +const BACKUPS_FIELDS = [ 'id', 'identifier', 'creationTime', 'packageVersion', 'type', 'dependsOn', 'state', 'manifestJson', 'format', 'preserveSecs', 'encryptionVersion' ]; exports = module.exports = { add, diff --git a/src/backups.js b/src/backups.js index 74bedfc09..55082acce 100644 --- a/src/backups.js +++ b/src/backups.js @@ -146,7 +146,7 @@ function testConfig(backupConfig, callback) { assert.strictEqual(typeof backupConfig, 'object'); assert.strictEqual(typeof callback, 'function'); - var func = api(backupConfig.provider); + const func = api(backupConfig.provider); if (!func) return callback(new BoxError(BoxError.BAD_FIELD, 'unknown storage provider', { field: 'provider' })); if (backupConfig.format !== 'tgz' && backupConfig.format !== 'rsync') return callback(new BoxError(BoxError.BAD_FIELD, 'unknown format', { field: 'format' })); diff --git a/src/reverseproxy.js b/src/reverseproxy.js index 591fce36e..9b24ecf88 100644 --- a/src/reverseproxy.js +++ b/src/reverseproxy.js @@ -185,12 +185,12 @@ function validateCertificate(location, domainObject, certificate) { } function reload(callback) { - if (constants.TEST) return callback(); + if (constants.TEST) return callback(null); shell.sudo('reload', [ RESTART_SERVICE_CMD, 'nginx' ], {}, function (error) { if (error) return callback(new BoxError(BoxError.NGINX_ERROR, `Error reloading nginx: ${error.message}`)); - callback(); + callback(null); }); } diff --git a/src/scheduler.js b/src/scheduler.js index 290e294e9..9b164ef42 100644 --- a/src/scheduler.js +++ b/src/scheduler.js @@ -6,7 +6,7 @@ exports = module.exports = { resumeJobs }; -let apps = require('./apps.js'), +const apps = require('./apps.js'), assert = require('assert'), async = require('async'), BoxError = require('./boxerror.js'), @@ -119,6 +119,8 @@ function stopJobs(appId, appState, callback) { } function sync() { + if (constants.TEST) return; + apps.getAll(function (error, allApps) { if (error) return debug(`sync: error getting app list. ${error.message}`); diff --git a/src/settings.js b/src/settings.js index 391caabb2..fd57bcd56 100644 --- a/src/settings.js +++ b/src/settings.js @@ -101,7 +101,7 @@ exports = module.exports = { LICENSE_KEY: 'license_key', LANGUAGE_KEY: 'language', CLOUDRON_ID_KEY: 'cloudron_id', - CLOUDRON_TOKEN_KEY: 'cloudron_token', + CLOUDRON_TOKEN_KEY: 'cloudron_token', // apstore token FIREWALL_BLOCKLIST_KEY: 'firewall_blocklist', API_SERVER_ORIGIN_KEY: 'api_server_origin', diff --git a/src/system.js b/src/system.js index b89eb5df6..f40c1c99b 100644 --- a/src/system.js +++ b/src/system.js @@ -123,13 +123,13 @@ function checkDiskSpace(callback) { debug('Checking disk space'); - getDisks(function (error, disks) { + getDisks(async function (error, disks) { if (error) { debug('checkDiskSpace: error getting disks %s', error.message); return callback(); } - var oos = disks.disks.some(function (entry) { + const oos = disks.disks.some(function (entry) { // ignore other filesystems but where box, app and platform data is if (entry.filesystem !== disks.boxDataDisk && entry.filesystem !== disks.platformDataDisk @@ -142,7 +142,9 @@ function checkDiskSpace(callback) { debug('checkDiskSpace: disk space checked. ok: %s', !oos); - notifications.alert(notifications.ALERT_DISK_SPACE, 'Server is running out of disk space', oos ? JSON.stringify(disks.disks, null, 4) : '', callback); + await notifications.alert(notifications.ALERT_DISK_SPACE, 'Server is running out of disk space', oos ? JSON.stringify(disks.disks, null, 4) : ''); + + callback(); }); } diff --git a/src/test/accesscontrol-test.js b/src/test/accesscontrol-test.js deleted file mode 100644 index 6276ae553..000000000 --- a/src/test/accesscontrol-test.js +++ /dev/null @@ -1,20 +0,0 @@ -/* jslint node:true */ -/* global it:false */ -/* global describe:false */ -/* global before:false */ -/* global after:false */ - -'use strict'; - -var accesscontrol = require('../accesscontrol.js'), - expect = require('expect.js'); - -describe('access control', function () { - describe('verifyToken', function () { - // FIXME - }); - - describe('hasRole', function () { - // FIXME - }); -}); diff --git a/src/test/acme2-test.js b/src/test/acme2-test.js index 4a736571c..f6965221b 100644 --- a/src/test/acme2-test.js +++ b/src/test/acme2-test.js @@ -5,25 +5,13 @@ 'use strict'; -var async = require('async'), - database = require('../database.js'), - acme2 = require('../acme2.js'), +const acme2 = require('../acme2.js'), + common = require('./common.js'), expect = require('expect.js'); describe('Acme2', function () { - before(function (done) { - async.series([ - database.initialize, - database._clear - ], done); - }); - - after(function (done) { - async.series([ - database._clear, - database.uninitialize - ], done); - }); + before(common.setup); + after(common.cleanup); describe('getChallengeSubdomain', function () { it('non-wildcard', function () { diff --git a/src/test/appstore-test.js b/src/test/appstore-test.js index c7e11451a..edc452ef5 100644 --- a/src/test/appstore-test.js +++ b/src/test/appstore-test.js @@ -7,44 +7,20 @@ 'use strict'; -var async = require('async'), - appstore = require('../appstore.js'), - database = require('../database.js'), +const appstore = require('../appstore.js'), + common = require('./common.js'), expect = require('expect.js'), nock = require('nock'), settings = require('../settings.js'), settingsdb = require('../settingsdb.js'); -const DASHBOARD_DOMAIN = 'appstore-test.example.com'; const APPSTORE_TOKEN = 'appstoretoken'; const APP_ID = 'appid'; const APPSTORE_APP_ID = 'appstoreappid'; -const MOCK_API_SERVER_ORIGIN = 'http://localhost:6060'; - -function setup(done) { - nock.cleanAll(); - - async.series([ - database.initialize, - database._clear, - settings._setApiServerOrigin.bind(null, MOCK_API_SERVER_ORIGIN), - settings.setDashboardLocation.bind(null, DASHBOARD_DOMAIN, 'my.' + DASHBOARD_DOMAIN), - settings.initCache - ], done); -} - -function cleanup(done) { - nock.cleanAll(); - - async.series([ - database._clear, - database.uninitialize - ], done); -} describe('Appstore', function () { - before(setup); - after(cleanup); + before(common.setup); + after(common.cleanup); beforeEach(nock.cleanAll); @@ -53,7 +29,7 @@ describe('Appstore', function () { }); it('can purchase an app', function (done) { - var scope1 = nock(MOCK_API_SERVER_ORIGIN) + var scope1 = nock(common.MOCK_API_SERVER_ORIGIN) .post(`/api/v1/cloudronapps?accessToken=${APPSTORE_TOKEN}`, function () { return true; }) .reply(201, {}); @@ -66,11 +42,11 @@ describe('Appstore', function () { }); it('unpurchase succeeds if app was never purchased', function (done) { - var scope1 = nock(MOCK_API_SERVER_ORIGIN) + var scope1 = nock(common.MOCK_API_SERVER_ORIGIN) .get(`/api/v1/cloudronapps/${APP_ID}?accessToken=${APPSTORE_TOKEN}`) .reply(404, {}); - var scope2 = nock(MOCK_API_SERVER_ORIGIN) + var scope2 = nock(common.MOCK_API_SERVER_ORIGIN) .delete(`/api/v1/cloudronapps/${APP_ID}?accessToken=${APPSTORE_TOKEN}`, function () { return true; }) .reply(204, {}); @@ -84,11 +60,11 @@ describe('Appstore', function () { }); it('can unpurchase an app', function (done) { - var scope1 = nock(MOCK_API_SERVER_ORIGIN) + var scope1 = nock(common.MOCK_API_SERVER_ORIGIN) .get(`/api/v1/cloudronapps/${APP_ID}?accessToken=${APPSTORE_TOKEN}`) .reply(200, {}); - var scope2 = nock(MOCK_API_SERVER_ORIGIN) + var scope2 = nock(common.MOCK_API_SERVER_ORIGIN) .delete(`/api/v1/cloudronapps/${APP_ID}?accessToken=${APPSTORE_TOKEN}`, function () { return true; }) .reply(204, {}); diff --git a/src/test/apptask-test.js b/src/test/apptask-test.js index 558b58a4d..79db9997f 100644 --- a/src/test/apptask-test.js +++ b/src/test/apptask-test.js @@ -5,131 +5,18 @@ 'use strict'; -var appdb = require('../appdb.js'), - apps = require('../apps.js'), - apptask = require('../apptask.js'), - async = require('async'), - blobs = require('../blobs.js'), - database = require('../database.js'), - domains = require('../domains.js'), +const apptask = require('../apptask.js'), + common = require('./common.js'), expect = require('expect.js'), fs = require('fs'), paths = require('../paths.js'), - settings = require('../settings.js'), - userdb = require('../userdb.js'), _ = require('underscore'); -var MANIFEST = { - 'id': 'io.cloudron.test', - 'author': 'The Presidents Of the United States Of America', - 'title': 'test title', - 'description': 'test description', - 'tagline': 'test rocks', - 'website': 'http://test.cloudron.io', - 'contactEmail': 'test@cloudron.io', - 'version': '0.1.0', - 'manifestVersion': 1, - 'dockerImage': 'cloudron/test:25.2.0', - 'healthCheckPath': '/', - 'httpPort': 7777, - 'tcpPorts': { - 'ECHO_SERVER_PORT': { - 'title': 'Echo Server Port', - 'description': 'Echo server', - 'containerPort': 7778 - } - }, - 'addons': { - 'oauth': { }, - 'redis': { }, - 'mysql': { }, - 'postgresql': { } - } -}; - -const DOMAIN_0 = { - domain: 'example.com', - zoneName: 'example.com', - provider: 'route53', - config: { - accessKeyId: 'accessKeyId', - secretAccessKey: 'secretAccessKey', - endpoint: 'http://localhost:5353' - }, - fallbackCertificate: null, - tlsConfig: { provider: 'letsencrypt-staging' }, - wellKnown: null -}; - -let AUDIT_SOURCE = { ip: '1.2.3.4' }; - -var ADMIN = { - id: 'admin123', - username: 'admin123', - password: 'secret', - email: 'admin@me.com', - fallbackEmail: 'admin@me.com', - salt: 'morton', - createdAt: 'sometime back', - resetToken: '', - displayName: '', - role: 'owner', - source: '' -}; - -var APP = { - id: 'appid', - appStoreId: 'appStoreId', - installationState: apps.ISTATE_PENDING_INSTALL, - runState: 'running', - location: 'applocation', - domain: DOMAIN_0.domain, - fqdn: DOMAIN_0.domain + '.' + 'applocation', - manifest: MANIFEST, - containerId: 'someid', - portBindings: null, - accessRestriction: null, - memoryLimit: 0, - mailboxDomain: DOMAIN_0.domain, - alternateDomains: [], - aliasDomains: [] -}; +const { APP } = common; describe('apptask', function () { - before(function (done) { - async.series([ - database.initialize, - database._clear, - blobs.initSecrets, - settings.setDashboardLocation.bind(null, DOMAIN_0.domain, 'my.' + DOMAIN_0.domain), - domains.add.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE), - userdb.add.bind(null, ADMIN.id, ADMIN), - appdb.add.bind(null, APP.id, APP.appStoreId, APP.manifest, APP.location, APP.domain, APP.portBindings, APP) - ], done); - }); - - after(function (done) { - async.series([ - database._clear, - database.uninitialize - ], done); - }); - - it('configure nginx correctly', function (done) { - apptask._configureReverseProxy(APP, function (error) { - expect(fs.existsSync(paths.NGINX_APPCONFIG_DIR + '/' + APP.id + '.conf')); - expect(error).to.be(null); - done(); - }); - }); - - it('unconfigure nginx', function (done) { - apptask._unconfigureReverseProxy(APP, function (error) { - expect(!fs.existsSync(paths.NGINX_APPCONFIG_DIR + '/' + APP.id + '.conf')); - expect(error).to.be(null); - done(); - }); - }); + before(common.setup); + after(common.cleanup); it('create volume', function (done) { apptask._createAppDir(APP, function (error) { diff --git a/src/test/backups-test.js b/src/test/backups-test.js index a387b50f7..f3c9de9b0 100644 --- a/src/test/backups-test.js +++ b/src/test/backups-test.js @@ -6,15 +6,12 @@ 'use strict'; -var appdb = require('../appdb.js'), - async = require('async'), +const async = require('async'), backupdb = require('../backupdb.js'), backups = require('../backups.js'), BoxError = require('../boxerror.js'), - createTree = require('./common.js').createTree, - database = require('../database'), + common = require('./common.js'), DataLayout = require('../datalayout.js'), - domains = require('../domains.js'), expect = require('expect.js'), fs = require('fs'), os = require('os'), @@ -25,6 +22,8 @@ var appdb = require('../appdb.js'), settings = require('../settings.js'), tasks = require('../tasks.js'); +const { createTree, APP } = common; + function createBackup(callback) { backups.startBackupTask({ username: 'test' }, function (error, taskId) { // this call does not wait for the backup! if (error) return callback(error); @@ -42,7 +41,8 @@ function createBackup(callback) { if (error) return callback(error); if (result.length !== 1) return callback(new Error('result is not of length 1')); - callback(null, result[0]); + // the task progress and the db entry is set in the worker. wait for 2 seconds for backup lock to get released in parent process + setTimeout(() => callback(null, result[0]), 2000); }); }); } @@ -71,119 +71,13 @@ function cleanupBackups(callback) { }); } -describe('retention policy', function () { - it('keeps latest', function () { - let backup = { creationTime: moment().subtract(5, 's').toDate(), state: backups.BACKUP_STATE_NORMAL }; - backups._applyBackupRetentionPolicy([backup], { keepWithinSecs: 1, keepLatest: true }, []); - expect(backup.keepReason).to.be('latest'); - }); - - it('does not keep latest', function () { - let backup = { creationTime: moment().subtract(5, 's').toDate(), state: backups.BACKUP_STATE_NORMAL }; - backups._applyBackupRetentionPolicy([backup], { keepWithinSecs: 1, keepLatest: false }, []); - expect(backup.keepReason).to.be(undefined); - }); - - it('always keeps forever policy', function () { - let backup = { creationTime: new Date() }; - backups._applyBackupRetentionPolicy([backup], { keepWithinSecs: -1, keepLatest: true }, []); - expect(backup.keepReason).to.be('keepWithinSecs'); - }); - - it('preserveSecs takes precedence', function () { - let backup = { creationTime: new Date(), preserveSecs: 3000 }; - backups._applyBackupRetentionPolicy([backup], { keepWithinSecs: 1, keepLatest: true }, []); - expect(backup.keepReason).to.be('preserveSecs'); - }); - - it('1 daily', function () { - let b = [ - { id: '0', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().toDate() }, - { id: '1', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(1, 'h').toDate() }, - { id: '2', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(3, 'h').toDate() }, - { id: '3', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(20, 'h').toDate() }, - { id: '4', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(5, 'd').toDate() } - ]; - backups._applyBackupRetentionPolicy(b, { keepDaily: 1, keepLatest: true }, []); - expect(b[0].keepReason).to.be('keepDaily'); - expect(b[1].keepReason).to.be(undefined); - expect(b[2].keepReason).to.be(undefined); - expect(b[3].keepReason).to.be(undefined); - expect(b[3].keepReason).to.be(undefined); - }); - - // if you are debugging this test, it's because of some timezone issue with all the hour substraction! - it('2 daily, 1 weekly', function () { - let b = [ - { id: '0', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().toDate() }, - { id: '1', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(1, 'h').toDate() }, - { id: '2', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(3, 'h').toDate() }, - { id: '3', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(26, 'h').toDate() }, - { id: '4', state: backups.BACKUP_STATE_ERROR, creationTime: moment().subtract(32, 'h').toDate() }, - { id: '5', state: backups.BACKUP_STATE_CREATING, creationTime: moment().subtract(50, 'h').toDate() }, - { id: '6', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(5, 'd').toDate() } - ]; - backups._applyBackupRetentionPolicy(b, { keepDaily: 2, keepWeekly: 1, keepLatest: false }, []); - expect(b[0].keepReason).to.be('keepDaily'); // today - expect(b[1].keepReason).to.be('keepWeekly'); // today - expect(b[2].keepReason).to.be(undefined); - expect(b[3].keepReason).to.be('keepDaily'); // yesterday - expect(b[4].discardReason).to.be('error'); // errored - expect(b[5].discardReason).to.be('creating-too-long'); // creating for too long - expect(b[6].keepReason).to.be(undefined); // outside retention policy - }); - - it('2 daily, 3 monthly, 1 yearly', function () { - let b = [ - { id: '0', state: backups.BACKUP_STATE_CREATING, creationTime: moment().toDate() }, - { id: '1', state: backups.BACKUP_STATE_ERROR, creationTime: moment().toDate() }, - { id: '2', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().toDate() }, - { id: '3', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(1, 'h').toDate() }, - { id: '4', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(3, 'h').toDate() }, - { id: '5', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(26, 'h').toDate() }, - { id: '6', state: backups.BACKUP_STATE_CREATING, creationTime: moment().subtract(49, 'h').toDate() }, - { id: '7', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(51, 'd').toDate() }, - { id: '8', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(84, 'd').toDate() }, - { id: '9', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(97, 'd').toDate() }, - ]; - backups._applyBackupRetentionPolicy(b, { keepDaily: 2, keepMonthly: 3, keepYearly: 1, keepLatest: true }, []); - expect(b[0].keepReason).to.be('creating'); - expect(b[1].discardReason).to.be('error'); // errored - expect(b[2].keepReason).to.be('keepDaily'); - expect(b[3].keepReason).to.be('keepMonthly'); - expect(b[4].keepReason).to.be('keepYearly'); - expect(b[5].keepReason).to.be('keepDaily'); // yesterday - expect(b[6].discardReason).to.be('creating-too-long'); // errored - expect(b[7].keepReason).to.be('keepMonthly'); - expect(b[8].keepReason).to.be('keepMonthly'); - expect(b[9].keepReason).to.be(undefined); - }); - -}); - describe('backups', function () { - const DOMAIN_0 = { - domain: 'example.com', - zoneName: 'example.com', - provider: 'manual', - config: { }, - fallbackCertificate: null, - tlsConfig: { provider: 'fallback' }, - wellKnown: null - }; - const AUDIT_SOURCE = { ip: '1.2.3.4' }; - - const manifest = { version: '0.0.1', manifestVersion: 1, dockerImage: 'foo', healthCheckPath: '/', httpPort: 3, title: 'ok', addons: { } }; - before(function (done) { const BACKUP_DIR = path.join(os.tmpdir(), 'cloudron-backup-test'); async.series([ + common.setup, fs.mkdir.bind(null, BACKUP_DIR, { recursive: true }), - database.initialize, - database._clear, - settings.setDashboardLocation.bind(null, DOMAIN_0.domain, 'my.' + DOMAIN_0.domain), - domains.add.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE), settingsdb.set.bind(null, settings.BACKUP_CONFIG_KEY, JSON.stringify({ provider: 'filesystem', password: 'supersecret', @@ -194,10 +88,96 @@ describe('backups', function () { ], done); }); - after(function (done) { - async.series([ - database._clear - ], done); + after(common.cleanup); + + describe('retention policy', function () { + it('keeps latest', function () { + let backup = { creationTime: moment().subtract(5, 's').toDate(), state: backups.BACKUP_STATE_NORMAL }; + backups._applyBackupRetentionPolicy([backup], { keepWithinSecs: 1, keepLatest: true }, []); + expect(backup.keepReason).to.be('latest'); + }); + + it('does not keep latest', function () { + let backup = { creationTime: moment().subtract(5, 's').toDate(), state: backups.BACKUP_STATE_NORMAL }; + backups._applyBackupRetentionPolicy([backup], { keepWithinSecs: 1, keepLatest: false }, []); + expect(backup.keepReason).to.be(undefined); + }); + + it('always keeps forever policy', function () { + let backup = { creationTime: new Date() }; + backups._applyBackupRetentionPolicy([backup], { keepWithinSecs: -1, keepLatest: true }, []); + expect(backup.keepReason).to.be('keepWithinSecs'); + }); + + it('preserveSecs takes precedence', function () { + let backup = { creationTime: new Date(), preserveSecs: 3000 }; + backups._applyBackupRetentionPolicy([backup], { keepWithinSecs: 1, keepLatest: true }, []); + expect(backup.keepReason).to.be('preserveSecs'); + }); + + it('1 daily', function () { + let b = [ + { id: '0', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().toDate() }, + { id: '1', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(1, 'h').toDate() }, + { id: '2', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(3, 'h').toDate() }, + { id: '3', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(20, 'h').toDate() }, + { id: '4', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(5, 'd').toDate() } + ]; + backups._applyBackupRetentionPolicy(b, { keepDaily: 1, keepLatest: true }, []); + expect(b[0].keepReason).to.be('keepDaily'); + expect(b[1].keepReason).to.be(undefined); + expect(b[2].keepReason).to.be(undefined); + expect(b[3].keepReason).to.be(undefined); + expect(b[3].keepReason).to.be(undefined); + }); + + // if you are debugging this test, it's because of some timezone issue with all the hour substraction! + it('2 daily, 1 weekly', function () { + let b = [ + { id: '0', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().toDate() }, + { id: '1', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(1, 'h').toDate() }, + { id: '2', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(3, 'h').toDate() }, + { id: '3', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(26, 'h').toDate() }, + { id: '4', state: backups.BACKUP_STATE_ERROR, creationTime: moment().subtract(32, 'h').toDate() }, + { id: '5', state: backups.BACKUP_STATE_CREATING, creationTime: moment().subtract(50, 'h').toDate() }, + { id: '6', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(5, 'd').toDate() } + ]; + backups._applyBackupRetentionPolicy(b, { keepDaily: 2, keepWeekly: 1, keepLatest: false }, []); + expect(b[0].keepReason).to.be('keepDaily'); // today + expect(b[1].keepReason).to.be('keepWeekly'); // today + expect(b[2].keepReason).to.be(undefined); + expect(b[3].keepReason).to.be('keepDaily'); // yesterday + expect(b[4].discardReason).to.be('error'); // errored + expect(b[5].discardReason).to.be('creating-too-long'); // creating for too long + expect(b[6].keepReason).to.be(undefined); // outside retention policy + }); + + it('2 daily, 3 monthly, 1 yearly', function () { + let b = [ + { id: '0', state: backups.BACKUP_STATE_CREATING, creationTime: moment().toDate() }, + { id: '1', state: backups.BACKUP_STATE_ERROR, creationTime: moment().toDate() }, + { id: '2', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().toDate() }, + { id: '3', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(1, 'h').toDate() }, + { id: '4', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(3, 'h').toDate() }, + { id: '5', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(26, 'h').toDate() }, + { id: '6', state: backups.BACKUP_STATE_CREATING, creationTime: moment().subtract(49, 'h').toDate() }, + { id: '7', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(51, 'd').toDate() }, + { id: '8', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(84, 'd').toDate() }, + { id: '9', state: backups.BACKUP_STATE_NORMAL, creationTime: moment().subtract(97, 'd').toDate() }, + ]; + backups._applyBackupRetentionPolicy(b, { keepDaily: 2, keepMonthly: 3, keepYearly: 1, keepLatest: true }, []); + expect(b[0].keepReason).to.be('creating'); + expect(b[1].discardReason).to.be('error'); // errored + expect(b[2].keepReason).to.be('keepDaily'); + expect(b[3].keepReason).to.be('keepMonthly'); + expect(b[4].keepReason).to.be('keepYearly'); + expect(b[5].keepReason).to.be('keepDaily'); // yesterday + expect(b[6].discardReason).to.be('creating-too-long'); // errored + expect(b[7].keepReason).to.be('keepMonthly'); + expect(b[8].keepReason).to.be('keepMonthly'); + expect(b[9].keepReason).to.be(undefined); + }); + }); describe('cleanup', function () { @@ -215,7 +195,7 @@ describe('backups', function () { var BACKUP_0_APP_0 = { // backup of installed app id: 'backup-app-00', - identifier: 'app0', + identifier: APP.id, encryptionVersion: null, packageVersion: '1.0.0', type: backups.BACKUP_TYPE_APP, @@ -255,7 +235,7 @@ describe('backups', function () { packageVersion: '1.0.0', type: backups.BACKUP_TYPE_APP, state: backups.BACKUP_STATE_NORMAL, - identifier: 'app0', + identifier: APP.id, dependsOn: [], manifest: null, format: 'tgz' @@ -273,14 +253,6 @@ describe('backups', function () { format: 'tgz' }; - before(function (done) { - appdb.add('app0', 'appStoreId', manifest, 'location', DOMAIN_0.domain, [ ] /* portBindings */, { installationState: 'installed', runState: 'running', mailboxDomain: DOMAIN_0.domain }, done); - }); - - after(function (done) { - appdb.del('app0', done); - }); - it('succeeds without backups', function (done) { cleanupBackups(done); }); @@ -289,7 +261,7 @@ describe('backups', function () { async.eachSeries([[ BACKUP_0, BACKUP_0_APP_0, BACKUP_0_APP_1 ], [ BACKUP_1, BACKUP_1_APP_0, BACKUP_1_APP_1 ]], function (backup, callback) { // space out backups setTimeout(function () { - async.each(backup, (b, done) => backupdb.add(b.id, b, done), callback); + async.eachSeries(backup, (b, done) => backupdb.add(b.id, b, done), callback); }, 2000); }, function (error) { expect(error).to.not.be.ok(); @@ -347,6 +319,7 @@ describe('backups', function () { backupdb.getByTypePaged(backups.BACKUP_TYPE_APP, 1, 1000, function (error, result) { expect(error).to.not.be.ok(); expect(result.length).to.equal(3); + result = result.sort((r1, r2) => r1.id.localeCompare(r2.id)); expect(result[0].id).to.be(BACKUP_0_APP_0.id); // because app is installed, latest backup is preserved expect(result[1].id).to.be(BACKUP_1_APP_0.id); // referenced by box expect(result[2].id).to.be(BACKUP_1_APP_1.id); // referenced by box @@ -360,7 +333,7 @@ describe('backups', function () { }); describe('fs meta data', function () { - var tmpdir; + let tmpdir; before(function () { tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'backups-test')); }); @@ -407,9 +380,9 @@ describe('backups', function () { }); describe('filesystem', function () { - var backupInfo1; + let backupInfo1; - var backupConfig = { + const backupConfig = { provider: 'filesystem', backupFolder: path.join(os.tmpdir(), 'backups-test-filesystem'), format: 'tgz', @@ -428,8 +401,9 @@ describe('backups', function () { done(); }); - it('fails to set backup config for non-existing folder', function (done) { - settings.setBackupConfig(backupConfig, function (error) { + it('fails to set backup config for bad folder', function (done) { + const tmp = Object.assign({}, backupConfig, { backupFolder: '/root/oof' }); + settings.setBackupConfig(tmp, function (error) { expect(error).to.be.a(BoxError); expect(error.reason).to.equal(BoxError.BAD_FIELD); @@ -438,11 +412,11 @@ describe('backups', function () { }); it('succeeds to set backup config', function (done) { - fs.mkdirSync(backupConfig.backupFolder, { recursive: true }); - settings.setBackupConfig(backupConfig, function (error) { expect(error).to.be(null); + expect(fs.existsSync(path.join(backupConfig.backupFolder, 'snapshot'))).to.be(true); // auto-created + done(); }); }); diff --git a/src/test/common.js b/src/test/common.js index 10ed74a2f..2fe84d807 100644 --- a/src/test/common.js +++ b/src/test/common.js @@ -1,11 +1,111 @@ 'use strict'; -var fs = require('fs'), +const appdb = require('../appdb.js'), + apps = require('../apps.js'), + async = require('async'), + blobs = require('../blobs.js'), + database = require('../database.js'), + domains = require('../domains.js'), + fs = require('fs'), + mailer = require('../mailer.js'), + nock = require('nock'), path = require('path'), - rimraf = require('rimraf'); + rimraf = require('rimraf'), + settings = require('../settings.js'), + settingsdb = require('../settingsdb.js'), + users = require('../users.js'); + +const MANIFEST = { + 'id': 'io.cloudron.test', + 'author': 'The Presidents Of the United States Of America', + 'title': 'test title', + 'description': 'test description', + 'tagline': 'test rocks', + 'website': 'http://test.cloudron.io', + 'contactEmail': 'test@cloudron.io', + 'version': '0.1.0', + 'manifestVersion': 1, + 'dockerImage': 'cloudron/test:25.2.0', + 'healthCheckPath': '/', + 'httpPort': 7777, + 'tcpPorts': { + 'ECHO_SERVER_PORT': { + 'title': 'Echo Server Port', + 'description': 'Echo server', + 'containerPort': 7778 + } + }, + 'addons': { + 'oauth': { }, + 'redis': { }, + 'mysql': { }, + 'postgresql': { } + } +}; + +const DOMAIN = { + domain: 'example.com', + zoneName: 'example.com', + provider: 'route53', + config: { + accessKeyId: 'accessKeyId', + secretAccessKey: 'secretAccessKey', + endpoint: 'http://localhost:5353' + }, + fallbackCertificate: null, + tlsConfig: { provider: 'letsencrypt-staging' }, + wellKnown: null +}; + +const AUDIT_SOURCE = { ip: '1.2.3.4' }; + +const ADMIN = { + id: 'admin123', + username: 'admin123', + password: 'secret123', + email: 'admin@me.com', + fallbackEmail: 'admin@me.com', + salt: 'morton', + createdAt: 'sometime back', + resetToken: '', + displayName: '', + role: 'owner', + source: '' +}; + +const APP = { + id: 'appid', + appStoreId: 'appStoreId', + installationState: apps.ISTATE_PENDING_INSTALL, + runState: 'running', + location: 'applocation', + domain: DOMAIN.domain, + fqdn: DOMAIN.domain + '.' + 'applocation', + manifest: MANIFEST, + containerId: 'someid', + portBindings: null, + accessRestriction: null, + memoryLimit: 0, + mailboxDomain: DOMAIN.domain, + alternateDomains: [], + aliasDomains: [] +}; exports = module.exports = { - createTree: createTree + createTree, + setup, + cleanup, + + MOCK_API_SERVER_ORIGIN: 'http://localhost:6060', + DASHBOARD_DOMAIN: 'test.example.com', + DASHBOARD_FQDN: 'my.test.example.com', + + APP, + ADMIN, + AUDIT_SOURCE, + DOMAIN, + MANIFEST, + APPSTORE_TOKEN: 'atoken' }; function createTree(root, obj) { @@ -30,3 +130,29 @@ function createTree(root, obj) { createSubTree(obj, root); } +function setup(done) { + nock.cleanAll(); + + async.series([ + database.initialize, + database._clear, + settings._setApiServerOrigin.bind(null, exports.MOCK_API_SERVER_ORIGIN), + settings.setDashboardLocation.bind(null, exports.DASHBOARD_DOMAIN, exports.DASHBOARD_FQDN), + settings.initCache, + blobs.initSecrets, + domains.add.bind(null, DOMAIN.domain, DOMAIN, AUDIT_SOURCE), + users.createOwner.bind(null, ADMIN.username, ADMIN.password, ADMIN.email, ADMIN.displayName, AUDIT_SOURCE), + appdb.add.bind(null, APP.id, APP.appStoreId, APP.manifest, APP.location, APP.domain, APP.portBindings, APP), + settingsdb.set.bind(null, settings.CLOUDRON_TOKEN_KEY, exports.APPSTORE_TOKEN), // appstore token + ], done); +} + +function cleanup(done) { + nock.cleanAll(); + mailer._mailQueue = []; + + async.series([ + database._clear, + database.uninitialize + ], done); +} diff --git a/src/test/database-test.js b/src/test/database-test.js index b20bad55e..a2968385e 100644 --- a/src/test/database-test.js +++ b/src/test/database-test.js @@ -33,7 +33,7 @@ var USER_0 = { email: 'safe@me.com', fallbackEmail: 'safer@me.com', salt: 'morton', - createdAt: 'sometime back', + creationTime: Date.now(), resetToken: hat(256), displayName: '', twoFactorAuthenticationEnabled: false, @@ -52,7 +52,7 @@ var USER_1 = { email: 'safe2@me.com', fallbackEmail: 'safer2@me.com', salt: 'tata', - createdAt: 'sometime back', + creationTime: Date.now(), resetToken: '', displayName: 'Herbert 1', twoFactorAuthenticationEnabled: false, @@ -71,7 +71,7 @@ var USER_2 = { email: 'safe3@me.com', fallbackEmail: 'safer3@me.com', salt: 'tata', - createdAt: 'sometime back', + creationTime: Date.now(), resetToken: '', displayName: 'Herbert 2', twoFactorAuthenticationEnabled: false, diff --git a/src/test/dns-test.js b/src/test/dns-test.js index abadafa8f..85004428a 100644 --- a/src/test/dns-test.js +++ b/src/test/dns-test.js @@ -7,46 +7,20 @@ 'use strict'; -var async = require('async'), - AWS = require('aws-sdk'), - GCDNS = require('@google-cloud/dns').DNS, - database = require('../database.js'), +const AWS = require('aws-sdk'), + common = require('./common.js'), domains = require('../domains.js'), expect = require('expect.js'), + GCDNS = require('@google-cloud/dns').DNS, nock = require('nock'), - querystring = require('querystring'), - settings = require('../settings.js'), - util = require('util'), _ = require('underscore'); -var DOMAIN_0 = { - domain: 'example-dns-test.com', - zoneName: 'example-dns-test.com', - provider: 'noop', - config: {}, - fallbackCertificate: null, - tlsConfig: { provider: 'fallback' }, - wellKnown: null -}; - -var AUDIT_SOURCE = { ip: '1.2.3.4' }; +const { AUDIT_SOURCE, DOMAIN } = common; +const DOMAIN_0 = Object.assign({}, DOMAIN); // make a copy describe('dns provider', function () { - before(function (done) { - async.series([ - database.initialize, - database._clear, - settings.setDashboardLocation.bind(null, DOMAIN_0.domain, 'my.' + DOMAIN_0.domain), - domains.add.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE) - ], done); - }); - - after(function (done) { - async.series([ - database._clear, - database.uninitialize - ], done); - }); + before(common.setup); + after(common.cleanup); describe('noop', function () { before(function (done) { diff --git a/src/test/dockerproxy-test.js b/src/test/dockerproxy-test.js index 75eae7d53..256d6c7a8 100644 --- a/src/test/dockerproxy-test.js +++ b/src/test/dockerproxy-test.js @@ -6,7 +6,7 @@ 'use strict'; -var constants = require('../constants.js'), +const constants = require('../constants.js'), dockerProxy = require('../dockerproxy.js'), exec = require('child_process').exec, expect = require('expect.js'); diff --git a/src/test/eventlog-test.js b/src/test/eventlog-test.js index da3077084..d23884e38 100644 --- a/src/test/eventlog-test.js +++ b/src/test/eventlog-test.js @@ -7,30 +7,22 @@ 'use strict'; const async = require('async'), + common = require('./common.js'), database = require('../database.js'), delay = require('delay'), eventlog = require('../eventlog.js'), expect = require('expect.js'), notifications = require('../notifications.js'); -function setup(done) { - // ensure data/config/mount paths - database.initialize(function (error) { - expect(error).to.be(null); - done(); - }); -} - -function cleanup(done) { - async.series([ - database._clear, - database.uninitialize - ], done); -} - describe('Eventlog', function () { - before(setup); - after(cleanup); + before(function (done) { + async.series([ + common.setup, + eventlog._clear // domain add events + ], done); + }); + + after(common.cleanup); var eventId; @@ -38,6 +30,7 @@ describe('Eventlog', function () { const id = await eventlog.add('some.event', { ip: '1.2.3.4' }, { appId: 'thatapp' }); expect(id).to.be.a('string'); + console.log(id); eventId = id; }); @@ -126,7 +119,7 @@ describe('Eventlog', function () { await notifications._add(eventId, 'title', 'some message'); } - await delay(2000); + await delay(3000); const id = await eventlog.add('some.event', { ip: '1.2.3.4' }, { appId: 'thatapp' }); diff --git a/src/test/network-test.js b/src/test/network-test.js index 1f94d49b6..fcefa7966 100644 --- a/src/test/network-test.js +++ b/src/test/network-test.js @@ -1,16 +1,21 @@ /* global it:false */ /* global describe:false */ /* global before:false */ +/* global after:false */ 'use strict'; -var BoxError = require('../boxerror.js'), +const BoxError = require('../boxerror.js'), + common = require('./common.js'), expect = require('expect.js'), fs = require('fs'), network = require('../network.js'), paths = require('../paths.js'); describe('Network', function () { + before(common.setup); + after(common.cleanup); + describe('Blocklist', function () { before(function () { @@ -32,7 +37,7 @@ describe('Network', function () { network.getBlocklist(function (error, result) { expect(error).to.equal(null); - expect(result).to.equal('\n'); + expect(result).to.equal(''); done(); }); @@ -45,7 +50,7 @@ describe('Network', function () { network.getBlocklist(function (error, result) { expect(error).to.equal(null); - expect(result).to.equal('192.168.178.1\n'); + expect(result).to.equal('192.168.178.1'); done(); }); @@ -58,7 +63,7 @@ describe('Network', function () { network.getBlocklist(function (error, result) { expect(error).to.equal(null); - expect(result).to.equal('2a02:8106:2f:bb00:7afc:5703:ee71:3ef8\n'); + expect(result).to.equal('2a02:8106:2f:bb00:7afc:5703:ee71:3ef8'); done(); }); @@ -71,7 +76,7 @@ describe('Network', function () { network.getBlocklist(function (error, result) { expect(error).to.equal(null); - expect(result).to.equal('2a02:8106:2f:bb00:7afc:5703:ee71:3ef8\n# some comment\n192.168.178.1\n'); + expect(result).to.equal('2a02:8106:2f:bb00:7afc:5703:ee71:3ef8\n# some comment\n192.168.178.1'); done(); }); @@ -84,7 +89,7 @@ describe('Network', function () { network.getBlocklist(function (error, result) { expect(error).to.equal(null); - expect(result).to.equal('192.168.178.1/24\n'); + expect(result).to.equal('192.168.178.1/24'); done(); }); @@ -97,7 +102,7 @@ describe('Network', function () { network.getBlocklist(function (error, result) { expect(error).to.equal(null); - expect(result).to.equal('2001:db8::\n'); + expect(result).to.equal('2001:db8::'); done(); }); @@ -146,7 +151,7 @@ describe('Network', function () { network.getBlocklist(function (error, result) { expect(error).to.equal(null); - expect(result).to.equal('192.168.178.1\n'); + expect(result).to.equal('192.168.178.1'); done(); }); @@ -159,7 +164,7 @@ describe('Network', function () { network.getBlocklist(function (error, result) { expect(error).to.equal(null); - expect(result).to.equal('2001:db8:1234::1\n'); + expect(result).to.equal('2001:db8:1234::1'); done(); }); @@ -172,7 +177,7 @@ describe('Network', function () { network.getBlocklist(function (error, result) { expect(error).to.equal(null); - expect(result).to.equal('192.168.178.1/32\n'); + expect(result).to.equal('192.168.178.1/32'); done(); }); @@ -185,7 +190,7 @@ describe('Network', function () { network.getBlocklist(function (error, result) { expect(error).to.equal(null); - expect(result).to.equal('2001:db8:1234::\n'); + expect(result).to.equal('2001:db8:1234::'); done(); }); diff --git a/src/test/notifications-test.js b/src/test/notifications-test.js index 20322b305..8d77a0b7e 100644 --- a/src/test/notifications-test.js +++ b/src/test/notifications-test.js @@ -6,9 +6,9 @@ 'use strict'; -const async = require('async'), - BoxError = require('../boxerror.js'), - database = require('../database.js'), +const BoxError = require('../boxerror.js'), + common = require('./common.js'), + delay = require('delay'), expect = require('expect.js'), notifications = require('../notifications.js'), safe = require('safetydance'); @@ -20,23 +20,9 @@ const EVENT_0 = { data: {} }; -function setup(done) { - async.series([ - database.initialize, - database._clear, - ], done); -} - -function cleanup(done) { - async.series([ - database._clear, - database.uninitialize - ], done); -} - describe('Notifications', function () { - before(setup); - after(cleanup); + before(common.setup); + after(common.cleanup); let notificationIds = []; @@ -46,6 +32,7 @@ describe('Notifications', function () { expect(error).to.equal(null); expect(id).to.be.a('string'); notificationIds.push(id); + await delay(1000); } }); @@ -65,9 +52,9 @@ describe('Notifications', function () { it('can list notifications', async function () { const result = await notifications.list({}, 1, 10); expect(result.length).to.be(3); - expect(result[0].title).to.be('title 0'); + expect(result[0].title).to.be('title 2'); expect(result[1].title).to.be('title 1'); - expect(result[2].title).to.be('title 2'); + expect(result[2].title).to.be('title 0'); }); it('can update notification', async function () { diff --git a/src/test/reverseproxy-test.js b/src/test/reverseproxy-test.js index 9935f74f4..d4c56a9a9 100644 --- a/src/test/reverseproxy-test.js +++ b/src/test/reverseproxy-test.js @@ -5,45 +5,20 @@ 'use strict'; -const async = require('async'), - blobs = require('../blobs.js'), - database = require('../database.js'), +const common = require('./common.js'), domains = require('../domains.js'), expect = require('expect.js'), - reverseProxy = require('../reverseproxy.js'), - settings = require('../settings.js'); + fs = require('fs'), + paths = require('../paths.js'), + reverseProxy = require('../reverseproxy.js'); -const DOMAIN_0 = { - domain: 'example-reverseproxy-test.com', - zoneName: 'example-reverseproxy-test.com', - provider: 'noop', - config: {}, - fallbackCertificate: null, - tlsConfig: { provider: 'fallback' }, - wellKnown: null -}; - -let AUDIT_SOURCE = { ip: '1.2.3.4' }; - -function setup(done) { - async.series([ - database.initialize, - database._clear, - blobs.initSecrets, - settings.setDashboardLocation.bind(null, DOMAIN_0.domain, 'my.' + DOMAIN_0.domain), - domains.add.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE), - settings.initCache - ], done); -} - -function cleanup(done) { - async.series([ - database._clear, - database.uninitialize - ], done); -} +const { DOMAIN, AUDIT_SOURCE, APP } = common; +const DOMAIN_0 = Object.assign({}, DOMAIN); describe('Certificates', function () { + before(common.setup); + after(common.cleanup); + describe('validateCertificate', function () { let foobarDomain = { domain: 'foobar.com', @@ -169,14 +144,9 @@ describe('Certificates', function () { before(function (done) { DOMAIN_0.tlsConfig = { provider: 'letsencrypt-prod' }; - async.series([ - setup, - domains.update.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE) - ], done); + domains.update(DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE, done); }); - after(cleanup); - it('returns prod acme in prod cloudron', function (done) { reverseProxy._getAcmeApi(DOMAIN_0, function (error, api, options) { expect(error).to.be(null); @@ -200,14 +170,9 @@ describe('Certificates', function () { before(function (done) { DOMAIN_0.tlsConfig = { provider: 'letsencrypt-staging' }; - async.series([ - setup, - domains.update.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE) - ], done); + domains.update(DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE, done); }); - after(cleanup); - it('returns staging acme in prod cloudron', function (done) { reverseProxy._getAcmeApi(DOMAIN_0, function (error, api, options) { expect(error).to.be(null); @@ -226,4 +191,22 @@ describe('Certificates', function () { }); }); }); + + describe('configureApp', function () { + it('configure nginx correctly', function (done) { + reverseProxy.configureApp(APP, AUDIT_SOURCE, function (error) { + expect(fs.existsSync(paths.NGINX_APPCONFIG_DIR + '/' + APP.id + '.conf')); + expect(error).to.be(null); + done(); + }); + }); + + it('unconfigure nginx', function (done) { + reverseProxy.unconfigureApp(APP, function (error) { + expect(!fs.existsSync(paths.NGINX_APPCONFIG_DIR + '/' + APP.id + '.conf')); + expect(error).to.be(null); + done(); + }); + }); + }); }); diff --git a/src/test/system-test.js b/src/test/system-test.js index d9b74e692..453fab455 100644 --- a/src/test/system-test.js +++ b/src/test/system-test.js @@ -6,28 +6,13 @@ 'use strict'; -const async = require('async'), - database = require('../database.js'), +const common = require('./common.js'), expect = require('expect.js'), system = require('../system.js'); -function setup(done) { - async.series([ - database.initialize, - database._clear - ], done); -} - -function cleanup(done) { - async.series([ - database._clear, - database.uninitialize - ], done); -} - describe('System', function () { - before(setup); - after(cleanup); + before(common.setup); + after(common.cleanup); it('can get disks', function (done) { system.getDisks(function (error, disks) { diff --git a/src/test/tasks-test.js b/src/test/tasks-test.js index 1d83420fb..70a2029c7 100644 --- a/src/test/tasks-test.js +++ b/src/test/tasks-test.js @@ -6,30 +6,15 @@ 'use strict'; -var async = require('async'), - database = require('../database.js'), +const common = require('./common.js'), expect = require('expect.js'), fs = require('fs'), paths = require('../paths.js'), tasks = require('../tasks.js'); -function setup(done) { - async.series([ - database.initialize, - database._clear - ], done); -} - -function cleanup(done) { - async.series([ - database._clear, - database.uninitialize - ], done); -} - describe('task', function () { - before(setup); - after(cleanup); + before(common.setup); + after(common.cleanup); it('can run valid task - success', function (done) { tasks.add(tasks._TASK_IDENTITY, [ 'ping' ], function (error, taskId) { diff --git a/src/test/translation-test.js b/src/test/translation-test.js index 10bf588fc..6105c1532 100644 --- a/src/test/translation-test.js +++ b/src/test/translation-test.js @@ -5,16 +5,12 @@ 'use strict'; -var expect = require('expect.js'), +const expect = require('expect.js'), translation = require('../translation.js'); describe('translation', function () { describe('translate', function () { - before(function (done) { - done(); - }); - it('nonexisting token', function () { var out = translation.translate('Foo {{ bar }}', {}, {}); expect(out).to.contain('{{ bar }}'); diff --git a/src/test/updatechecker-test.js b/src/test/updatechecker-test.js index 2f609bbf5..773c41a62 100644 --- a/src/test/updatechecker-test.js +++ b/src/test/updatechecker-test.js @@ -5,252 +5,145 @@ 'use strict'; -var appdb = require('../appdb.js'), - apps = require('../apps.js'), - async = require('async'), +const common = require('./common.js'), constants = require('../constants.js'), - cron = require('../cron.js'), - database = require('../database.js'), - domains = require('../domains.js'), expect = require('expect.js'), - mailer = require('../mailer.js'), nock = require('nock'), paths = require('../paths.js'), safe = require('safetydance'), semver = require('semver'), settings = require('../settings.js'), - settingsdb = require('../settingsdb.js'), - updatechecker = require('../updatechecker.js'), - users = require('../users.js'); + updatechecker = require('../updatechecker.js'); -// owner -var USER_0 = { - username: 'username0', - password: 'Username0pass?1234', - email: 'user0@email.com', - displayName: 'User 0', - source: '', - permissions: null -}; - -const DASHBOARD_DOMAIN = 'updatechecker-test.example.com'; - -const DOMAIN_0 = { - domain: 'example.com', - zoneName: 'example.com', - config: {}, - provider: 'manual', - fallbackCertificate: null, - tlsConfig: { provider: 'fallback' }, - wellKnown: null -}; - -var AUDIT_SOURCE = { - ip: '1.2.3.4' -}; +const { APP, APPSTORE_TOKEN, MOCK_API_SERVER_ORIGIN } = common; const UPDATE_VERSION = semver.inc(constants.VERSION, 'major'); -function checkMails(number, done) { - // mails are enqueued async - setTimeout(function () { - expect(mailer._mailQueue.length).to.equal(number); - mailer._mailQueue = []; - done(); - }, 500); -} +describe('updatechecker', function () { + before(common.setup); + after(common.cleanup); -function cleanup(done) { - mailer._mailQueue = []; - safe.fs.unlinkSync(paths.UPDATE_CHECKER_FILE); + describe('box', function () { + before(function (done) { + safe.fs.unlinkSync(paths.UPDATE_CHECKER_FILE); - async.series([ - cron.stopJobs, - database._clear, - database.uninitialize - ], done); -} + settings.setAutoupdatePattern(constants.AUTOUPDATE_PATTERN_NEVER, done); + }); -describe('updatechecker - box', function () { - before(function (done) { - safe.fs.unlinkSync(paths.UPDATE_CHECKER_FILE); + it('no updates', function (done) { + nock.cleanAll(); - mailer._mailQueue = []; + var scope = nock(MOCK_API_SERVER_ORIGIN) + .get('/api/v1/boxupdate') + .query({ boxVersion: constants.VERSION, accessToken: APPSTORE_TOKEN, automatic: false }) + .reply(204, { } ); - async.series([ - database.initialize, - database._clear, - settings._setApiServerOrigin.bind(null, 'http://localhost:4444'), - settings.setDashboardLocation.bind(null, DASHBOARD_DOMAIN, 'my.' + DASHBOARD_DOMAIN), - cron.startJobs, - domains.add.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE), - settings.setDashboardLocation.bind(null, DOMAIN_0.domain, 'my.' + DOMAIN_0.domain), - users.createOwner.bind(null, USER_0.username, USER_0.password, USER_0.email, USER_0.displayName, AUDIT_SOURCE), - settings.setAutoupdatePattern.bind(null, constants.AUTOUPDATE_PATTERN_NEVER), - settingsdb.set.bind(null, settings.CLOUDRON_TOKEN_KEY, 'atoken'), - ], done); - }); + updatechecker.checkForUpdates({ automatic: false }, function (error) { + expect(!error).to.be.ok(); + expect(updatechecker.getUpdateInfo().box).to.not.be.ok(); + expect(scope.isDone()).to.be.ok(); + done(); + }); + }); - after(cleanup); + it('new version', function (done) { + nock.cleanAll(); - it('no updates', function (done) { - nock.cleanAll(); + var scope = nock(MOCK_API_SERVER_ORIGIN) + .get('/api/v1/boxupdate') + .query({ boxVersion: constants.VERSION, accessToken: APPSTORE_TOKEN, automatic: false }) + .reply(200, { version: UPDATE_VERSION, changelog: [''], sourceTarballUrl: 'box.tar.gz', sourceTarballSigUrl: 'box.tar.gz.sig', boxVersionsUrl: 'box.versions', boxVersionsSigUrl: 'box.versions.sig' } ); - var scope = nock('http://localhost:4444') - .get('/api/v1/boxupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken', automatic: false }) - .reply(204, { } ); + updatechecker.checkForUpdates({ automatic: false }, function (error) { + expect(!error).to.be.ok(); + expect(updatechecker.getUpdateInfo().box.version).to.be(UPDATE_VERSION); + expect(updatechecker.getUpdateInfo().box.sourceTarballUrl).to.be('box.tar.gz'); + expect(scope.isDone()).to.be.ok(); + done(); + }); + }); - updatechecker.checkForUpdates({ automatic: false }, function (error) { - expect(!error).to.be.ok(); - expect(updatechecker.getUpdateInfo().box).to.not.be.ok(); - expect(scope.isDone()).to.be.ok(); + it('bad response offers whatever was last valid', function (done) { + nock.cleanAll(); - checkMails(0, done); + var scope = nock(MOCK_API_SERVER_ORIGIN) + .get('/api/v1/boxupdate') + .query({ boxVersion: constants.VERSION, accessToken: APPSTORE_TOKEN, automatic: false }) + .reply(404, { version: '2.0.0-pre.0', changelog: [''], sourceTarballUrl: 'box-pre.tar.gz' } ); + + updatechecker.checkForUpdates({ automatic: false }, function (error) { + expect(error).to.be.ok(); + expect(updatechecker.getUpdateInfo().box.version).to.be(UPDATE_VERSION); + expect(updatechecker.getUpdateInfo().box.sourceTarballUrl).to.be('box.tar.gz'); + expect(scope.isDone()).to.be.ok(); + done(); + }); }); }); - it('new version', function (done) { - nock.cleanAll(); + describe('app', function () { + before(function (done) { + safe.fs.unlinkSync(paths.UPDATE_CHECKER_FILE); - var scope = nock('http://localhost:4444') - .get('/api/v1/boxupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken', automatic: false }) - .reply(200, { version: UPDATE_VERSION, changelog: [''], sourceTarballUrl: 'box.tar.gz', sourceTarballSigUrl: 'box.tar.gz.sig', boxVersionsUrl: 'box.versions', boxVersionsSigUrl: 'box.versions.sig' } ); - - updatechecker.checkForUpdates({ automatic: false }, function (error) { - expect(!error).to.be.ok(); - expect(updatechecker.getUpdateInfo().box.version).to.be(UPDATE_VERSION); - expect(updatechecker.getUpdateInfo().box.sourceTarballUrl).to.be('box.tar.gz'); - expect(scope.isDone()).to.be.ok(); - - checkMails(1, done); + settings.setAutoupdatePattern(constants.AUTOUPDATE_PATTERN_NEVER, done); }); - }); - it('bad response offers whatever was last valid', function (done) { - nock.cleanAll(); + it('no updates', function (done) { + nock.cleanAll(); - var scope = nock('http://localhost:4444') - .get('/api/v1/boxupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken', automatic: false }) - .reply(404, { version: '2.0.0-pre.0', changelog: [''], sourceTarballUrl: 'box-pre.tar.gz' } ); + var scope = nock(MOCK_API_SERVER_ORIGIN) + .get('/api/v1/appupdate') + .query({ boxVersion: constants.VERSION, accessToken: APPSTORE_TOKEN, appId: APP.appStoreId, appVersion: APP.manifest.version, automatic: false }) + .reply(204, { } ); - updatechecker.checkForUpdates({ automatic: false }, function (error) { - expect(error).to.be.ok(); - expect(updatechecker.getUpdateInfo().box.version).to.be(UPDATE_VERSION); - expect(updatechecker.getUpdateInfo().box.sourceTarballUrl).to.be('box.tar.gz'); - expect(scope.isDone()).to.be.ok(); + updatechecker._checkAppUpdates({ automatic: false }, function (error) { + expect(!error).to.be.ok(); + expect(updatechecker.getUpdateInfo()).to.eql({}); + expect(scope.isDone()).to.be.ok(); + done(); + }); + }); - checkMails(0, done); - }); - }); -}); - -describe('updatechecker - app', function () { - var APP_0 = { - id: 'appid-0', - appStoreId: 'io.cloudron.app', - installationState: apps.ISTATE_PENDING_INSTALL, - error: null, - runState: 'running', - location: 'some-location-0', - domain: DOMAIN_0.domain, - manifest: { - version: '1.0.0', dockerImage: 'docker/app0', healthCheckPath: '/', httpPort: 80, title: 'app0', - tcpPorts: { - PORT: { - description: 'this is a port that i expose', - containerPort: '1234' - } - } - }, - containerId: null, - portBindings: { PORT: 5678 }, - healthy: null, - accessRestriction: null, - memoryLimit: 0, - mailboxName: 'mail', - mailboxDomain: DOMAIN_0.domain - }; - - before(function (done) { - mailer._mailQueue = []; - - async.series([ - database.initialize, - database._clear, - settings._setApiServerOrigin.bind(null, 'http://localhost:4444'), - cron.startJobs, - domains.add.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE), - settings.setDashboardLocation.bind(null, DOMAIN_0.domain, 'my.' + DOMAIN_0.domain), - users.createOwner.bind(null, USER_0.username, USER_0.password, USER_0.email, USER_0.displayName, AUDIT_SOURCE), - appdb.add.bind(null, APP_0.id, APP_0.appStoreId, APP_0.manifest, APP_0.location, APP_0.domain, apps._translatePortBindings(APP_0.portBindings, APP_0.manifest), APP_0), - settings.setAutoupdatePattern.bind(null, constants.AUTOUPDATE_PATTERN_NEVER), - settingsdb.set.bind(null, settings.CLOUDRON_TOKEN_KEY, 'atoken'), - ], done); - }); - - after(cleanup); - - it('no updates', function (done) { - nock.cleanAll(); - - var scope = nock('http://localhost:4444') - .get('/api/v1/appupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken', appId: APP_0.appStoreId, appVersion: APP_0.manifest.version, automatic: false }) - .reply(204, { } ); - - updatechecker._checkAppUpdates({ automatic: false }, function (error) { - expect(!error).to.be.ok(); - expect(updatechecker.getUpdateInfo()).to.eql({}); - expect(scope.isDone()).to.be.ok(); - - checkMails(0, done); - }); - }); - - it('bad response', function (done) { - nock.cleanAll(); - - var scope = nock('http://localhost:4444') - .get('/api/v1/appupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken', appId: APP_0.appStoreId, appVersion: APP_0.manifest.version, automatic: false }) - .reply(500, { update: { manifest: { version: '1.0.0', changelog: '* some changes' } } } ); - - updatechecker._checkAppUpdates({ automatic: false }, function (error) { - expect(!error).to.be.ok(); - expect(updatechecker.getUpdateInfo()).to.eql({}); - expect(scope.isDone()).to.be.ok(); - - checkMails(0, done); - }); - }); - - it('offers new version', function (done) { - nock.cleanAll(); - - var scope = nock('http://localhost:4444') - .get('/api/v1/appupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken', appId: APP_0.appStoreId, appVersion: APP_0.manifest.version, automatic: false }) - .reply(200, { manifest: { version: '2.0.0', changelog: '* some changes' } } ); - - updatechecker._checkAppUpdates({ automatic: false }, function (error) { - expect(!error).to.be.ok(); - expect(updatechecker.getUpdateInfo()).to.eql({ 'appid-0': { manifest: { version: '2.0.0', changelog: '* some changes' }, unstable: false } }); - expect(scope.isDone()).to.be.ok(); - - checkMails(1, done); - }); - }); - - it('does not offer old version', function (done) { - nock.cleanAll(); - - updatechecker._checkAppUpdates({ automatic: false }, function (error) { - expect(!error).to.be.ok(); - expect(updatechecker.getUpdateInfo()).to.eql({ }); - checkMails(0, done); + it('bad response', function (done) { + nock.cleanAll(); + + var scope = nock(MOCK_API_SERVER_ORIGIN) + .get('/api/v1/appupdate') + .query({ boxVersion: constants.VERSION, accessToken: APPSTORE_TOKEN, appId: APP.appStoreId, appVersion: APP.manifest.version, automatic: false }) + .reply(500, { update: { manifest: { version: '1.0.0', changelog: '* some changes' } } } ); + + updatechecker._checkAppUpdates({ automatic: false }, function (error) { + expect(!error).to.be.ok(); + expect(updatechecker.getUpdateInfo()).to.eql({}); + expect(scope.isDone()).to.be.ok(); + done(); + }); + }); + + it('offers new version', function (done) { + nock.cleanAll(); + + var scope = nock(MOCK_API_SERVER_ORIGIN) + .get('/api/v1/appupdate') + .query({ boxVersion: constants.VERSION, accessToken: APPSTORE_TOKEN, appId: APP.appStoreId, appVersion: APP.manifest.version, automatic: false }) + .reply(200, { manifest: { version: '2.0.0', changelog: '* some changes' } } ); + + updatechecker._checkAppUpdates({ automatic: false }, function (error) { + expect(!error).to.be.ok(); + expect(updatechecker.getUpdateInfo()).to.eql({ 'appid': { manifest: { version: '2.0.0', changelog: '* some changes' }, unstable: false } }); + expect(scope.isDone()).to.be.ok(); + done(); + }); + }); + + it('does not offer old version', function (done) { + nock.cleanAll(); + + updatechecker._checkAppUpdates({ automatic: false }, function (error) { + expect(!error).to.be.ok(); + expect(updatechecker.getUpdateInfo()).to.eql({ }); + done(); + }); }); }); }); diff --git a/src/updatechecker.js b/src/updatechecker.js index 3940036b1..50900fe2a 100644 --- a/src/updatechecker.js +++ b/src/updatechecker.js @@ -21,7 +21,7 @@ function setUpdateInfo(state) { // appid -> update info { creationDate, manifest } // box -> { version, changelog, upgrade, sourceTarballUrl } state.version = 2; - safe.fs.writeFileSync(paths.UPDATE_CHECKER_FILE, JSON.stringify(state, null, 4), 'utf8'); + if (!safe.fs.writeFileSync(paths.UPDATE_CHECKER_FILE, JSON.stringify(state, null, 4), 'utf8')) debug(`setUpdateInfo: Error writing to update checker file: ${safe.error.message}`); } function getUpdateInfo() { @@ -74,7 +74,7 @@ function checkBoxUpdates(options, callback) { debug('checkBoxUpdates: checking for updates'); - appstore.getBoxUpdate(options, function (error, updateInfo) { + appstore.getBoxUpdate(options, async function (error, updateInfo) { if (error) return callback(error); let state = getUpdateInfo(); @@ -90,7 +90,7 @@ function checkBoxUpdates(options, callback) { if (state.box && state.box.version === updateInfo.version) { debug(`checkBoxUpdates: Skipping notification of box update ${updateInfo.version} as user was already notified`); - return callback(); + return callback(null); } debug(`checkBoxUpdates: ${updateInfo.version} is available`); @@ -99,14 +99,13 @@ function checkBoxUpdates(options, callback) { const message = `Changelog:\n${changelog}\n\nGo to the settings view to update.\n\n`; - notifications.alert(notifications.ALERT_BOX_UPDATE, `Cloudron v${updateInfo.version} is available`, message, function (error) { - if (error) return callback(error); + [error] = await safe(notifications.alert(notifications.ALERT_BOX_UPDATE, `Cloudron v${updateInfo.version} is available`, message)); + if (error) return callback(error); - state.box = updateInfo; - setUpdateInfo(state); + state.box = updateInfo; + setUpdateInfo(state); - callback(); - }); + callback(null); }); }