1
0
Fork 0
arangodb/tests/js/client/shell/shell-foxx-api-spec.js

1077 lines
44 KiB
JavaScript

/* global arango, describe, beforeEach, afterEach, it VPACK_TO_V8 */
'use strict';
const expect = require('chai').expect;
var utils = require('@arangodb/foxx/manager-utils');
const FoxxManager = require('@arangodb/foxx/manager');
const request = require('@arangodb/request');
const util = require('@arangodb/util');
const fs = require('fs');
const internal = require('internal');
const path = require('path');
const basePath = path.resolve(internal.pathForTesting('common'), 'test-data', 'apps', 'headers');
const arangodb = require('@arangodb');
const arango = require('@arangodb').arango;
const errors = arangodb.errors;
const db = arangodb.db;
const aql = arangodb.aql;
var origin = arango.getEndpoint().replace(/\+vpp/, '').replace(/^tcp:/, 'http:').replace(/^ssl:/, 'https:').replace(/^vst:/, 'http:');
function loadFoxxIntoZip(path) {
let zip = utils.zipDirectory(path);
let content = fs.readFileSync(zip);
fs.remove(zip);
return {
type: 'inlinezip',
buffer: content
};
}
function installFoxx(mountpoint, which) {
let headers = {};
let content;
if (which.type === 'js') {
headers['content-type'] = 'application/javascript';
content = which.buffer;
} else if (which.type === 'dir') {
headers['content-type'] = 'application/zip';
var utils = require('@arangodb/foxx/manager-utils');
let zip = utils.zipDirectory(which.buffer);
content = fs.readFileSync(zip);
fs.remove(zip);
} else if (which.type === 'inlinezip') {
content = which.buffer;
headers['content-type'] = 'application/zip';
} else if (which.type === 'url') {
content = { source: which };
} else if (which.type === 'file') {
content = fs.readFileSync(which.buffer);
}
let devmode = '';
if (which.hasOwnProperty('devmode') && which.devmode === true) {
devmode = '&development=true';
}
const crudResp = arango.POST('/_api/foxx?mount=' + mountpoint + devmode, content, headers);
expect(crudResp).to.have.property('manifest');
}
function deleteFox(mountpoint) {
const deleteResp = arango.DELETE('/_api/foxx/service?force=true&mount=' + mountpoint);
expect(deleteResp).to.have.property('code');
expect(deleteResp.code).to.equal(204);
expect(deleteResp.error).to.equal(false);
}
describe('FoxxApi commit', function () {
const mount = '/test';
beforeEach(function () {
try {
FoxxManager.uninstall(mount, {force: true});
} catch (e) {}
FoxxManager.install(basePath, mount);
});
afterEach(function () {
try {
FoxxManager.uninstall(mount, {force: true});
} catch (e) {}
});
it('should fix missing service definition', function () {
db._query(aql`
FOR service IN _apps
FILTER service.mount == ${mount}
REMOVE service IN _apps
`);
let result = arango.GET('/test/header-echo');
expect(result.code).to.equal(404);
result = arango.POST('/_api/foxx/commit', '');
expect(result.code).to.equal(204);
result = arango.GET_RAW('/test/header-echo', { origin: origin});
expect(result.code).to.equal(200);
if (arango.getEndpoint().match(/^vst:/)) {
// GeneralServer/HttpCommTask.cpp handles this, not implemented for vst
let body = VPACK_TO_V8(result.body);
expect(body['origin']).to.equal(origin);
} else {
expect(result.headers['access-control-allow-origin']).to.equal(origin);
}
});
it('should fix missing checksum', function () {
db._query(aql`
FOR service IN _apps
FILTER service.mount == ${mount}
UPDATE service WITH { checksum: null } IN _apps
`);
let checksum = db._query(aql`
FOR service IN _apps
FILTER service.mount == ${mount}
RETURN service.checksum
`).next();
expect(checksum).to.equal(null);
const result = arango.POST('/_api/foxx/commit', '');
expect(result.code).to.equal(204);
checksum = db._query(aql`
FOR service IN _apps
FILTER service.mount == ${mount}
RETURN service.checksum
`).next();
expect(checksum).to.not.equal(null);
});
it('without param replace should not fix wrong checksum', function () {
db._query(aql`
FOR service IN _apps
FILTER service.mount == ${mount}
UPDATE service WITH { checksum: '1234' } IN _apps
`);
let checksum = db._query(aql`
FOR service IN _apps
FILTER service.mount == ${mount}
RETURN service.checksum
`).next();
expect(checksum).to.equal('1234');
const result = arango.POST('/_api/foxx/commit', '');
expect(result.code).to.equal(204);
checksum = db._query(aql`
FOR service IN _apps
FILTER service.mount == ${mount}
RETURN service.checksum
`).next();
expect(checksum).to.equal('1234');
});
it('with param replace should fix wrong checksum', function () {
db._query(aql`
FOR service IN _apps
FILTER service.mount == ${mount}
UPDATE service WITH { checksum: '1234' } IN _apps
`);
let checksum = db._query(aql`
FOR service IN _apps
FILTER service.mount == ${mount}
RETURN service.checksum
`).next();
expect(checksum).to.equal('1234');
const result = arango.POST('/_api/foxx/commit?replace=true', '');
expect(result.code).to.equal(204);
checksum = db._query(aql`
FOR service IN _apps
FILTER service.mount == ${mount}
RETURN service.checksum
`).next();
expect(checksum).to.not.equal('1234');
});
it('should fix missing bundle', function () {
db._query(aql`
FOR bundle IN _appbundles
REMOVE bundle IN _appbundles
`);
let bundles = db._query(aql`
FOR bundle IN _appbundles
RETURN 1
`).toArray().length;
expect(bundles).to.equal(0);
const result = arango.POST('/_api/foxx/commit', { qs: { replace: true } });
expect(result.code).to.equal(204);
bundles = db._query(aql`
FOR bundle IN _appbundles
RETURN 1
`).toArray().length;
expect(bundles).to.equal(1);
});
});
describe('Foxx service', () => {
const mount = '/foxx-crud-test';
const minimalWorkingServicePath = path.resolve(internal.pathForTesting('common'), 'test-data', 'apps', 'minimal-working-service');
const minimalWorkingZip = loadFoxxIntoZip(minimalWorkingServicePath);
const minimalWorkingZipDev = {
buffer: minimalWorkingZip.buffer,
devmode: true,
type: minimalWorkingZip.type
};
const minimalWorkingZipPath = utils.zipDirectory(minimalWorkingServicePath);
const itzpapalotlPath = path.resolve(internal.pathForTesting('common'), 'test-data', 'apps', 'itzpapalotl');
const itzpapalotlZip = loadFoxxIntoZip(itzpapalotlPath);
const serviceServiceMount = '/foxx-crud-test-download';
const serviceServicePath = path.resolve(internal.pathForTesting('common'), 'test-data', 'apps', 'service-service', 'index.js');
const crudTestServiceSource = {
type: 'js',
buffer: fs.readFileSync(serviceServicePath)
};
beforeEach(() => {
installFoxx(serviceServiceMount, crudTestServiceSource);
});
afterEach(() => {
try {
deleteFox(serviceServiceMount);
} catch (e) {}
});
afterEach(() => {
try {
deleteFox(mount);
} catch (e) {}
});
const cases = [
{
name: 'localJsFile',
request: {
body: {source: path.resolve(minimalWorkingServicePath, 'index.js')},
json: true
}
},
{
name: 'localZipFile',
request: {
body: {source: minimalWorkingZipPath},
json: true
}
},
{
name: 'localDir',
request: {
body: {source: minimalWorkingServicePath},
json: true
}
},
{
name: 'jsBuffer',
request: {
body: fs.readFileSync(path.resolve(minimalWorkingServicePath, 'index.js')),
contentType: 'application/javascript'
}
},
{
name: 'zipBuffer',
request: {
body: fs.readFileSync(minimalWorkingZipPath),
contentType: 'application/zip'
}
},
{
name: 'remoteJsFile',
request: {
body: {source: `${origin}/_db/${db._name()}${serviceServiceMount}/js`},
json: true
}
},
{
name: 'remoteZipFile',
request: {
body: {source: `${origin}/_db/${db._name()}${serviceServiceMount}/zip`},
json: true
}
}
];
for (const c of cases) {
it(`installed via ${c.name} should be available`, () => {
let headers = {};
if (c.request.hasOwnProperty('contentType')) {
headers['content-type'] = c.request.contentType;
}
const installResp = arango.POST('/_api/foxx?mount=' + mount + '&foo=bar', c.request.body, headers);
expect(installResp).to.have.property('manifest');
const resp = arango.GET(mount);
expect(resp).to.eql({hello: 'world'});
});
it(`replaced via ${c.name} should be available`, () => {
installFoxx(mount, itzpapalotlZip);
let headers = {};
if (c.request.hasOwnProperty('contentType')) {
headers['content-type'] = c.request.contentType;
}
const replaceResp = arango.PUT('/_api/foxx/service?mount=' + mount, c.request.body, headers);
expect(replaceResp).to.have.property('manifest');
const resp = arango.GET(mount);
expect(resp).to.eql({hello: 'world'});
});
it(`upgrade via ${c.name} should be available`, () => {
installFoxx(mount, itzpapalotlZip);
let headers = {};
if (c.request.hasOwnProperty('contentType')) {
headers['content-type'] = c.request.contentType;
}
const upgradeResp = arango.PATCH('/_api/foxx/service?mount=' + mount, c.request.body, headers);
expect(upgradeResp).to.have.property('manifest');
const resp = arango.GET(mount);
expect(resp).to.eql({hello: 'world'});
});
}
it('uninstalled should not be available', () => {
installFoxx(mount, minimalWorkingZip);
const delResp = arango.DELETE('/_api/foxx/service?mount=' + mount);
expect(delResp.code).to.equal(204);
expect(delResp.error).to.equal(false);
const resp = arango.GET(mount);
expect(resp.code).to.equal(404);
});
const badMainServicePath = path.resolve(internal.pathForTesting('common'), 'test-data', 'apps', 'fails-on-mount');
it('failing on mount should not be installed', () => {
const installResp = arango.POST('/_api/foxx?mount=' + mount, { source: badMainServicePath });
expect(installResp.code).to.equal(400);
expect(installResp).to.have.property('error', true);
expect(installResp).to.have.property('errorNum', errors.ERROR_MODULE_FAILURE.code);
});
it('failing on mount should successfully upgrade', () => {
installFoxx(mount, minimalWorkingZip);
const upgradeResp = arango.PATCH('/_api/foxx/service?mount=' + mount, {source: badMainServicePath});
expect(upgradeResp).to.have.property('manifest');
const resp = arango.GET(mount);
expect(resp.code).to.equal(503);
expect(resp).to.have.property('error', true);
expect(resp).to.have.property('errorNum', errors.ERROR_HTTP_SERVICE_UNAVAILABLE.code);
});
it('failing on mount should successfully replace', () => {
installFoxx(mount, minimalWorkingZip);
const upgradeResp = arango.PUT('/_api/foxx/service?mount=' + mount, {source: badMainServicePath});
expect(upgradeResp).to.have.property('manifest');
const resp = arango.GET(mount);
expect(resp.code).to.equal(503);
expect(resp).to.have.property('error', true);
expect(resp).to.have.property('errorNum', errors.ERROR_HTTP_SERVICE_UNAVAILABLE.code);
});
const confPath = path.resolve(internal.pathForTesting('common'), 'test-data', 'apps', 'with-configuration');
it('empty configuration should be available', () => {
installFoxx(mount, minimalWorkingZip);
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount);
expect(resp).to.eql({});
});
it('empty non-minimal configuration should be available', () => {
installFoxx(mount, minimalWorkingZip);
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount + '&minimal=false');
expect(resp).to.eql({});
});
it('empty minimal configuration should be available', () => {
installFoxx(mount, minimalWorkingZip);
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount + '&minimal=true');
expect(resp).to.eql({});
});
it('configuration should be available', () => {
installFoxx(mount, {type: 'dir', buffer: confPath});
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount);
expect(resp).to.have.property('test1');
expect(resp.test1).to.not.have.property('current');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('non-minimal configuration should be available', () => {
installFoxx(mount, {type: 'dir', buffer: confPath});
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount + '&minimal=false');
expect(resp).to.have.property('test1');
expect(resp.test1).to.not.have.property('current');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('minimal configuration should be available', () => {
installFoxx(mount, {type: 'dir', buffer: confPath});
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount + '&minimal=true');
expect(resp).to.eql({});
});
it('configuration should be available after update', () => {
installFoxx(mount, {type: 'dir', buffer: confPath});
const updateResp = arango.PATCH('/_api/foxx/configuration?mount=' + mount,
{ test1: 'test'});
expect(updateResp).to.have.property('values');
expect(updateResp.values).to.have.property('test1', 'test');
expect(updateResp.values).to.not.have.property('test2');
expect(updateResp).to.not.have.property('warnings');
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount);
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', 'test');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('non-minimal configuration should be available after update', () => {
installFoxx(mount, {type: 'dir', buffer: confPath});
const updateResp = arango.PATCH('/_api/foxx/configuration?mount=' + mount + '&minimal=false', { test1: 'test'});
expect(updateResp).to.have.property('test1');
expect(updateResp.test1).to.have.property('current', 'test');
expect(updateResp.test1).to.not.have.property('warning');
expect(updateResp).to.have.property('test2');
expect(updateResp.test2).to.not.have.property('current');
expect(updateResp.test2).to.not.have.property('warning');
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount + '&minimal=false');
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', 'test');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('minimal configuration should be available after update', () => {
installFoxx(mount, {type: 'dir', buffer: confPath});
const updateResp = arango.PATCH('/_api/foxx/configuration?mount=' + mount + '&minimal=true', {
test1: 'test'
});
expect(updateResp).to.have.property('values');
expect(updateResp.values).to.have.property('test1', 'test');
expect(updateResp.values).to.not.have.property('test2');
expect(updateResp).to.not.have.property('warnings');
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount + '&minimal=true');
expect(resp).to.have.property('test1', 'test');
expect(resp).to.not.have.property('test2');
});
it('configuration should be available after replace', () => {
installFoxx(mount, {type: 'dir', buffer: confPath});
const replaceResp = arango.PUT('/_api/foxx/configuration?mount=' + mount, {
test1: 'test'
});
expect(replaceResp).to.have.property('values');
expect(replaceResp.values).to.have.property('test1', 'test');
expect(replaceResp.values).to.not.have.property('test2');
expect(replaceResp).to.have.property('warnings');
expect(replaceResp.warnings).to.have.property('test2', 'is required');
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount);
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', 'test');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('non-minimal configuration should be available after replace', () => {
installFoxx(mount, {type: 'dir', buffer: confPath});
const replaceResp = arango.PUT('/_api/foxx/configuration?mount=' + mount + '&minimal=false', {
test1: 'test'
});
expect(replaceResp).to.have.property('test1');
expect(replaceResp.test1).to.have.property('current', 'test');
expect(replaceResp.test1).to.not.have.property('warning');
expect(replaceResp).to.have.property('test2');
expect(replaceResp.test2).to.not.have.property('current');
expect(replaceResp.test2).to.have.property('warning', 'is required');
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount);
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', 'test');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('minimal configuration should be available after replace', () => {
installFoxx(mount, {type: 'dir', buffer: confPath});
const replaceResp = arango.PUT('/_api/foxx/configuration?mount=' + mount + '&minimal=true', {
test1: 'test'
});
expect(replaceResp).to.have.property('values');
expect(replaceResp.values).to.have.property('test1', 'test');
expect(replaceResp.values).to.not.have.property('test2');
expect(replaceResp).to.have.property('warnings');
expect(replaceResp.warnings).to.have.property('test2', 'is required');
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount + '&minimal=true');
expect(resp).to.have.property('test1', 'test');
expect(resp).to.not.have.property('test2');
});
it('configuration should be merged after update', () => {
installFoxx(mount, {type: 'dir', buffer: confPath});
const replaceResp = arango.PUT('/_api/foxx/configuration?mount=' + mount, {
test2: 'test2'
});
const updateResp = arango.PATCH('/_api/foxx/configuration?mount=' + mount, {
test1: 'test1'
});
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount);
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', 'test1');
expect(resp).to.have.property('test2');
expect(resp.test2).to.have.property('current', 'test2');
});
it('non-minimal configuration should be merged after update', () => {
installFoxx(mount, {type: 'dir', buffer: confPath});
const replaceResp = arango.PUT('/_api/foxx/configuration?mount=' + mount + '&minimal=false', {
test2: 'test2'
});
const updateResp = arango.PATCH('/_api/foxx/configuration?mount=' + mount + '&minimal=false', {
test1: 'test1'
});
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount + '&minimal=false');
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', 'test1');
expect(resp).to.have.property('test2');
expect(resp.test2).to.have.property('current', 'test2');
});
it('minimal configuration should be merged after update', () => {
installFoxx(mount, {type: 'dir', buffer: confPath});
const replaceResp = arango.PUT('/_api/foxx/configuration?mount=' + mount + '&minimal=true', {
test2: 'test2'
});
const updateResp = arango.PATCH('/_api/foxx/configuration?mount=' + mount + '&minimal=true', {
test1: 'test1'
});
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount + '&minimal=true');
expect(resp).to.have.property('test1', 'test1');
expect(resp).to.have.property('test2', 'test2');
});
it('configuration should be overwritten after replace', () => {
installFoxx(mount, {type: 'dir', buffer: confPath});
const updateResp = arango.PATCH('/_api/foxx/configuration?mount=' + mount, {
test2: 'test2'
});
const replaceResp = arango.PUT('/_api/foxx/configuration?mount=' + mount, {
test1: 'test'
});
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount);
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', 'test');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('non-minimal configuration should be overwritten after replace', () => {
installFoxx(mount, {type: 'dir', buffer: confPath});
const updateResp = arango.PATCH('/_api/foxx/configuration?mount=' + mount + '&minimal=false', {
test2: 'test2'
});
const replaceResp = arango.PUT('/_api/foxx/configuration?mount=' + mount + '&minimal=false', {
test1: 'test'
});
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount + '&minimal=false');
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', 'test');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('minimal configuration should be overwritten after replace', () => {
installFoxx(mount, {type: 'dir', buffer: confPath});
const updateResp = arango.PATCH('/_api/foxx/configuration?mount=' + mount + '&minimal=true', {
test2: 'test2'
});
const replaceResp = arango.PUT('/_api/foxx/configuration?mount=' + mount + '&minimal=true', {
test1: 'test'
});
const resp = arango.GET('/_api/foxx/configuration?mount=' + mount + '&minimal=true');
expect(resp).to.have.property('test1', 'test');
expect(resp).not.to.have.property('test2');
});
const depPath = path.resolve(internal.pathForTesting('common'), 'test-data', 'apps', 'with-dependencies');
it('empty configuration should be available', () => {
installFoxx(mount, {type: 'dir', buffer: minimalWorkingServicePath});
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount);
expect(resp).to.eql({});
});
it('empty non-minimal configuration should be available', () => {
installFoxx(mount, {type: 'dir', buffer: minimalWorkingServicePath});
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount + '&minimal=false');
expect(resp).to.eql({});
});
it('empty minimal configuration should be available', () => {
installFoxx(mount, {type: 'dir', buffer: minimalWorkingServicePath});
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount + '&minimal=true');
expect(resp).to.eql({});
});
it('dependencies should be available', () => {
installFoxx(mount, {type: 'dir', buffer: depPath});
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount);
expect(resp).to.have.property('test1');
expect(resp.test1).to.not.have.property('current');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('non-minimal dependencies should be available', () => {
installFoxx(mount, {type: 'dir', buffer: depPath});
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount + '&minimal=false');
expect(resp).to.have.property('test1');
expect(resp.test1).to.not.have.property('current');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('minimal dependencies should be available', () => {
installFoxx(mount, {type: 'dir', buffer: depPath});
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount + '&minimal=true');
expect(resp).to.eql({});
});
it('dependencies should be available after update', () => {
installFoxx(mount, {type: 'dir', buffer: depPath});
const updateResp = arango.PATCH('/_api/foxx/dependencies?mount=' + mount, {
test1: '/test'
});
expect(updateResp).to.have.property('values');
expect(updateResp.values).to.have.property('test1', '/test');
expect(updateResp.values).not.to.have.property('test2');
expect(updateResp).to.not.have.property('warnings');
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount);
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', '/test');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('non-minimal dependencies should be available after update', () => {
installFoxx(mount, {type: 'dir', buffer: depPath});
const updateResp = arango.PATCH('/_api/foxx/dependencies?mount=' + mount + '&minimal=false', {
test1: '/test'
});
expect(updateResp).to.have.property('test1');
expect(updateResp.test1).to.have.property('current', '/test');
expect(updateResp.test1).to.not.have.property('warning');
expect(updateResp).to.have.property('test2');
expect(updateResp.test2).to.not.have.property('current');
expect(updateResp.test2).to.not.have.property('warning');
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount + '&minimal=false');
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', '/test');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('minimal dependencies should be available after update', () => {
installFoxx(mount, {type: 'dir', buffer: depPath});
const updateResp = arango.PATCH('/_api/foxx/dependencies?mount=' + mount + '&minimal=true', {
test1: '/test'
});
expect(updateResp).to.have.property('values');
expect(updateResp.values).to.have.property('test1', '/test');
expect(updateResp.values).not.to.have.property('test2');
expect(updateResp).to.not.have.property('warnings');
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount + '&minimal=true');
expect(resp).to.have.property('test1', '/test');
expect(resp).to.not.have.property('test2');
});
it('dependencies should be available after replace', () => {
installFoxx(mount, {type: 'dir', buffer: depPath});
const replaceResp = arango.PUT('/_api/foxx/dependencies?mount=' + mount, {
test1: '/test'
});
expect(replaceResp).to.have.property('values');
expect(replaceResp.values).to.have.property('test1', '/test');
expect(replaceResp.values).to.not.have.property('test2');
expect(replaceResp).to.have.property('warnings');
expect(replaceResp.warnings).to.have.property('test2', 'is required');
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount);
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', '/test');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('non-minimal dependencies should be available after replace', () => {
installFoxx(mount, {type: 'dir', buffer: depPath});
const replaceResp = arango.PUT('/_api/foxx/dependencies?mount=' + mount + '&minimal=false', {
test1: '/test'
});
expect(replaceResp).to.have.property('test1');
expect(replaceResp.test1).to.have.property('current', '/test');
expect(replaceResp.test1).to.not.have.property('warning');
expect(replaceResp).to.have.property('test2');
expect(replaceResp.test2).to.not.have.property('current');
expect(replaceResp.test2).to.have.property('warning', 'is required');
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount + '&minimal=false');
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', '/test');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('minimal dependencies should be available after replace', () => {
installFoxx(mount, {type: 'dir', buffer: depPath});
const replaceResp = arango.PUT('/_api/foxx/dependencies?mount=' + mount + '&minimal=true', {
test1: '/test'
});
expect(replaceResp).to.have.property('values');
expect(replaceResp.values).to.have.property('test1', '/test');
expect(replaceResp.values).to.not.have.property('test2');
expect(replaceResp).to.have.property('warnings');
expect(replaceResp.warnings).to.have.property('test2', 'is required');
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount + '&minimal=true');
expect(resp).to.have.property('test1', '/test');
expect(resp).to.not.have.property('test2');
});
it('dependencies should be merged after update', () => {
installFoxx(mount, {type: 'dir', buffer: depPath});
const replaceResp = arango.PUT('/_api/foxx/dependencies?mount=' + mount, {
test2: '/test2'
});
expect(replaceResp).to.have.property('values');
expect(replaceResp.values).to.not.have.property('test1');
expect(replaceResp.values).to.have.property('test2', '/test2');
expect(replaceResp).to.have.property('warnings');
expect(replaceResp.warnings).to.have.property('test1', 'is required');
const updateResp = arango.PATCH('/_api/foxx/dependencies?mount=' + mount, {
test1: '/test1'
});
expect(updateResp).to.have.property('values');
expect(updateResp.values).to.have.property('test1', '/test1');
expect(updateResp.values).to.have.property('test2', '/test2');
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount);
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', '/test1');
expect(resp).to.have.property('test2');
expect(resp.test2).to.have.property('current', '/test2');
});
it('non-minimal dependencies should be merged after update', () => {
installFoxx(mount, {type: 'dir', buffer: depPath});
const replaceResp = arango.PUT('/_api/foxx/dependencies?mount=' + mount + '&minimal=false', {
test2: '/test2'
});
expect(replaceResp).to.have.property('test1');
expect(replaceResp.test1).to.have.property('warning', 'is required');
expect(replaceResp).to.have.property('test2');
expect(replaceResp.test2).to.have.property('current', '/test2');
const updateResp = arango.PATCH('/_api/foxx/dependencies?mount=' + mount + '&minimal=false', {
test1: '/test1'
});
expect(updateResp).to.have.property('test1');
expect(updateResp.test1).to.have.property('current', '/test1');
expect(updateResp.test1).to.not.have.property('warning');
expect(updateResp).to.have.property('test2');
expect(updateResp.test2).to.have.property('current', '/test2');
expect(updateResp.test2).to.not.have.property('warning');
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount + '&minimal=false');
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', '/test1');
expect(resp).to.have.property('test2');
expect(resp.test2).to.have.property('current', '/test2');
});
it('minimal dependencies should be merged after update', () => {
installFoxx(mount, {type: 'dir', buffer: depPath});
const replaceResp = arango.PUT('/_api/foxx/dependencies?mount=' + mount + '&minimal=true', {
test2: '/test2'
});
expect(replaceResp).to.have.property('values');
expect(replaceResp.values).to.have.property('test2', '/test2');
expect(replaceResp.values).to.not.have.property('test1');
expect(replaceResp).to.have.property('warnings');
expect(replaceResp.warnings).to.have.property('test1', 'is required');
const updateResp = arango.PATCH('/_api/foxx/dependencies?mount=' + mount + '&minimal=true', {
test1: '/test1'
});
expect(updateResp).to.have.property('values');
expect(updateResp.values).to.have.property('test1', '/test1');
expect(updateResp.values).to.have.property('test2', '/test2');
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount + '&minimal=true');
expect(resp).to.have.property('test1', '/test1');
expect(resp).to.have.property('test2', '/test2');
});
it('dependencies should be overwritten after replace', () => {
installFoxx(mount, {type: 'dir', buffer: depPath});
const updateResp = arango.PATCH('/_api/foxx/dependencies?mount=' + mount, {
test2: '/test2'
});
expect(updateResp).to.have.property('values');
expect(updateResp).to.not.have.property('warnings');
expect(updateResp.values).to.have.property('test2', '/test2');
expect(updateResp.values).to.not.have.property('test1');
const replaceResp = arango.PUT('/_api/foxx/dependencies?mount=' + mount, {
test1: '/test'
});
expect(replaceResp).to.have.property('values');
expect(replaceResp.values).to.have.property('test1', '/test');
expect(replaceResp.values).to.not.have.property('test2');
expect(replaceResp).to.have.property('warnings');
expect(replaceResp.warnings).to.have.property('test2', 'is required');
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount);
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', '/test');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('non-minimal dependencies should be overwritten after replace', () => {
installFoxx(mount, {type: 'dir', buffer: depPath});
const updateResp = arango.PATCH('/_api/foxx/dependencies?mount=' + mount + '&minimal=false', {
test2: '/test2'
});
expect(updateResp).to.have.property('test1');
expect(updateResp.test1).to.not.have.property('current');
expect(updateResp.test1).to.not.have.property('warning');
expect(updateResp).to.have.property('test2');
expect(updateResp.test2).to.have.property('current', '/test2');
expect(updateResp.test2).to.not.have.property('warning');
const replaceResp = arango.PUT('/_api/foxx/dependencies?mount=' + mount + '&minimal=false', {
test1: '/test'
});
expect(replaceResp).to.have.property('test1');
expect(replaceResp.test1).to.have.property('current', '/test');
expect(replaceResp.test1).to.not.have.property('warning');
expect(replaceResp.test2).to.not.have.property('current');
expect(replaceResp.test2).to.have.property('warning', 'is required');
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount + '&minimal=false');
expect(resp).to.have.property('test1');
expect(resp.test1).to.have.property('current', '/test');
expect(resp).to.have.property('test2');
expect(resp.test2).to.not.have.property('current');
});
it('minimal dependencies should be overwritten after replace', () => {
installFoxx(mount, {type: 'dir', buffer: depPath});
const updateResp = arango.PATCH('/_api/foxx/dependencies?mount=' + mount + '&minimal=true', {
test2: '/test2'
});
expect(updateResp).to.have.property('values');
expect(updateResp).to.not.have.property('warnings');
expect(updateResp.values).to.have.property('test2', '/test2');
expect(updateResp.values).to.not.have.property('test1');
const replaceResp = arango.PUT('/_api/foxx/dependencies?mount=' + mount + '&minimal=true', {
test1: '/test'
});
expect(replaceResp).to.have.property('values');
expect(replaceResp.values).to.have.property('test1', '/test');
expect(replaceResp.values).to.not.have.property('test2');
expect(replaceResp).to.have.property('warnings');
expect(replaceResp.warnings).to.have.property('test2', 'is required');
const resp = arango.GET('/_api/foxx/dependencies?mount=' + mount + '&minimal=true');
expect(resp).to.have.property('test1', '/test');
expect(resp).to.not.have.property('test2');
});
it('should be downloadable', () => {
installFoxx(mount, {type: 'dir', buffer: minimalWorkingServicePath});
const resp = arango.POST('/_api/foxx/download?mount=' + mount, '');
// expect(resp.headers['content-type']).to.equal('application/zip');
expect(util.isZipBuffer(resp)).to.equal(true);
});
const readmePath = path.resolve(internal.pathForTesting('common'), 'test-data', 'apps', 'with-readme');
it('should deliver the readme', () => {
installFoxx(mount, {type: 'dir', buffer: readmePath});
const resp = arango.GET('/_api/foxx/readme?mount=' + mount);
// expect(resp.headers['content-type']).to.equal('text/plain; charset=utf-8');
expect(resp).to.equal('Please read this.');
});
it('should indicate a missing readme', () => {
installFoxx(mount, minimalWorkingZip);
const resp = arango.GET('/_api/foxx/readme?mount=' + mount);
expect(resp.code).to.equal(204);
expect(resp.error).to.equal(false);
});
it('should provide a swagger description', () => {
installFoxx(mount, minimalWorkingZip);
const resp = arango.GET('/_api/foxx/swagger?mount=' + mount);
expect(resp).to.have.property('swagger', '2.0');
expect(resp).to.have.property('basePath', `/_db/${db._name()}${mount}`);
expect(resp).to.have.property('info');
expect(resp.info).to.have.property('title', 'minimal-working-manifest');
expect(resp.info).to.have.property('description', '');
expect(resp.info).to.have.property('version', '0.0.0');
expect(resp.info).to.have.property('license');
expect(resp).to.have.property('paths');
expect(resp.paths).to.have.property('/');
expect(resp.paths['/']).to.have.property('get');
});
it('list should allow excluding system services', () => {
installFoxx(mount, minimalWorkingZip);
const withSystem = arango.GET('/_api/foxx');
const withoutSystem = arango.GET('/_api/foxx?excludeSystem=true');
const numSystemWithSystem = withSystem.map(service => service.mount).filter(mount => mount.startsWith('/_')).length;
const numSystemWithoutSystem = withoutSystem.map(service => service.mount).filter(mount => mount.startsWith('/_')).length;
expect(numSystemWithSystem).to.above(0);
expect(numSystemWithSystem).to.equal(withSystem.length - withoutSystem.length);
expect(numSystemWithoutSystem).to.equal(0);
});
it('should be contained in service list', () => {
installFoxx(mount, minimalWorkingZip);
const resp = arango.GET('/_api/foxx');
const service = resp.find(service => service.mount === mount);
expect(service).to.have.property('name', 'minimal-working-manifest');
expect(service).to.have.property('version', '0.0.0');
expect(service).to.have.property('provides');
expect(service.provides).to.eql({});
expect(service).to.have.property('development', false);
expect(service).to.have.property('legacy', false);
});
it('information should be returned', () => {
installFoxx(mount, minimalWorkingZip);
const resp = arango.GET('/_api/foxx/service?mount=' + mount);
const service = resp;
expect(service).to.have.property('mount', mount);
expect(service).to.have.property('name', 'minimal-working-manifest');
expect(service).to.have.property('version', '0.0.0');
expect(service).to.have.property('development', false);
expect(service).to.have.property('legacy', false);
expect(service).to.have.property('manifest');
expect(service.manifest).to.be.an('object');
expect(service).to.have.property('options');
expect(service.options).to.be.an('object');
expect(service).to.have.property('checksum');
expect(service.checksum).to.be.a('string');
});
const scriptPath = path.resolve(internal.pathForTesting('common'), 'test-data', 'apps', 'minimal-working-setup-teardown');
it('list of scripts should be available', () => {
installFoxx(mount, {type: 'dir', buffer: scriptPath});
const resp = arango.GET('/_api/foxx/scripts?mount=' + mount);
expect(resp).to.have.property('setup', 'Setup');
expect(resp).to.have.property('teardown', 'Teardown');
});
it('script should be available', () => {
installFoxx(mount, {type: 'dir', buffer: scriptPath});
const col = `${mount}_setup_teardown`.replace(/\//, '').replace(/-/g, '_');
expect(db._collection(col)).to.be.an('object');
const resp = arango.POST('/_api/foxx/scripts/teardown?mount=' + mount, '');
db._flushCache();
expect(db._collection(col)).to.equal(null);
});
it('non-existing script should not be available', () => {
installFoxx(mount, {type: 'dir', buffer: scriptPath});
const resp = arango.POST('/_api/foxx/scripts/no?mount=' + mount, '');
});
const echoPath = path.resolve(internal.pathForTesting('common'), 'test-data', 'apps', 'echo-script');
it('should pass argv to script and return exports', () => {
installFoxx(mount, {type: 'dir', buffer: echoPath});
const argv = {hello: 'world'};
const resp = arango.POST('/_api/foxx/scripts/echo?mount=' + mount, argv);
expect(resp).to.eql([argv]);
});
it('should treat array script argv like any other script argv', () => {
installFoxx(mount, {type: 'dir', buffer: echoPath});
const argv = ['yes', 'please'];
const resp = arango.POST('/_api/foxx/scripts/echo?mount=' + mount, argv);
expect(resp).to.eql([argv]);
});
it('set devmode should enable devmode', () => {
installFoxx(mount, minimalWorkingZip);
const resp = arango.GET('/_api/foxx/service?mount=' + mount);
expect(resp.development).to.equal(false);
const devResp = arango.POST('/_api/foxx/development?mount=' + mount, '');
expect(devResp.development).to.equal(true);
const respAfter = arango.GET('/_api/foxx/service?mount=' + mount);
expect(respAfter.development).to.equal(true);
});
it('clear devmode should disable devmode', () => {
installFoxx(mount, minimalWorkingZipDev);
const resp = arango.GET('/_api/foxx/service?mount=' + mount);
expect(resp.development).to.equal(true);
const devResp = arango.DELETE('/_api/foxx/development?mount=' + mount);
expect(devResp.development).to.equal(false);
const respAfter = arango.GET('/_api/foxx/service?mount=' + mount);
expect(respAfter.development).to.equal(false);
});
const routes = [
['GET', '/_api/foxx/service'],
['PATCH', '/_api/foxx/service', { source: minimalWorkingZipPath }],
['PUT', '/_api/foxx/service', { source: minimalWorkingZipPath }],
['DELETE', '/_api/foxx/service'],
['GET', '/_api/foxx/configuration'],
['PATCH', '/_api/foxx/configuration'],
['PUT', '/_api/foxx/configuration'],
['GET', '/_api/foxx/dependencies'],
['PATCH', '/_api/foxx/dependencies'],
['PUT', '/_api/foxx/dependencies'],
['POST', '/_api/foxx/development'],
['DELETE', '/_api/foxx/development'],
['GET', '/_api/foxx/scripts'],
['POST', '/_api/foxx/scripts/xxx'],
['POST', '/_api/foxx/tests'],
['POST', '/_api/foxx/download'],
['GET', '/_api/foxx/readme'],
['GET', '/_api/foxx/swagger']
];
for (const [reqFun, url, body] of routes) {
it(`should return 400 when mount is omitted for ${reqFun} ${url}`, () => {
let bbody = body;
if (body === undefined) {
bbody = '';
}
const resp = arango[reqFun](url, bbody);
expect(resp.code).to.equal(400);
});
it(`should return 400 when mount is unknown for ${reqFun} ${url}`, () => {
let bbody = body;
if (body === undefined) {
bbody = '';
}
const resp = arango[reqFun](url + '?mount=/dev/null', bbody);
expect(resp.code).to.equal(400);
});
}
it('tests should run', () => {
const testPath = path.resolve(internal.pathForTesting('common'), 'test-data', 'apps', 'with-tests');
FoxxManager.install(testPath, mount);
const resp = arango.POST('/_api/foxx/tests?mount=' + mount, '');
expect(resp).to.have.property('stats');
expect(resp).to.have.property('tests');
expect(resp).to.have.property('pending');
expect(resp).to.have.property('failures');
expect(resp).to.have.property('passes');
});
it('replace on invalid mount should not be installed', () => {
const replaceResp = arango.PUT('/_api/foxx/service?mount=' + mount, {
source: minimalWorkingZipPath
});
expect(replaceResp.code).to.equal(400);
const resp = arango.GET(mount);
expect(resp.code).to.equal(404);
});
it('replace on invalid mount should be installed when forced', () => {
const replaceResp = arango.PUT('/_api/foxx/service?mount=' + mount + '&force=true', {
source: minimalWorkingZipPath
});
const resp = arango.GET(mount);
expect(resp).to.eql({hello: 'world'});
});
});