happner
Version:
distributed application engine with evented storage and mesh services
384 lines (331 loc) • 8.81 kB
JavaScript
/**
* Created by nomilous on 2016/07/28.
*/
var path = require('path');
var filename = path.basename(__filename);
var should = require('chai').should();
var Happner = require('../');
var Promise = require('bluebird');
var request = Promise.promisify(require('request'));
describe(filename, function () {
require('benchmarket').start();
after(require('benchmarket').store());
var mesh;
before(function (done) {
Happner.create({
modules: {
'factory': { // component that adds another component via _mesh
instance: {
createComponent: function ($happn, name, callback) {
$happn._mesh._createElement({
module: {
name: name,
config: {
instance: {
method: function (callback) {
callback(null, name + ' OK');
}
}
}
},
component: {
name: name,
config: {}
}
}, callback);
}
}
}
},
components: {
'factory': {
accessLevel: 'mesh'
}
}
})
.then(function (_mesh) {
mesh = _mesh;
done();
})
.catch(done);
});
after(function (done) {
if (!mesh) return done();
mesh.stop({reconnect: false}, done);
});
it('can add a component to the mesh', function (done) {
mesh._createElement({
module: {
name: 'newComponent',
config: {
instance: {
method: function (callback) {
callback(null, 'newComponent OK');
},
page: function (req, res) {
res.end('WEB PAGE');
}
}
}
},
component: {
name: 'newComponent',
config: {
web: {
routes: {
page: 'page'
}
}
}
}
})
.then(function () {
// use the new component's method
return mesh.exchange.newComponent.method();
})
.then(function (result) {
result.should.equal('newComponent OK');
})
.then(function () {
// use new component's web method
return request('http://localhost:55000/newComponent/page');
})
.then(function (result) {
result[1].should.equal('WEB PAGE');
})
.then(done)
.catch(done);
});
it('can add a new component to the mesh (from another component using _mesh)', function (done) {
mesh.exchange.factory.createComponent('componentName')
.then(function () {
// use the new component
return mesh.exchange.componentName.method();
})
.then(function (result) {
result.should.equal('componentName OK');
})
.then(done)
.catch(done);
});
it('can remove components from the mesh including web methods', function (done) {
mesh._createElement({
module: {
name: 'anotherComponent',
config: {
instance: {
method: function (callback) {
callback(null, 'anotherComponent OK');
},
page: function (req, res) {
res.end('WEB PAGE');
}
}
}
},
component: {
name: 'anotherComponent',
config: {
web: {
routes: {
page: 'page'
}
}
}
}
})
.then(function () {
return mesh.exchange.anotherComponent.method()
})
.then(function (result) {
result.should.equal('anotherComponent OK');
})
.then(function () {
return request('http://localhost:55000/anotherComponent/page')
})
.then(function (result) {
result[1].should.equal('WEB PAGE');
})
// now remove the component
.then(function () {
return mesh._destroyElement('anotherComponent')
})
// exchange reference is gone
.then(function (result) {
should.not.exist(mesh.exchange.anotherComponent);
})
// web route is gone
.then(function () {
return request('http://localhost:55000/anotherComponent/page');
})
.then(function (result) {
result[1].should.equal('Cannot GET /anotherComponent/page\n');
})
.then(done)
.catch(done)
});
it('emits description change on adding component', function (done) {
mesh._mesh.data.on('/mesh/schema/description', {count: 1}, function (data, meta) {
try {
data.components.component1.methods.should.eql({
method: {
parameters: [
{name: 'callback'}
]
}
});
done();
} catch (e) {
done(e);
}
});
mesh._createElement({
module: {
name: 'component1',
config: {
instance: {
method: function (callback) {
callback();
}
}
}
},
component: {
name: 'component1',
config: {}
}
}).catch(done);
});
it('emits description change on destroying component', function (done) {
mesh._createElement({
module: {
name: 'component2',
config: {
instance: {
method: function (callback) {
callback();
}
}
}
},
component: {
name: 'component2',
config: {}
}
})
.then(function () {
return mesh._mesh.data.on('/mesh/schema/description', {count: 1}, function (data, meta) {
should.not.exist(data.components.component2);
done();
});
})
.then(function () {
return mesh._destroyElement('component2');
})
.catch(done);
});
it('informs mesh client on create component', function (done) {
var client = new Happner.MeshClient();
client.login()
.then(function () {
return client.once('components/created', function (array) {
try {
array[0].description.name.should.equal('component3');
done();
} catch (e) {
done(e);
}
});
})
.then(function () {
return mesh._createElement({
module: {
name: 'component3',
config: {
instance: {
method: function (callback) {
callback();
}
}
}
},
component: {
name: 'component3',
config: {}
}
})
})
.catch(done);
});
it('informs mesh client on destroy component', function (done) {
var client = new Happner.MeshClient();
return mesh._createElement({
module: {
name: 'component4',
config: {
instance: {
method: function (callback) {
callback();
}
}
}
},
component: {
name: 'component4',
config: {}
}
})
.then(function () {
return client.login();
})
.then(function () {
return client.once('components/destroyed', function (array) {
try {
array[0].description.name.should.equal('component4');
done();
} catch (e) {
done(e);
}
});
})
.then(function () {
return mesh._destroyElement('component4');
})
.catch(done);
});
xit('what happens to reference still held', function (done) {
var keepRefToDeletedComponent;
return mesh._createElement({
module: {
name: 'component5',
config: {
instance: {
method: function (callback) {
callback();
}
}
}
},
component: {
name: 'component5',
config: {}
}
})
.then(function () {
keepRefToDeletedComponent = mesh.exchange.component5;
})
.then(function () {
return mesh._destroyElement('component5');
})
.then(function () {
console.log(keepRefToDeletedComponent);
return keepRefToDeletedComponent.method();
})
.then(function (result) {
console.log(result);
})
.then(done)
.catch(done);
});
require('benchmarket').stop();
});