/* global describe, it, beforeEach, afterEach */ 'use strict'; const FoxxManager = require('@arangodb/foxx/manager'); const ArangoCollection = require('@arangodb').ArangoCollection; const fs = require('fs'); const db = require('internal').db; const arangodb = require('@arangodb'); const arango = require('@arangodb').arango; const aql = arangodb.aql; const basePath = fs.makeAbsolute(fs.join(require('internal').pathForTesting('common'), 'test-data', 'apps')); const expect = require('chai').expect; const download = require('internal').download; const origin = arango.getEndpoint().replace(/\+vpp/, '').replace(/^tcp:/, 'http:').replace(/^ssl:/, 'https:').replace(/^vst:/, 'http:'); describe('Foxx Manager', function () { describe('(CRUD operation) ', function () { const setupTeardownApp = fs.join(basePath, 'minimal-working-setup-teardown'); const setupApp = fs.join(basePath, 'minimal-working-setup'); const mount = '/testmount'; const prefix = mount.substr(1).replace(/[-.:/]/, '_'); const setupTeardownCol = `${prefix}_setup_teardown`; const setupCol = `${prefix}_setup`; describe('installed service', function () { beforeEach(function () { try { FoxxManager.uninstall(mount, {force: true}); } catch (e) { } try { db._drop(setupTeardownCol); } catch (e) { } }); afterEach(function () { try { FoxxManager.uninstall(mount, {force: false}); } catch (e) { } try { db._drop(setupTeardownCol); } catch (e) { } try { // some tests create these: db._drop('testmount_setup'); } catch (e) { } }); it('should contains in _apps', function () { FoxxManager.install(setupTeardownApp, mount); const checksum = db._query(aql` FOR service IN _apps FILTER service.mount == ${mount} RETURN service.checksum `).next(); expect(checksum).to.be.a('string'); expect(checksum).not.to.be.empty; }); it('should run the setup script', function () { FoxxManager.install(setupTeardownApp, mount); expect(db._collection(setupTeardownCol)).to.be.an.instanceOf(ArangoCollection); }); it('with option {setup:false} should not run the setup script', function () { FoxxManager.install(setupTeardownApp, mount, {setup: false}); expect(db._collection(setupTeardownCol)).to.equal(null); }); it('should be available', function () { FoxxManager.install(setupTeardownApp, mount); const url = `${origin}${mount}/test`; const res = download(url); expect(res.code).to.equal(200); expect(res.body).to.equal('true'); }); }); describe('uninstalled service', function () { beforeEach(function () { try { FoxxManager.uninstall(mount, {force: true}); } catch (e) { } try { db._drop(setupTeardownCol); } catch (e) { } }); afterEach(function () { try { FoxxManager.uninstall(mount, {force: true}); } catch (e) { } try { db._drop(setupTeardownCol); } catch (e) { } try { // some tests create these: db._drop('testmount_setup'); } catch (e) { } try { // some tests create these: db._drop('testmount_setup'); } catch (e) { } }); it('should not contains in _apps', function () { FoxxManager.install(setupTeardownApp, mount); FoxxManager.uninstall(mount); const s = db._query(aql` FOR service IN _apps FILTER service.mount == ${mount} RETURN service `).toArray(); expect(s.length).to.equal(0); }); it('should run the teardown script', function () { FoxxManager.install(setupTeardownApp, mount); FoxxManager.uninstall(mount); expect(db._collection(setupTeardownCol)).to.equal(null); }); it('with option {teardown:false} should not run the teardown script', function () { FoxxManager.install(setupTeardownApp, mount); FoxxManager.uninstall(mount, {teardown: false}); expect(db._collection(setupTeardownCol)).to.be.an.instanceOf(ArangoCollection); }); }); describe('replaced service', function () { beforeEach(function () { try { FoxxManager.uninstall(mount, {force: true}); } catch (e) { } try { db._drop(setupTeardownCol); } catch (e) { } try { FoxxManager.install(setupTeardownApp, mount); } catch (e) { } }); afterEach(function () { try { FoxxManager.uninstall(mount, {force: true}); } catch (e) { } try { db._drop(setupTeardownCol); } catch (e) { } try { // some tests create these: db._drop('testmount_setup'); } catch (e) { } }); it('should update it checksum in _apps', function () { const q = aql` FOR service IN _apps FILTER service.mount == ${mount} RETURN service.checksum `; const oldChecksum = db._query(q).toArray()[0]; FoxxManager.replace(setupApp, mount); const newChecksum = db._query(q).toArray()[0]; expect(newChecksum).to.not.be.equal(oldChecksum); }); it('should run the old teardown and new setup script', function () { expect(db._collection(setupTeardownCol)).to.be.an.instanceOf(ArangoCollection); FoxxManager.replace(setupApp, mount); expect(db._collection(setupTeardownCol)).to.equal(null); expect(db._collection(setupCol)).to.be.an.instanceOf(ArangoCollection); }); it('with option {teardown:false} should not run the teardown script', function () { FoxxManager.replace(setupApp, mount, {teardown: false}); expect(db._collection(setupTeardownCol)).to.be.an.instanceOf(ArangoCollection); expect(db._collection(setupCol)).to.be.an.instanceOf(ArangoCollection); }); }); describe('installed service with malformed setup', function () { const malformedSetupApp = fs.join(basePath, 'malformed-setup-file'); beforeEach(function () { try { FoxxManager.uninstall(mount, {force: true}); } catch (e) { } }); afterEach(function () { try { FoxxManager.uninstall(mount, {force: false}); } catch (e) { } try { // some tests create these: db._drop('testmount_setup'); } catch (e) { } }); it('should not contains in _apps', function () { try { FoxxManager.install(malformedSetupApp, mount); } catch (e) { } const size = db._query(aql` FOR service IN _apps FILTER service.mount == ${mount} RETURN service.checksum `).toArray().length; expect(size).to.equal(0); }); it('should not be available', function () { try { FoxxManager.install(malformedSetupApp, mount); } catch (e) { } const url = `${origin}${mount}/test`; const res = download(url); expect(res.code).to.equal(404); }); }); describe('installed service with malformed setup path', function () { const malformedSetupApp = fs.join(basePath, 'malformed-setup-path'); beforeEach(function () { try { FoxxManager.uninstall(mount, {force: true}); } catch (e) { } }); afterEach(function () { try { FoxxManager.uninstall(mount, {force: false}); } catch (e) { } }); it('should not contains in _apps', function () { try { FoxxManager.install(malformedSetupApp, mount); } catch (e) { } const size = db._query(aql` FOR service IN _apps FILTER service.mount == ${mount} RETURN service.checksum `).toArray().length; expect(size).to.equal(0); }); it('should not be available', function () { try { FoxxManager.install(malformedSetupApp, mount); } catch (e) { } const url = `${origin}${mount}/test`; const res = download(url); expect(res.code).to.equal(404); }); }); describe('installed service with broken setup', function () { const malformedSetupApp = fs.join(basePath, 'broken-setup-file'); beforeEach(function () { try { FoxxManager.uninstall(mount, {force: true}); } catch (e) { } }); afterEach(function () { try { FoxxManager.uninstall(mount, {force: false}); } catch (e) { } try { // some tests create these: db._drop('testmount_setup'); } catch (e) { } }); it('should contains in _apps', function () { try { FoxxManager.install(malformedSetupApp, mount); } catch (e) { } const size = db._query(aql` FOR service IN _apps FILTER service.mount == ${mount} RETURN service.checksum `).toArray().length; expect(size).to.equal(0); }); it('should not be available', function () { try { FoxxManager.install(malformedSetupApp, mount); } catch (e) { } const url = `${origin}${mount}/test`; const res = download(url); expect(res.code).to.equal(404); }); }); describe('service with heal during setup', function () { // Regression test: There was a race condition during foxx service install // when selfHeal() ran after the service files were put in their final // location and before the service was written to _apps. // This service calls selfHeal() during setup to simulate that case. const setupHealApp = fs.join(basePath, 'heal-during-setup'); const setupMinimalApp = fs.join(basePath, 'minimal-working-service'); beforeEach(function () { try { FoxxManager.uninstall(mount, {force: true}); } catch (e) { } }); afterEach(function () { try { FoxxManager.uninstall(mount, {force: false}); } catch (e) { } }); it('should be available after install', function () { FoxxManager.install(setupHealApp, mount); const url = origin + mount; const res = download(url); expect(res.code).to.equal(200); expect(res.body).to.equal('true'); }); it('should be available after replace', function () { { // set up some service FoxxManager.install(setupMinimalApp, mount); const url = origin + mount; const res = download(url); expect(res.code).to.equal(200); expect(JSON.parse(res.body)).to.deep.equal({hello: 'world'}); } { // replace it and call heal() during setup FoxxManager.replace(setupHealApp, mount); const url = origin + mount; const res = download(url); expect(res.code).to.equal(200); expect(res.body).to.equal('true'); } }); it('should be available after upgrade', function () { { // set up some service FoxxManager.install(setupMinimalApp, mount); const url = origin + mount; const res = download(url); expect(res.code).to.equal(200); expect(JSON.parse(res.body)).to.deep.equal({hello: 'world'}); } { // upgrade it and call heal() during setup FoxxManager.upgrade(setupHealApp, mount); const url = origin + mount; const res = download(url); expect(res.code).to.equal(200); expect(res.body).to.equal('true'); } }); }); }); });