/* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ 'use strict'; const appdb = require('../appdb.js'), apps = require('../apps.js'), async = require('async'), BoxError = require('../boxerror.js'), database = require('../database'), domaindb = require('../domaindb'), expect = require('expect.js'), mailboxdb = require('../mailboxdb.js'), reverseProxy = require('../reverseproxy.js'), settingsdb = require('../settingsdb.js'), _ = require('underscore'); const DOMAIN_0 = { domain: 'foobar.com', zoneName: 'foobar.com', provider: 'digitalocean', config: { token: 'abcd' }, tlsConfig: { provider: 'fallback' }, wellKnown: null }; DOMAIN_0.fallbackCertificate = reverseProxy.generateFallbackCertificateSync(DOMAIN_0.domain); const DOMAIN_1 = { domain: 'foo.cloudron.io', zoneName: 'cloudron.io', provider: 'manual', config: null, tlsConfig: { provider: 'fallback' }, wellKnown: null }; DOMAIN_1.fallbackCertificate = reverseProxy.generateFallbackCertificateSync(DOMAIN_1.domain); describe('database', function () { before(function (done) { async.series([ database.initialize, database._clear ], done); }); after(function (done) { async.series([ database._clear, database.uninitialize ], done); }); describe('domains', function () { after(function (done) { database._clear(done); }); it('can add domain', function (done) { domaindb.add(DOMAIN_0.domain, DOMAIN_0, done); }); it('can add another domain', function (done) { domaindb.add(DOMAIN_1.domain, DOMAIN_1, done); }); it('cannot add same domain twice', function (done) { domaindb.add(DOMAIN_0.domain, DOMAIN_0, function (error) { expect(error).to.be.ok(); expect(error.reason).to.be(BoxError.ALREADY_EXISTS); done(); }); }); it('can get domain', function (done) { domaindb.get(DOMAIN_0.domain, function (error, result) { expect(error).to.equal(null); expect(result).to.be.an('object'); expect(result.domain).to.equal(DOMAIN_0.domain); expect(result.zoneName).to.equal(DOMAIN_0.zoneName); expect(result.config).to.eql(DOMAIN_0.config); done(); }); }); it('can update domain', function (done) { const newConfig = { provider: 'manual' }; const newTlsConfig = { provider: 'foobar' }; domaindb.update(DOMAIN_1.domain, { provider: DOMAIN_1.provider, config: newConfig, tlsConfig: newTlsConfig }, function (error) { expect(error).to.equal(null); domaindb.get(DOMAIN_1.domain, function (error, result) { expect(error).to.equal(null); expect(result).to.be.an('object'); expect(result.domain).to.equal(DOMAIN_1.domain); expect(result.zoneName).to.equal(DOMAIN_1.zoneName); expect(result.provider).to.equal(DOMAIN_1.provider); expect(result.config).to.eql(newConfig); expect(result.tlsConfig).to.eql(newTlsConfig); DOMAIN_1.config = newConfig; DOMAIN_1.tlsConfig = newTlsConfig; done(); }); }); }); it('can get all domains', function (done) { domaindb.getAll(function (error, result) { expect(error).to.equal(null); expect(result).to.be.an('array'); expect(result.length).to.equal(2); // sorted by domain expect(result[0].domain).to.equal(DOMAIN_1.domain); expect(result[0].zoneName).to.equal(DOMAIN_1.zoneName); expect(result[0].provider).to.equal(DOMAIN_1.provider); expect(result[0].config).to.eql(DOMAIN_1.config); expect(result[0].tlsConfig).to.eql(DOMAIN_1.tlsConfig); expect(result[1].domain).to.equal(DOMAIN_0.domain); expect(result[1].zoneName).to.equal(DOMAIN_0.zoneName); expect(result[1].provider).to.equal(DOMAIN_0.provider); expect(result[1].config).to.eql(DOMAIN_0.config); expect(result[1].tlsConfig).to.eql(DOMAIN_0.tlsConfig); done(); }); }); it('cannot delete non-existing domain', function (done) { domaindb.del('not.exists', function (error) { expect(error).to.be.a(BoxError); expect(error.reason).to.equal(BoxError.NOT_FOUND); done(); }); }); var APP_0 = { id: 'appid-0', appStoreId: 'appStoreId-0', installationState: apps.ISTATE_PENDING_INSTALL, error: null, runState: 'running', location: 'some-location-0', domain: DOMAIN_0.domain, manifest: { version: '0.1', dockerImage: 'docker/app0', healthCheckPath: '/', httpPort: 80, title: 'app0' }, containerId: null, containerIp: null, portBindings: { port: { hostPort: 5678, type: 'tcp' } }, health: null, accessRestriction: null, lastBackupId: null, memoryLimit: 4294967296, cpuShares: 1024, sso: true, debugMode: null, reverseProxyConfig: {}, enableBackup: true, env: {}, mailboxName: 'talktome', mailboxDomain: DOMAIN_0.domain, enableAutomaticUpdate: true, dataDir: null, tags: [], label: null, taskId: null, mounts: [], proxyAuth: false, servicesConfig: {}, hasIcon: false, hasAppStoreIcon: false }; it('cannot delete referenced domain', function (done) { appdb.add(APP_0.id, APP_0.appStoreId, APP_0.manifest, APP_0.location, APP_0.domain, APP_0.portBindings, APP_0, function (error) { expect(error).to.be(null); domaindb.del(DOMAIN_0.domain, function (error) { expect(error).to.be.a(BoxError); expect(error.reason).to.equal(BoxError.CONFLICT); appdb.del(APP_0.id, done); }); }); }); it('can delete existing domain', function (done) { domaindb.del(DOMAIN_0.domain, function (error) { expect(error).to.be(null); domaindb.get(DOMAIN_0.domain, function (error) { expect(error).to.be.a(BoxError); expect(error.reason).to.equal(BoxError.NOT_FOUND); done(); }); }); }); }); describe('apps', function () { var APP_0 = { id: 'appid-0', appStoreId: 'appStoreId-0', installationState: apps.ISTATE_PENDING_INSTALL, error: null, runState: 'running', location: 'some-location-0', domain: DOMAIN_0.domain, manifest: { version: '0.1', dockerImage: 'docker/app0', healthCheckPath: '/', httpPort: 80, title: 'app0' }, containerId: null, containerIp: null, portBindings: { port: { hostPort: 5678, type: 'tcp' } }, health: null, accessRestriction: null, memoryLimit: 4294967296, cpuShares: 256, sso: true, debugMode: null, reverseProxyConfig: {}, enableBackup: true, enableMailbox: true, alternateDomains: [], aliasDomains: [], env: { 'CUSTOM_KEY': 'CUSTOM_VALUE' }, mailboxName: 'talktome', mailboxDomain: DOMAIN_0.domain, enableAutomaticUpdate: true, dataDir: null, tags: [], label: null, taskId: null, mounts: [], proxyAuth: false, servicesConfig: {}, hasIcon: false, hasAppStoreIcon: false }; var APP_1 = { id: 'appid-1', appStoreId: 'appStoreId-1', installationState: apps.ISTATE_PENDING_INSTALL, // app health tests rely on this initial state error: null, runState: 'running', location: 'some-location-1', domain: DOMAIN_0.domain, manifest: { version: '0.2', dockerImage: 'docker/app1', healthCheckPath: '/', httpPort: 80, title: 'app1' }, containerId: null, containerIp: null, portBindings: { }, health: null, accessRestriction: { users: [ 'foobar' ] }, memoryLimit: 0, cpuShares: 512, sso: true, debugMode: null, reverseProxyConfig: {}, enableBackup: true, alternateDomains: [], aliasDomains: [], env: {}, enableMailbox: true, mailboxName: 'callme', mailboxDomain: DOMAIN_0.domain, enableAutomaticUpdate: true, dataDir: null, tags: [], label: null, taskId: null, mounts: [], proxyAuth: false, servicesConfig: {}, hasIcon: false, hasAppStoreIcon: false }; before(function (done) { async.series([ database._clear, domaindb.add.bind(null, DOMAIN_0.domain, DOMAIN_0) ], done); }); after(function (done) { database._clear(done); }); it('add fails due to missing arguments', function () { expect(function () { appdb.add(APP_0.id, APP_0.manifest, APP_0.installationState, function () {}); }).to.throwError(); expect(function () { appdb.add(APP_0.id, function () {}); }).to.throwError(); }); it('exists returns false', function (done) { appdb.exists(APP_0.id, function (error, exists) { expect(error).to.be(null); expect(exists).to.be(false); done(); }); }); it('add succeeds', function (done) { appdb.add(APP_0.id, APP_0.appStoreId, APP_0.manifest, APP_0.location, APP_0.domain, APP_0.portBindings, APP_0, function (error) { expect(error).to.be(null); done(); }); }); it('exists succeeds', function (done) { appdb.exists(APP_0.id, function (error, exists) { expect(error).to.be(null); expect(exists).to.be(true); done(); }); }); it('getPortBindings succeeds', function (done) { appdb.getPortBindings(APP_0.id, function (error, bindings) { expect(error).to.be(null); expect(bindings).to.be.an(Object); expect(bindings).to.be.eql({ port: { hostPort: '5678', type: 'tcp' } }); done(); }); }); it('add of same app fails', function (done) { appdb.add(APP_0.id, APP_0.appStoreId, APP_0.manifest, APP_0.location, APP_0.domain, [], APP_0, function (error) { expect(error).to.be.a(BoxError); expect(error.reason).to.be(BoxError.ALREADY_EXISTS); done(); }); }); it('get succeeds', function (done) { appdb.get(APP_0.id, function (error, result) { expect(error).to.be(null); expect(result).to.be.an('object'); expect(_.omit(result, ['creationTime', 'updateTime', 'ts', 'healthTime', 'resetTokenCreationTime'])).to.be.eql(APP_0); done(); }); }); it('get of nonexisting code fails', function (done) { appdb.get(APP_1.id, function (error, result) { expect(error).to.be.a(BoxError); expect(error.reason).to.be(BoxError.NOT_FOUND); expect(result).to.not.be.ok(); done(); }); }); it('update succeeds', function (done) { APP_0.installationState = 'some-other-status'; APP_0.location = 'some-other-location'; APP_0.manifest.version = '0.2'; APP_0.accessRestriction = ''; APP_0.memoryLimit = 1337; APP_0.cpuShares = 1024; var data = { installationState: APP_0.installationState, location: APP_0.location, domain: APP_0.domain, manifest: APP_0.manifest, accessRestriction: APP_0.accessRestriction, memoryLimit: APP_0.memoryLimit, cpuShares: APP_0.cpuShares }; appdb.update(APP_0.id, data, function (error) { expect(error).to.be(null); appdb.get(APP_0.id, function (error, result) { expect(error).to.be(null); expect(result).to.be.an('object'); expect(_.omit(result, ['creationTime', 'updateTime', 'ts', 'healthTime','resetTokenCreationTime'])).to.be.eql(APP_0); done(); }); }); }); it('update of nonexisting app fails', function (done) { appdb.update(APP_1.id, { installationState: APP_1.installationState, location: APP_1.location }, function (error) { expect(error).to.be.a(BoxError); expect(error.reason).to.be(BoxError.NOT_FOUND); done(); }); }); it('add second app succeeds', function (done) { appdb.add(APP_1.id, APP_1.appStoreId, APP_1.manifest, APP_1.location, APP_1.domain, [], APP_1, function (error) { expect(error).to.be(null); done(); }); }); it('getAll succeeds', function (done) { appdb.getAll(function (error, result) { expect(error).to.be(null); expect(result).to.be.an(Array); expect(result.length).to.be(2); expect(_.omit(result[0], ['creationTime', 'updateTime','ts', 'healthTime', 'resetTokenCreationTime'])).to.be.eql(APP_0); expect(_.omit(result[1], ['creationTime', 'updateTime','ts', 'healthTime', 'resetTokenCreationTime'])).to.be.eql(APP_1); done(); }); }); it('getAppStoreIds succeeds', function (done) { appdb.getAppStoreIds(function (error, results) { expect(error).to.be(null); expect(results).to.be.an(Array); expect(results.length).to.be(2); expect(results[0].appStoreId).to.equal(APP_0.appStoreId); expect(results[1].appStoreId).to.equal(APP_1.appStoreId); done(); }); }); it('delete succeeds', function (done) { appdb.del(APP_0.id, function (error) { expect(error).to.be(null); done(); }); }); it('getPortBindings should be empty', function (done) { appdb.getPortBindings(APP_0.id, function (error, bindings) { expect(error).to.be(null); expect(bindings).to.be.an(Object); expect(bindings).to.be.eql({ }); done(); }); }); it('cannot delete previously delete record', function (done) { appdb.del(APP_0.id, function (error) { expect(error).to.be.a(BoxError); expect(error.reason).to.be(BoxError.NOT_FOUND); done(); }); }); it('can set app as healthy', function (done) { appdb.setHealth(APP_1.id, apps.HEALTH_HEALTHY, new Date(), function (error) { expect(error).to.be(null); done(); }); }); it('cannot set health of unknown app', function (done) { appdb.setHealth('randomId', apps.HEALTH_HEALTHY, new Date(), function (error) { expect(error).to.be.ok(); done(); }); }); it('return empty addon config array for invalid app', function (done) { appdb.getAddonConfigByAppId('randomid', function (error, results) { expect(error).to.be(null); expect(results).to.eql([ ]); done(); }); }); it('setAddonConfig succeeds', function (done) { appdb.setAddonConfig(APP_1.id, 'addonid1', [ { name: 'ENV1', value: 'env' }, { name: 'ENV2', value: 'env2' } ], function (error) { expect(error).to.be(null); done(); }); }); it('setAddonConfig succeeds', function (done) { appdb.setAddonConfig(APP_1.id, 'addonid2', [ { name: 'ENV3', value: 'env' } ], function (error) { expect(error).to.be(null); done(); }); }); it('getAddonConfig succeeds', function (done) { appdb.getAddonConfig(APP_1.id, 'addonid1', function (error, results) { expect(error).to.be(null); expect(results).to.eql([ { name: 'ENV1', value: 'env' }, { name: 'ENV2', value: 'env2' } ]); done(); }); }); it('getAddonConfigByAppId succeeds', function (done) { appdb.getAddonConfigByAppId(APP_1.id, function (error, results) { expect(error).to.be(null); expect(results).to.eql([ { name: 'ENV1', value: 'env' }, { name: 'ENV2', value: 'env2' }, { name: 'ENV3', value: 'env' } ]); done(); }); }); it('getAddonConfigByName succeeds', function (done) { appdb.getAddonConfigByName(APP_1.id, 'addonid1', 'ENV2', function (error, value) { expect(error).to.be(null); expect(value).to.be('env2'); done(); }); }); it('getAddonConfigByName of unknown value succeeds', function (done) { appdb.getAddonConfigByName(APP_1.id, 'addonid1', 'NOPE', function (error) { expect(error.reason).to.be(BoxError.NOT_FOUND); done(); }); }); it('unsetAddonConfig succeeds', function (done) { appdb.unsetAddonConfig(APP_1.id, 'addonid1', function (error) { expect(error).to.be(null); done(); }); }); it('unsetAddonConfig did remove configs', function (done) { appdb.getAddonConfigByAppId(APP_1.id, function (error, results) { expect(error).to.be(null); expect(results).to.eql([ { name: 'ENV3', value: 'env' }]); done(); }); }); it('unsetAddonConfigByAppId succeeds', function (done) { appdb.unsetAddonConfigByAppId(APP_1.id, function (error) { expect(error).to.be(null); done(); }); }); it('unsetAddonConfigByAppId did remove configs', function (done) { appdb.getAddonConfigByAppId(APP_1.id, function (error, results) { expect(error).to.be(null); expect(results).to.eql([ ]); done(); }); }); }); describe('settings', function () { it('can set value', function (done) { settingsdb.set('somekey', 'somevalue', function (error) { expect(error).to.be(null); done(); }); }); it('can get the set value', function (done) { settingsdb.get('somekey', function (error, value) { expect(error).to.be(null); expect(value).to.be('somevalue'); done(); }); }); it('can get all values', function (done) { settingsdb.getAll(function (error, result) { expect(error).to.be(null); expect(result).to.be.an(Array); expect(result[0].name).to.be('somekey'); expect(result[0].value).to.be('somevalue'); expect(result.length).to.be(1); // the value set above done(); }); }); it('can update a value', function (done) { settingsdb.set('somekey', 'someothervalue', function (error) { expect(error).to.be(null); done(); }); }); it('can get updated value', function (done) { settingsdb.get('somekey', function (error, value) { expect(error).to.be(null); expect(value).to.be('someothervalue'); done(); }); }); }); describe('importFromFile', function () { before(function (done) { async.series([ database.initialize, database._clear ], done); }); it('cannot import from non-existent file', function (done) { database.importFromFile('/does/not/exist', function (error) { expect(error).to.be.ok(); done(); }); }); it('can export to file', function (done) { // arch only has maria db which lacks some mysqldump options we need, this is only here to allow running the tests :-/ if (require('child_process').execSync('/usr/bin/mysqldump --version').toString().indexOf('MariaDB') !== -1) return done(); database.exportToFile('/tmp/box.mysqldump', function (error) { expect(error).to.be(null); done(); }); }); it('can import from file', function (done) { // arch only has maria db which lacks some mysqldump options we need, this is only here to allow running the tests :-/ if (require('child_process').execSync('/usr/bin/mysqldump --version').toString().indexOf('MariaDB') !== -1) return done(); database.importFromFile('/tmp/box.mysqldump', function (error) { expect(error).to.be(null); done(); }); }); }); describe('mailboxes', function () { before(function (done) { async.series([ domaindb.add.bind(null, DOMAIN_0.domain, DOMAIN_0), ], done); }); after(function (done) { database._clear(done); }); it('add user mailbox succeeds', function (done) { mailboxdb.addMailbox('girish', DOMAIN_0.domain, { ownerId: 'uid-0', ownerType: 'user', active: true }, function (error) { expect(error).to.be(null); done(); }); }); it('cannot add dup entry', function (done) { mailboxdb.addMailbox('girish', DOMAIN_0.domain, { ownerId: 'uid-1', ownerType: 'group', active: true }, function (error) { expect(error.reason).to.be(BoxError.ALREADY_EXISTS); done(); }); }); it('add app mailbox succeeds', function (done) { mailboxdb.addMailbox('support', DOMAIN_0.domain, { ownerId: 'osticket', ownerType: 'user', active: true}, function (error) { expect(error).to.be(null); done(); }); }); it('get succeeds', function (done) { mailboxdb.getMailbox('support', DOMAIN_0.domain, function (error, mailbox) { expect(error).to.be(null); expect(mailbox.name).to.equal('support'); expect(mailbox.ownerId).to.equal('osticket'); expect(mailbox.domain).to.equal(DOMAIN_0.domain); expect(mailbox.creationTime).to.be.a(Date); done(); }); }); it('list mailboxes succeeds', function (done) { mailboxdb.listMailboxes(DOMAIN_0.domain, null /* search */, 1, 10, function (error, mailboxes) { expect(error).to.be(null); expect(mailboxes.length).to.be(2); expect(mailboxes[0].name).to.be('girish'); expect(mailboxes[1].name).to.be('support'); done(); }); }); it('can set alias', function (done) { mailboxdb.setAliasesForName('support', DOMAIN_0.domain, [ { name: 'support2', domain: DOMAIN_0.domain }, { name: 'help', domain: DOMAIN_0.domain } ], function (error) { expect(error).to.be(null); done(); }); }); it('list all mailboxes succeeds', function (done) { mailboxdb.listAllMailboxes(1, 10, function (error, mailboxes) { expect(error).to.be(null); expect(mailboxes.length).to.be(2); expect(mailboxes[0].name).to.be('girish'); expect(mailboxes[1].name).to.be('support'); expect(mailboxes[1].domain).to.be(DOMAIN_0.domain); done(); }); }); it('can get aliases of name', function (done) { mailboxdb.getAliasesForName('support', DOMAIN_0.domain, function (error, results) { expect(error).to.be(null); expect(results.length).to.be(2); expect(results[0].name).to.be('help'); expect(results[0].domain).to.be(DOMAIN_0.domain); expect(results[1].name).to.be('support2'); expect(results[1].domain).to.be(DOMAIN_0.domain); done(); }); }); it('can get alias', function (done) { mailboxdb.getAlias('support2', DOMAIN_0.domain, function (error, result) { expect(error).to.be(null); expect(result.name).to.be('support2'); expect(result.aliasName).to.be('support'); expect(result.aliasDomain).to.be(DOMAIN_0.domain); done(); }); }); it('can get by owner id', function (done) { mailboxdb.getByOwnerId('osticket', function (error, results) { expect(error).to.be(null); expect(results.length).to.be(3); expect(results[0].name).to.be('help'); expect(results[1].name).to.be('support'); expect(results[2].name).to.be('support2'); done(); }); }); it('cannot get non-existing group', function (done) { mailboxdb.getList('random', DOMAIN_0.domain, function (error) { expect(error.reason).to.be(BoxError.NOT_FOUND); done(); }); }); it('can change name', function (done) { mailboxdb.updateName('support', DOMAIN_0.domain, 'support3', DOMAIN_0.domain, function (error) { expect(error).to.be(null); mailboxdb.updateName('support3', DOMAIN_0.domain, 'support', DOMAIN_0.domain, done); }); }); it('cannot change name to existing one', function (done) { mailboxdb.updateName('support', DOMAIN_0.domain, 'support2', DOMAIN_0.domain, function (error) { expect(error).to.be.ok(); expect(error.reason).to.eql(BoxError.ALREADY_EXISTS); done(); }); }); it('unset aliases', function (done) { mailboxdb.setAliasesForName('support', DOMAIN_0.domain, [], function (error) { expect(error).to.be(null); mailboxdb.getAliasesForName('support', DOMAIN_0.domain, function (error, results) { expect(error).to.be(null); expect(results.length).to.be(0); done(); }); }); }); it('del succeeds', function (done) { mailboxdb.del('girish', DOMAIN_0.domain, function (error) { expect(error).to.be(null); done(); }); }); it('del by ownerId succeeds', function (done) { mailboxdb.delByOwnerId('osticket', function (error) { expect(error).to.be(null); mailboxdb.getByOwnerId('osticket', function (error) { expect(error).to.be.ok(); expect(error.reason).to.be(BoxError.NOT_FOUND); done(); }); }); }); }); });