Files
cloudron-box/src/test/apps-test.js
2021-09-21 10:13:06 -07:00

263 lines
11 KiB
JavaScript

/* global it:false */
/* global describe:false */
/* global before:false */
/* global after:false */
'use strict';
const apps = require('../apps.js'),
BoxError = require('../boxerror.js'),
common = require('./common.js'),
expect = require('expect.js'),
safe = require('safetydance');
describe('Apps', function () {
const { domainSetup, cleanup, app, admin, user } = common;
before(domainSetup);
after(cleanup);
describe('validatePortBindings', function () {
it('does not allow invalid host port', function () {
expect(apps._validatePortBindings({ port: -1 }, { tcpPorts: { port: 5000 } })).to.be.an(Error);
expect(apps._validatePortBindings({ port: 0 }, { tcpPorts: { port: 5000 } })).to.be.an(Error);
expect(apps._validatePortBindings({ port: 'text' }, { tcpPorts: { port: 5000 } })).to.be.an(Error);
expect(apps._validatePortBindings({ port: 65536 }, { tcpPorts: { port: 5000 } })).to.be.an(Error);
expect(apps._validatePortBindings({ port: 470 }, { tcpPorts: { port: 5000 } })).to.be.an(Error);
});
it('does not allow ports not as part of manifest', function () {
expect(apps._validatePortBindings({ port: 1567 }, { tcpPorts: { } })).to.be.an(Error);
expect(apps._validatePortBindings({ port: 1567 }, { tcpPorts: { port3: null } })).to.be.an(Error);
});
it('does not allow reserved ports', function () {
expect(apps._validatePortBindings({ port: 443 }, { tcpPorts: { port: 5000 } })).to.be.an(Error);
expect(apps._validatePortBindings({ port: 50000 }, { tcpPorts: { port: 5000 } })).to.be.an(Error);
expect(apps._validatePortBindings({ port: 51000 }, { tcpPorts: { port: 5000 } })).to.be.an(Error);
expect(apps._validatePortBindings({ port: 50100 }, { tcpPorts: { port: 5000 } })).to.be.an(Error);
});
it('allows valid bindings', function () {
expect(apps._validatePortBindings({ port: 1024 }, { tcpPorts: { port: 5000 } })).to.be(null);
expect(apps._validatePortBindings({
port1: 4033,
port2: 3242,
port3: 1234
}, { tcpPorts: { port1: null, port2: null, port3: null } })).to.be(null);
});
});
describe('validateAccessRestriction', function () {
it('allows null input', function () {
expect(apps._validateAccessRestriction(null)).to.eql(null);
});
it('does not allow wrong user type', function () {
expect(apps._validateAccessRestriction({ users: {} })).to.be.an(Error);
});
it('allows user input', function () {
expect(apps._validateAccessRestriction({ users: [] })).to.eql(null);
});
it('allows single user input', function () {
expect(apps._validateAccessRestriction({ users: [ 'someuserid' ] })).to.eql(null);
});
it('allows multi user input', function () {
expect(apps._validateAccessRestriction({ users: [ 'someuserid', 'someuserid1', 'someuserid2', 'someuserid3' ] })).to.eql(null);
});
});
describe('canAccess', function () {
const someuser = { id: 'someuser', groupIds: [], role: 'user' };
const adminuser = { id: 'adminuser', groupIds: [ 'groupie' ], role: 'admin' };
it('returns true for unrestricted access', function () {
expect(apps.canAccess({ accessRestriction: null }, someuser)).to.be(true);
});
it('returns true for allowed user', function () {
expect(apps.canAccess({ accessRestriction: { users: [ 'someuser' ] } }, someuser)).to.be(true);
});
it('returns true for allowed user with multiple allowed', function () {
expect(apps.canAccess({ accessRestriction: { users: [ 'foo', 'someuser', 'anotheruser' ] } }, someuser)).to.be(true);
});
it('returns false for not allowed user', function () {
expect(apps.canAccess({ accessRestriction: { users: [ 'foo' ] } }, someuser)).to.be(false);
});
it('returns false for not allowed user with multiple allowed', function () {
expect(apps.canAccess({ accessRestriction: { users: [ 'foo', 'anotheruser' ] } }, someuser)).to.be(false);
});
it('returns false for no group or user', function () {
expect(apps.canAccess({ accessRestriction: { users: [ ], groups: [ ] } }, someuser)).to.be(false);
});
it('returns false for invalid group or user', function () {
expect(apps.canAccess({ accessRestriction: { users: [ ], groups: [ 'nop' ] } }, someuser)).to.be(false);
});
it('returns true for admin user', function () {
expect(apps.canAccess({ accessRestriction: { users: [ ], groups: [ 'nop' ] } }, adminuser)).to.be(true);
});
});
describe('crud', function () {
it('cannot get invalid app', async function () {
const result = await apps.get('nope');
expect(result).to.be(null);
});
it('can add app', async function () {
await apps.add(app.id, app.appStoreId, app.manifest, app.location, app.domain, app.portBindings, app);
});
it('cannot add with same app id', async function () {
const [error] = await safe(apps.add(app.id, app.appStoreId, app.manifest, app.location, app.domain, app.portBindings, app));
expect(error.reason).to.be(BoxError.ALREADY_EXISTS);
});
it('cannot add with same app id', async function () {
const [error] = await safe(apps.add(app.id, app.appStoreId, app.manifest, app.location, app.domain, app.portBindings, app));
expect(error.reason).to.be(BoxError.ALREADY_EXISTS);
});
it('can get app', async function () {
const result = await apps.get(app.id);
expect(result.manifest).to.eql(app.manifest);
expect(result.portBindings).to.eql({});
expect(result.location).to.eql(app.location);
});
it('can list apps', async function () {
const result = await apps.list();
expect(result.length).to.be(1);
expect(result[0].manifest).to.eql(app.manifest);
expect(result[0].portBindings).to.eql({});
expect(result[0].location).to.eql(app.location);
});
it('can listByUser', async function () {
let result = await apps.listByUser(admin);
expect(result.length).to.be(1);
result = await apps.listByUser(user);
expect(result.length).to.be(1);
});
it('update succeeds', async function () {
const data = {
installationState: 'some-other-status',
location:'some-other-location',
domain: app.domain, // needs to be set whenever location is set
manifest: Object.assign({}, app.manifest, { version: '0.2.0' }),
accessRestriction: '',
memoryLimit: 1337,
cpuShares: 102,
};
await apps.update(app.id, data);
const newApp = await apps.get(app.id);
expect(newApp.installationState).to.be('some-other-status');
expect(newApp.location).to.be('some-other-location');
expect(newApp.manifest.version).to.be('0.2.0');
expect(newApp.accessRestriction).to.be('');
expect(newApp.memoryLimit).to.be(1337);
expect(newApp.cpuShares).to.be(102);
});
it('update of nonexisting app fails', async function () {
const [error] = await safe(apps.update('random', { installationState: app.installationState, location: app.location }));
expect(error.reason).to.be(BoxError.NOT_FOUND);
});
it('delete succeeds', async function () {
await apps.del(app.id);
});
it('cannot delete previously delete record', async function () {
const [error] = await safe(apps.del(app.id));
expect(error.reason).to.be(BoxError.NOT_FOUND);
});
});
describe('setHealth', function () {
before(async function () {
await apps.add(app.id, app.appStoreId, app.manifest, app.location, app.domain, app.portBindings, app);
});
it('can set app as healthy', async function () {
const result = await apps.get(app.id);
expect(result.health).to.be(null);
await apps.setHealth(app.id, apps.HEALTH_HEALTHY, new Date());
});
it('did set app as healthy', async function () {
const result = await apps.get(app.id);
expect(result.health).to.be(apps.HEALTH_HEALTHY);
});
it('cannot set health of unknown app', async function () {
const [error] = await safe(apps.setHealth('randomId', apps.HEALTH_HEALTHY, new Date()));
expect(error.reason).to.be(BoxError.NOT_FOUND);
});
});
describe('configureInstalledApps', function () {
const app1 = Object.assign({}, app, { id: 'id1', installationState: apps.ISTATE_ERROR, location: 'loc1' });
const app2 = Object.assign({}, app, { id: 'id2', installationState: apps.ISTATE_INSTALLED, location: 'loc2' });
before(async function () {
await apps.update(app.id, { installationState: apps.ISTATE_INSTALLED });
await apps.add(app1.id, app1.appStoreId, app1.manifest, app1.location, app1.domain, app1.portBindings, app1);
await apps.add(app2.id, app2.appStoreId, app2.manifest, app2.location, app2.domain, app2.portBindings, app2);
});
after(async function () {
await apps.del(app1.id);
await apps.del(app2.id);
});
it('can mark apps for reconfigure', async function () {
await apps.configureInstalledApps();
const result = await apps.list();
expect(result[0].installationState).to.be(apps.ISTATE_PENDING_CONFIGURE);
expect(result[1].installationState).to.be(apps.ISTATE_ERROR);
expect(result[2].installationState).to.be(apps.ISTATE_PENDING_CONFIGURE);
});
});
describe('restoreInstalledApps', function () {
const app1 = Object.assign({}, app, { id: 'id1', installationState: apps.ISTATE_ERROR, location: 'loc1' });
const app2 = Object.assign({}, app, { id: 'id2', installationState: apps.ISTATE_INSTALLED, location: 'loc2' });
before(async function () {
await apps.update(app.id, { installationState: apps.ISTATE_INSTALLED });
await apps.add(app1.id, app1.appStoreId, app1.manifest, app1.location, app1.domain, app1.portBindings, app1);
await apps.add(app2.id, app2.appStoreId, app2.manifest, app2.location, app2.domain, app2.portBindings, app2);
});
after(async function () {
await apps.del(app1.id);
await apps.del(app2.id);
});
it('can mark apps for reconfigure', async function () {
await apps.restoreInstalledApps({});
const result = await apps.list();
expect(result[0].installationState).to.be(apps.ISTATE_PENDING_INSTALL);
expect(result[1].installationState).to.be(apps.ISTATE_ERROR);
expect(result[2].installationState).to.be(apps.ISTATE_PENDING_INSTALL);
});
});
});