Commit ed8dc251 authored by Tom JORQUERA's avatar Tom JORQUERA

Merge branch 'client_registry' into 'master'

Add server-side controller with client registry

See merge request !39
parents b45d497e e3bd3fac
Pipeline #4470 passed with stage
in 37 seconds
......@@ -21,16 +21,22 @@
const config = require('./config.json');
const runner = require('./lib/runner.js')(config.runner);
const controller = require('./lib/controller.js')('./client');
const loader = require('./lib/loader.js')('./client');
const controllerFactory = require('./lib/controller.js');
console.log('starting hublot...');
controller.loadAll('controller', 'lib', 'robot')
loader.loadAll('controller', 'lib', 'robot')
.then(modules => {
console.log('modules loaded... launching runner');
// Note: result can be stored in a variable to control further the browser
// e.g.: let client = runner.run(...); client.end();
runner.run(modules, config.visio.url, 'test-bot', config.client);
console.log('modules loaded... creating controller');
const controller = controllerFactory.create(runner, modules, config);
console.log('creating client');
// Note: client returned object can be used to control further the browser
// e.g.: let client = controller.client('room'); client.end();
controller.client('test-bot');
})
.catch(err => {
console.error(err);
......
/*
* Copyright (c) 2017 Linagora.
*
......@@ -20,55 +21,26 @@
'use strict';
const fs = require('mz/fs');
// Module to control the server-side behavior and state
module.exports = root => {
const create = (runner, modules, config) => {
const controller = {
load: module => new Promise((resolve, reject) => {
// First we read the file with the same name than the folder
fs.readFile(root + '/' + module + '/' + module + '.js', 'utf8')
.then(content => {
// Then we read all the other files in the folder
fs.readdir(root + '/' + module)
.then(files => {
// Remove the file we already read
files.splice(files.indexOf(module + '.js'), 1);
// Ignore test files
files = files.filter(filename => !filename.endsWith('.test.js'));
Promise.all(files.map(
f => fs.readFile(root + '/' + module + '/' + f, 'utf8')
))
.then(contents => {
// Concatenate all the files and resolve
resolve([content].concat(contents));
})
.catch(err => {
reject(err);
});
})
.catch(err => {
reject(err);
});
})
.catch(err => {
reject(err);
});
}),
registry: {},
client: room => {
if (room in controller.registry) {
return null;
}
loadAll: (...modules) => new Promise((resolve, reject) => {
// Load all the passed modules
Promise.all(
modules.map(f => controller.load(f)))
.then(values => {
// Concatenate all the arrays into a single one
resolve(Array.prototype.concat.apply([], values));
}).catch(err => {
reject(err);
});
})
controller.registry[room] = runner.run(modules,
config.visio.url,
room,
config.client);
return controller.registry[room];
}
};
return controller;
};
module.exports = {
create
};
......@@ -20,100 +20,44 @@
'use strict';
jest.mock('mz/fs');
const {create} = require('./controller.js');
describe('loadModules', () => {
const MOCK_FILES = {
'/test/controller/controller.js': '1',
'/test/controller/controller.test.js': 'sometest',
'/test/controller/file.js': '2',
'/test/controller/file.test.js': 'sometest',
'/test/lib/lib.js': '3',
'/test/robot/robot.js': '4',
'/test/notavalidmodule/file.js': '5'
};
// Here are some needed mocks
beforeEach(() => {
require('mz/fs').__setup(MOCK_FILES);
});
describe('loading a module', () => {
test('should include all (and only) non-test files in its dir.', done => {
const controller = require('./controller.js')('/test');
controller.load('controller')
.then(res => {
expect(res.length).toBe(2);
expect(res.includes('1')).toBeTruthy();
expect(res.includes('2')).toBeTruthy();
done();
});
});
test('should return the files in the correct order', done => {
const controller = require('./controller.js')('/test');
controller.load('controller')
.then(res => {
expect(res[0]).toBe('1');
expect(res[1]).toBe('2');
done();
});
});
test('should fail for a nonexistent module', done => {
const controller = require('./controller.js')('/test');
const runnerMock = {
run: () => {}
};
controller.load('nonexistent')
.catch(() => done());
});
const configMock = {
visio: {url: 'someurl.test'},
client: {}
};
test('should fail for a module missing its base file', done => {
const controller = require('./controller.js')('/test');
let controller;
controller.load('notavalidmodule')
.catch(() => done());
});
describe('controller', () => {
beforeEach(() => {
controller = create(runnerMock, [], configMock);
});
describe('loading several modules', () => {
test('should include all files from all modules', done => {
const controller = require('./controller.js')('/test');
controller.loadAll('controller', 'lib', 'robot')
.then(res => {
expect(res.length).toBe(4);
expect(res.includes('1')).toBeTruthy();
expect(res.includes('2')).toBeTruthy();
expect(res.includes('3')).toBeTruthy();
expect(res.includes('4')).toBeTruthy();
done();
});
});
test('should return files in correct order', done => {
const controller = require('./controller.js')('/test');
test('should allow to create a client to a new room', () => {
expect(controller.client).toBeDefined();
});
controller.loadAll('controller', 'lib', 'robot')
.then(res => {
expect(res[0]).toBe('1');
expect(res[1]).toBe('2');
expect(res[2]).toBe('3');
expect(res[3]).toBe('4');
done();
});
});
test('should register a newly created client to its room', () => {
const client = controller.client('test');
expect(controller.registry).toHaveProperty('test', client);
});
test('should fail when trying to load a non-existent module ', done => {
const controller = require('./controller.js')('/test');
controller.loadAll('controller', 'lib', 'robot', 'nonexistent')
.catch(() => done());
});
test('should return null when trying to create a client for an existing room', () => {
controller.client('test');
const client2 = controller.client('test');
expect(client2).toBeNull();
});
test('should fail when trying to load an invalid module', done => {
const controller = require('./controller.js')('/test');
controller.loadAll('controller', 'lib', 'robot', 'invalid')
.catch(() => done());
});
test('should not replace the client for an existing room', () => {
const client1 = controller.client('test');
controller.client('test');
expect(controller.registry).toHaveProperty('test', client1);
});
});
/*
* Copyright (c) 2017 Linagora.
*
* This file is part of Hublot
* (see https://ci.linagora.com/linagora/lgs/labs/hublot).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
const fs = require('mz/fs');
module.exports = root => {
const controller = {
load: module => new Promise((resolve, reject) => {
// First we read the file with the same name than the folder
fs.readFile(root + '/' + module + '/' + module + '.js', 'utf8')
.then(content => {
// Then we read all the other files in the folder
fs.readdir(root + '/' + module)
.then(files => {
// Remove the file we already read
files.splice(files.indexOf(module + '.js'), 1);
// Ignore test files
files = files.filter(filename => !filename.endsWith('.test.js'));
Promise.all(files.map(
f => fs.readFile(root + '/' + module + '/' + f, 'utf8')
))
.then(contents => {
// Concatenate all the files and resolve
resolve([content].concat(contents));
})
.catch(err => {
reject(err);
});
})
.catch(err => {
reject(err);
});
})
.catch(err => {
reject(err);
});
}),
loadAll: (...modules) => new Promise((resolve, reject) => {
// Load all the passed modules
Promise.all(
modules.map(f => controller.load(f)))
.then(values => {
// Concatenate all the arrays into a single one
resolve(Array.prototype.concat.apply([], values));
}).catch(err => {
reject(err);
});
})
};
return controller;
};
/*
* Copyright (c) 2017 Linagora.
*
* This file is part of Hublot
* (see https://ci.linagora.com/linagora/lgs/labs/hublot).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
jest.mock('mz/fs');
describe('loadModules', () => {
const MOCK_FILES = {
'/test/controller/controller.js': '1',
'/test/controller/controller.test.js': 'sometest',
'/test/controller/file.js': '2',
'/test/controller/file.test.js': 'sometest',
'/test/lib/lib.js': '3',
'/test/robot/robot.js': '4',
'/test/notavalidmodule/file.js': '5'
};
beforeEach(() => {
require('mz/fs').__setup(MOCK_FILES);
});
describe('loading a module', () => {
test('should include all (and only) non-test files in its dir.', done => {
const loader = require('./loader.js')('/test');
loader.load('controller')
.then(res => {
expect(res.length).toBe(2);
expect(res.includes('1')).toBeTruthy();
expect(res.includes('2')).toBeTruthy();
done();
});
});
test('should return the files in the correct order', done => {
const loader = require('./loader.js')('/test');
loader.load('controller')
.then(res => {
expect(res[0]).toBe('1');
expect(res[1]).toBe('2');
done();
});
});
test('should fail for a nonexistent module', done => {
const loader = require('./loader.js')('/test');
loader.load('nonexistent')
.catch(() => done());
});
test('should fail for a module missing its base file', done => {
const loader = require('./loader.js')('/test');
loader.load('notavalidmodule')
.catch(() => done());
});
});
describe('loading several modules', () => {
test('should include all files from all modules', done => {
const loader = require('./loader.js')('/test');
loader.loadAll('controller', 'lib', 'robot')
.then(res => {
expect(res.length).toBe(4);
expect(res.includes('1')).toBeTruthy();
expect(res.includes('2')).toBeTruthy();
expect(res.includes('3')).toBeTruthy();
expect(res.includes('4')).toBeTruthy();
done();
});
});
test('should return files in correct order', done => {
const loader = require('./loader.js')('/test');
loader.loadAll('controller', 'lib', 'robot')
.then(res => {
expect(res[0]).toBe('1');
expect(res[1]).toBe('2');
expect(res[2]).toBe('3');
expect(res[3]).toBe('4');
done();
});
});
test('should fail when trying to load a non-existent module ', done => {
const loader = require('./loader.js')('/test');
loader.loadAll('controller', 'lib', 'robot', 'nonexistent')
.catch(() => done());
});
test('should fail when trying to load an invalid module', done => {
const loader = require('./loader.js')('/test');
loader.loadAll('controller', 'lib', 'robot', 'invalid')
.catch(() => done());
});
});
});
......@@ -18,7 +18,7 @@
},
"devDependencies": {
"jest": "20.0.0",
"xo": "^0.18.2"
"xo": "0.18.2"
},
"author": "Linagora Folks",
"license": "AGPL-3.0",
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment