diff --git a/gulpfile.babel.js b/gulpfile.babel.js index b0b4440..7a382c9 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -371,7 +371,9 @@ gulp.task('serve:dist', cb => { }); gulp.task('test', cb => { - return runSequence('test:server', 'test:client', cb); + //return runSequence('test:server', 'test:client', cb); + //TODO: Integrate client side tests + return runSequence('test:server', cb); }); gulp.task('test:server', cb => { @@ -393,6 +395,11 @@ gulp.task('mocha:integration', () => { .pipe(mocha()); }); +gulp.task('mocha:integration:one', () => { + return gulp.src([`${serverPath}/**/custom_module.integration.js`, 'mocha.global.js']) + .pipe(mocha()); +}); + gulp.task('test:server:coverage', cb => { runSequence('coverage:pre', 'env:all', diff --git a/server/api/ansible/ansible.controller.js b/server/api/ansible/ansible.controller.js index 074a00b..1cdc2d6 100644 --- a/server/api/ansible/ansible.controller.js +++ b/server/api/ansible/ansible.controller.js @@ -86,7 +86,11 @@ export function command(req, res) { ) } -// Creates a new Ansible in the DB +/** + * List Ansible Modules + * @param req + * @param res + */ export function modules(req, res) { var ansibleEngine = req.body.ansibleEngine; @@ -108,6 +112,7 @@ export function modules(req, res) { // Gets a single Deploy from the DB export function getLogs(req, res) { + console.log("Param ID " + req.params.id); return Ansible.findById(req.params.id).exec() .then(handleEntityNotFound(res)) .then(function(entity){ @@ -169,6 +174,7 @@ export function execute(req, res) { var resultSent = false; + // Execute Ansible Playbook and return immediately with a new Job (Ansible) object ansibleTool.executeAnsible(logfilename, project_folder, playbook_name, inventory_file_name, tags_joined, limit_to_hosts_joined, verbose,check_mode, function(data){ //res.write(data) @@ -244,6 +250,7 @@ export function playbook_create(req, res) { function(data){ //res.write(data); //res.end() + console.log("data = " + data); if(!resultSent){ resultSent = true; res.send(data) @@ -251,6 +258,7 @@ export function playbook_create(req, res) { }, function(data){ //res.write(data) + console.log("data = " + data); if(!resultSent){ resultSent = true; res.status(500).send(data) diff --git a/server/api/ansible/ansible.integration.js b/server/api/ansible/ansible.integration.js index 71105f6..5d982af 100644 --- a/server/api/ansible/ansible.integration.js +++ b/server/api/ansible/ansible.integration.js @@ -4,187 +4,318 @@ var app = require('../..'); import request from 'supertest'; +import User from '../user/user.model'; var newAnsible; describe('Ansible API:', function() { - describe('GET /api/ansible', function() { - var ansibles; + var token; + var user; + var ansible_job; - beforeEach(function(done) { + // Clear users before testing + before(function() { + return User.remove().then(function() { + user = new User({ + name: 'Fake User', + email: 'test@example.com', + password: 'password' + }); + + return user.save(); + }); + }); + + // Clear users after testing + after(function() { + return User.remove(); + }); + + describe('GET /api/users/me', function() { + + before(function(done) { request(app) - .get('/api/ansible') + .post('/auth/local') + .send({ + email: 'test@example.com', + password: 'password' + }) .expect(200) .expect('Content-Type', /json/) .end((err, res) => { - if(err) { - return done(err); - } - ansibles = res.body; + token = res.body.token; done(); }); }); - it('should respond with JSON array', function() { - expect(ansibles).to.be.instanceOf(Array); + it('should respond with a user profile when authenticated', function(done) { + request(app) + .get('/api/users/me') + .set('authorization', `Bearer ${token}`) + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + expect(res.body._id.toString()).to.equal(user._id.toString()); + done(); + }); + }); + + it('should respond with a 401 when not authenticated', function(done) { + request(app) + .get('/api/users/me') + .expect(401) + .end(done); }); }); - describe('POST /api/ansible', function() { + describe('POST /modules/list', function() { + var modules; + beforeEach(function(done) { request(app) - .post('/api/ansible') + .post('/api/ansible/modules/list') + .timeout(10000) + .set('authorization', `Bearer ${token}`) .send({ - name: 'New Ansible', - info: 'This is the brand new ansible!!!' + ansibleEngine: { + 'host' : '' + } + }) + .expect(200) + //.expect('Content-Type', /json/) + .end((err, res) => { + if(err) { + return done(err); + } + modules = res.text; + done(); + }); + }); + + + it('should respond with list of Ansible Modules (containing ping module)', function() { + expect(modules).to.contain('ping'); + }); + }); + + + describe('POST /command to execute a sample command - echo "Hello World"', function() { + var modules; + + beforeEach(function(done) { + request(app) + .post('/api/ansible/command') + .set('authorization', `Bearer ${token}`) + .send({ + command: 'echo "Hello World"' + }) + .expect(200) + //.expect('Content-Type', /json/) + .end((err, res) => { + if(err) { + return done(err); + } + modules = res.text; + done(); + }); + }); + + + it('should respond with the result of command', function() { + expect(modules).to.contain('Hello World'); + }); + }); + + + describe('POST /inventory/create', function() { + var result; + + beforeEach(function(done) { + request(app) + .post('/api/ansible/inventory/create') + .set('authorization', `Bearer ${token}`) + .send({ + ansibleEngine: { + host : '', + projectFolder: '/tmp' + }, + inventoryName: 'inventory.txt', + inventoryFileContents: 'localhost ansible_connection=local' + }) + .expect(200) + //.expect('Content-Type', /json/) + .end((err, res) => { + if(err) { + return done(err); + } + result = res.text; + done(); + }); + }); + + + it('should respond with "file written"', function() { + expect(result).to.contain('file written'); + }); + }); + + + describe('POST /playbook/create', function() { + var result; + + beforeEach(function(done) { + request(app) + .post('/api/ansible/playbook/create') + .set('authorization', `Bearer ${token}`) + .send({ + ansibleEngine: { + host : '', + projectFolder: '/tmp' + }, + playbookName: 'test_playbook.yml', + playbookFileContents: '-\n' + + ' name: "Test Play1"\n' + + ' hosts: localhost\n' + + ' tasks:\n' + + ' - name: "Test Task1"\n' + + ' ping:\n' + }) + .expect(200) + //.expect('Content-Type', /json/) + .end((err, res) => { + if(err) { + return done(err); + } + result = res.text; + done(); + }); + }); + + + it('should respond with "file written"', function() { + expect(result).to.contain('file written'); + }); + }); + + + describe('POST /execute', function() { + + beforeEach(function(done) { + request(app) + .post('/api/ansible/execute') + .set('authorization', `Bearer ${token}`) + .send({ + ansibleEngine: { + host : '', + projectFolder: '/tmp/' + }, + selectedPlaybook: 'test_playbook.yml', + inventory_file_name: 'inventory.txt', }) .expect(201) - .expect('Content-Type', /json/) + //.expect('Content-Type', /json/) .end((err, res) => { if(err) { return done(err); } - newAnsible = res.body; + ansible_job = res.body; done(); }); }); - it('should respond with the newly created ansible', function() { - expect(newAnsible.name).to.equal('New Ansible'); - expect(newAnsible.info).to.equal('This is the brand new ansible!!!'); + + it('should respond with an Ansible Job object', function() { + expect(ansible_job.selectedPlaybook).to.equal('test_playbook.yml'); }); }); - describe('GET /api/ansible/:id', function() { - var ansible; + describe('GET /:id', function() { beforeEach(function(done) { request(app) - .get(`/api/ansible/${newAnsible._id}`) + .get('/api/ansible/' + ansible_job._id) + .set('authorization', `Bearer ${token}`) .expect(200) - .expect('Content-Type', /json/) + //.expect('Content-Type', /json/) .end((err, res) => { if(err) { return done(err); } - ansible = res.body; + ansible_job = res.body; + console.log("ansible_job " + JSON.stringify(ansible_job)); done(); }); }); - afterEach(function() { - ansible = {}; - }); - it('should respond with the requested ansible', function() { - expect(ansible.name).to.equal('New Ansible'); - expect(ansible.info).to.equal('This is the brand new ansible!!!'); + it('should respond with an Ansible Job object', function() { + expect(ansible_job.selectedPlaybook).to.equal('test_playbook.yml'); }); }); - describe('PUT /api/ansible/:id', function() { - var updatedAnsible; + describe('POST /playbook/delete', function() { + var result; beforeEach(function(done) { request(app) - .put(`/api/ansible/${newAnsible._id}`) + .post('/api/ansible/playbook/delete') + .set('authorization', `Bearer ${token}`) .send({ - name: 'Updated Ansible', - info: 'This is the updated ansible!!!' + ansibleEngine: { + host : '', + projectFolder: '/tmp' + }, + playbookName: 'test_playbook.yml', }) .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if(err) { - return done(err); - } - updatedAnsible = res.body; - done(); - }); - }); - - afterEach(function() { - updatedAnsible = {}; - }); - - it('should respond with the updated ansible', function() { - expect(updatedAnsible.name).to.equal('Updated Ansible'); - expect(updatedAnsible.info).to.equal('This is the updated ansible!!!'); - }); - - it('should respond with the updated ansible on a subsequent GET', function(done) { - request(app) - .get(`/api/ansible/${newAnsible._id}`) - .expect(200) - .expect('Content-Type', /json/) + //.expect('Content-Type', /json/) .end((err, res) => { if(err) { return done(err); } - let ansible = res.body; - - expect(ansible.name).to.equal('Updated Ansible'); - expect(ansible.info).to.equal('This is the updated ansible!!!'); - + result = res; done(); }); }); + + + it('should respond with status code 200', function() { + expect(result.status).to.equal(200); + }); }); - describe('PATCH /api/ansible/:id', function() { - var patchedAnsible; + + describe('POST /inventory/delete', function() { + var result; beforeEach(function(done) { request(app) - .patch(`/api/ansible/${newAnsible._id}`) - .send([ - { op: 'replace', path: '/name', value: 'Patched Ansible' }, - { op: 'replace', path: '/info', value: 'This is the patched ansible!!!' } - ]) + .post('/api/ansible/inventory/delete') + .set('authorization', `Bearer ${token}`) + .send({ + ansibleEngine: { + host : '', + projectFolder: '/tmp' + }, + inventoryName: 'inventory.txt', + }) .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { + //.expect('Content-Type', /json/) + .end((err, res) => { if(err) { return done(err); } - patchedAnsible = res.body; + result = res; done(); }); }); - afterEach(function() { - patchedAnsible = {}; - }); - it('should respond with the patched ansible', function() { - expect(patchedAnsible.name).to.equal('Patched Ansible'); - expect(patchedAnsible.info).to.equal('This is the patched ansible!!!'); + it('should respond with status code 200', function() { + expect(result.status).to.equal(200); }); }); - describe('DELETE /api/ansible/:id', function() { - it('should respond with 204 on successful removal', function(done) { - request(app) - .delete(`/api/ansible/${newAnsible._id}`) - .expect(204) - .end(err => { - if(err) { - return done(err); - } - done(); - }); - }); + //TODO: Add more Ansible test cases here - it('should respond with 404 when ansible does not exist', function(done) { - request(app) - .delete(`/api/ansible/${newAnsible._id}`) - .expect(404) - .end(err => { - if(err) { - return done(err); - } - done(); - }); - }); - }); }); diff --git a/server/api/ansible/index.js b/server/api/ansible/index.js index bf06fc3..e7aa2d8 100644 --- a/server/api/ansible/index.js +++ b/server/api/ansible/index.js @@ -5,9 +5,22 @@ var controller = require('./ansible.controller'); var router = express.Router(); -router.get('/', controller.index); +// List, create and get Ansible Jobs +router.get('/runs', controller.index); +router.get('/:id', controller.show); +router.post('/', controller.create); +router.put('/:id', controller.upsert); +router.patch('/:id', controller.patch); +router.delete('/:id', controller.destroy); -router.post('/modules', controller.modules); +// Ansible Command line APIs +// - Create and modify inventory files +// - Create and modify playbooks +// - Create and modify roles +// - List tags +// - Create and modify files +// - Create and modify Var files +router.post('/modules/list', controller.modules); router.post('/command', controller.command); router.post('/execute', controller.execute); @@ -50,10 +63,5 @@ router.post('/vars/roles/get', controller.get_roles_vars); router.get('/logs/:id', controller.getLogs); -router.get('/:id', controller.show); -router.post('/', controller.create); -router.put('/:id', controller.upsert); -router.patch('/:id', controller.patch); -router.delete('/:id', controller.destroy); module.exports = router; diff --git a/server/api/ansible/index.spec.js b/server/api/ansible/index.spec.js index 86817fc..0d4aa0c 100644 --- a/server/api/ansible/index.spec.js +++ b/server/api/ansible/index.spec.js @@ -10,7 +10,15 @@ var ansibleCtrlStub = { create: 'ansibleCtrl.create', upsert: 'ansibleCtrl.upsert', patch: 'ansibleCtrl.patch', - destroy: 'ansibleCtrl.destroy' + destroy: 'ansibleCtrl.destroy', + modules: 'ansibleCtrl.modules', + command: 'ansibleCtrl.command', + execute: 'ansibleCtrl.execute', + project_files: 'ansibleCtrl.project_files', + playbook_get: 'ansibleCtrl.playbook_get', + playbook_create: 'ansibleCtrl.playbook_create', + playbook_delete: 'ansibleCtrl.playbook_delete', + playbook_list: 'ansibleCtrl.playbook_list', }; var routerStub = { @@ -18,7 +26,15 @@ var routerStub = { put: sinon.spy(), patch: sinon.spy(), post: sinon.spy(), - delete: sinon.spy() + delete: sinon.spy(), + modules: sinon.spy(), + command: sinon.spy(), + execute: sinon.spy(), + project_files: sinon.spy(), + playbook_get: sinon.spy(), + playbook_create: sinon.spy(), + playbook_delete: sinon.spy(), + playbook_list: sinon.spy() }; // require the index with our stubbed out modules @@ -36,51 +52,70 @@ describe('Ansible API Router:', function() { expect(ansibleIndex).to.equal(routerStub); }); - describe('GET /api/ansible', function() { - it('should route to ansible.controller.index', function() { - expect(routerStub.get - .withArgs('/', 'ansibleCtrl.index') - ).to.have.been.calledOnce; - }); - }); - - describe('GET /api/ansible/:id', function() { - it('should route to ansible.controller.show', function() { - expect(routerStub.get - .withArgs('/:id', 'ansibleCtrl.show') - ).to.have.been.calledOnce; - }); - }); - - describe('POST /api/ansible', function() { - it('should route to ansible.controller.create', function() { + describe('POST /api/ansible/modules/list', function() { + it('should route to ansible.controller.modules', function() { expect(routerStub.post - .withArgs('/', 'ansibleCtrl.create') + .withArgs('/modules/list', 'ansibleCtrl.modules') ).to.have.been.calledOnce; }); }); - describe('PUT /api/ansible/:id', function() { - it('should route to ansible.controller.upsert', function() { - expect(routerStub.put - .withArgs('/:id', 'ansibleCtrl.upsert') - ).to.have.been.calledOnce; + describe('POST /api/ansible/command', function() { + it('should route to ansible.controller.command', function() { + expect(routerStub.post + .withArgs('/command', 'ansibleCtrl.command') + ).to.have.been.calledOnce; }); }); - describe('PATCH /api/ansible/:id', function() { - it('should route to ansible.controller.patch', function() { - expect(routerStub.patch - .withArgs('/:id', 'ansibleCtrl.patch') - ).to.have.been.calledOnce; + describe('POST /api/ansible/execute', function() { + it('should route to ansible.controller.execute', function() { + expect(routerStub.post + .withArgs('/execute', 'ansibleCtrl.execute') + ).to.have.been.calledOnce; }); }); - describe('DELETE /api/ansible/:id', function() { - it('should route to ansible.controller.destroy', function() { - expect(routerStub.delete - .withArgs('/:id', 'ansibleCtrl.destroy') - ).to.have.been.calledOnce; + describe('POST /api/ansible/project/files', function() { + it('should route to ansible.controller.project_files', function() { + expect(routerStub.post + .withArgs('/project/files', 'ansibleCtrl.project_files') + ).to.have.been.calledOnce; }); }); + + describe('POST /api/ansible/playbook/get', function() { + it('should route to ansible.controller.playbook_get', function() { + expect(routerStub.post + .withArgs('/playbook/get', 'ansibleCtrl.playbook_get') + ).to.have.been.calledOnce; + }); + }); + + describe('POST /api/ansible/playbook/create', function() { + it('should route to ansible.controller.playbook_create', function() { + expect(routerStub.post + .withArgs('/playbook/create', 'ansibleCtrl.playbook_create') + ).to.have.been.calledOnce; + }); + }); + + describe('POST /api/ansible/playbook/delete', function() { + it('should route to ansible.controller.playbook_delete', function() { + expect(routerStub.post + .withArgs('/playbook/delete', 'ansibleCtrl.playbook_delete') + ).to.have.been.calledOnce; + }); + }); + + describe('POST /api/ansible/playbook/list', function() { + it('should route to ansible.controller.playbook_list', function() { + expect(routerStub.post + .withArgs('/playbook/list', 'ansibleCtrl.playbook_list') + ).to.have.been.calledOnce; + }); + }); + + //TODO: Add the remaining test cases here + }); diff --git a/server/api/custom_module/custom_module.controller.js b/server/api/custom_module/custom_module.controller.js index 757cf83..d51f099 100644 --- a/server/api/custom_module/custom_module.controller.js +++ b/server/api/custom_module/custom_module.controller.js @@ -93,43 +93,38 @@ export function index(req, res) { .catch(handleError(res));*/ } -// Gets a single CustomModule from the DB +// Gets a single CustomModule or a module_template from DB export function show(req, res) { console.log("Show " + req.params.custom_module); var ansibleEngine = req.body.ansibleEngine; if(!ansibleEngine.customModules){ - res.status(500).send("Custom Modules Folder not defined in Ansible Engine") + return res.status(500).send("Custom Modules Folder not defined in Ansible Engine") } var command = 'cat "' + ansibleEngine.customModules + '"/' + req.params.custom_module; + // If request is for module template, return module_template from default path if(req.params.custom_module === 'template.py'){ - //command = 'cat ' + '/opt/ehc-builder-scripts/ansible_modules/template.py'; return require('fs').readFile('./helpers/module_template.py', (err, data) => { - if (err) res.status(500).send(data); + if (err) return res.status(500).send(data); res.send(data); }); + }else{ + ssh2_exec.executeCommand(command, + null, + function(data){ + res.send(data); + }, + function(data){ + res.status(500).send(data) + }, + ansibleEngine + ); } - - ssh2_exec.executeCommand(command, - null, - function(data){ - res.send(data); - }, - function(data){ - res.status(500).send(data) - }, - ansibleEngine - ); - - /*return CustomModule.findById(req.params.custom_module).exec() - .then(handleEntityNotFound(res)) - .then(respondWithResult(res)) - .catch(handleError(res));*/ } // Test Module @@ -145,7 +140,7 @@ export function testModule(req, res) { var test_module = '/tmp/test-module'; - var command = 'chmod 755 ' + test_module + '; ' + test_module + ' -m "' + ansibleEngine.customModules + '/' + req.params.custom_module + "\" -a '" + JSON.stringify(moduleArgs) + "'"; + var command = 'chmod 755 ' + test_module + '; python ' + test_module + ' -m "' + ansibleEngine.customModules + '/' + req.params.custom_module + "\" -a '" + JSON.stringify(moduleArgs) + "'"; scp2_exec.copyFileToScriptEngine('./helpers/test-module',test_module,ansibleEngine,function(){ console.log("Command=" + command); @@ -174,8 +169,6 @@ export function testModule(req, res) { // Creates a new CustomModule in the DB export function create(req, res) { - console.log("Create"); - var custom_module_name = req.params.custom_module; var custom_module_code = req.body.custom_module_code; diff --git a/server/api/custom_module/custom_module.integration.js b/server/api/custom_module/custom_module.integration.js index 5454c26..24dd78b 100644 --- a/server/api/custom_module/custom_module.integration.js +++ b/server/api/custom_module/custom_module.integration.js @@ -4,187 +4,168 @@ var app = require('../..'); import request from 'supertest'; +import User from '../user/user.model'; var newCustomModule; describe('CustomModule API:', function() { - describe('GET /api/custom_modules', function() { + var token; + var user; + + // Clear users before testing + before(function() { + return User.remove().then(function() { + user = new User({ + name: 'Fake User', + email: 'test@example.com', + password: 'password' + }); + + return user.save(); + }); + }); + + // Clear users after testing + after(function() { + return User.remove(); + }); + + describe('GET /api/users/me', function() { + + before(function(done) { + request(app) + .post('/auth/local') + .send({ + email: 'test@example.com', + password: 'password' + }) + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + token = res.body.token; + done(); + }); + }); + + it('should respond with a user profile when authenticated', function(done) { + request(app) + .get('/api/users/me') + .set('authorization', `Bearer ${token}`) + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + expect(res.body._id.toString()).to.equal(user._id.toString()); + done(); + }); + }); + + it('should respond with a 401 when not authenticated', function(done) { + request(app) + .get('/api/users/me') + .expect(401) + .end(done); + }); + }); + + + describe('POST /api/custom_modules/template.py/get', function() { var customModules; beforeEach(function(done) { request(app) - .get('/api/custom_modules') + .post('/api/custom_modules/template.py/get') + .set('authorization', `Bearer ${token}`) .expect(200) - .expect('Content-Type', /json/) - .end((err, res) => { - if(err) { - return done(err); - } - customModules = res.body; - done(); - }); - }); - - it('should respond with JSON array', function() { - expect(customModules).to.be.instanceOf(Array); - }); - }); - - describe('POST /api/custom_modules', function() { - beforeEach(function(done) { - request(app) - .post('/api/custom_modules') .send({ - name: 'New CustomModule', - info: 'This is the brand new customModule!!!' + ansibleEngine: { + host : '', + customModules: '/' + } }) - .expect(201) - .expect('Content-Type', /json/) + //.expect('Content-Type', /json/) .end((err, res) => { if(err) { return done(err); } - newCustomModule = res.body; + customModules = res.text; done(); }); }); - it('should respond with the newly created customModule', function() { - expect(newCustomModule.name).to.equal('New CustomModule'); - expect(newCustomModule.info).to.equal('This is the brand new customModule!!!'); + it('should respond with module_template', function() { + expect(customModules).to.contain('import AnsibleModule'); }); }); - describe('GET /api/custom_modules/:id', function() { - var customModule; + + describe('POST /api/custom_modules/test_module.py', function() { + var customModules; beforeEach(function(done) { request(app) - .get(`/api/custom_modules/${newCustomModule._id}`) + .post('/api/custom_modules/test_module.py') + .set('authorization', `Bearer ${token}`) .expect(200) - .expect('Content-Type', /json/) - .end((err, res) => { - if(err) { - return done(err); - } - customModule = res.body; - done(); - }); - }); - - afterEach(function() { - customModule = {}; - }); - - it('should respond with the requested customModule', function() { - expect(customModule.name).to.equal('New CustomModule'); - expect(customModule.info).to.equal('This is the brand new customModule!!!'); - }); - }); - - describe('PUT /api/custom_modules/:id', function() { - var updatedCustomModule; - - beforeEach(function(done) { - request(app) - .put(`/api/custom_modules/${newCustomModule._id}`) .send({ - name: 'Updated CustomModule', - info: 'This is the updated customModule!!!' + ansibleEngine: { + host : '', + customModules: '/tmp' + }, + custom_module_code: '#!/usr/bin/python\n' + + '\n' + + 'import datetime\n' + + 'import json\n' + + '\n' + + 'date = str(datetime.datetime.now())\n' + + 'print(json.dumps({\n' + + '"time" : date\n' + + '}))' }) - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if(err) { - return done(err); - } - updatedCustomModule = res.body; - done(); - }); - }); - - afterEach(function() { - updatedCustomModule = {}; - }); - - it('should respond with the updated customModule', function() { - expect(updatedCustomModule.name).to.equal('Updated CustomModule'); - expect(updatedCustomModule.info).to.equal('This is the updated customModule!!!'); - }); - - it('should respond with the updated customModule on a subsequent GET', function(done) { - request(app) - .get(`/api/custom_modules/${newCustomModule._id}`) - .expect(200) - .expect('Content-Type', /json/) + //.expect('Content-Type', /json/) .end((err, res) => { if(err) { return done(err); } - let customModule = res.body; - - expect(customModule.name).to.equal('Updated CustomModule'); - expect(customModule.info).to.equal('This is the updated customModule!!!'); - + customModules = res.text; done(); }); }); + + it('should respond with "Saved"', function() { + expect(customModules).to.contain('Saved'); + }); }); - describe('PATCH /api/custom_modules/:id', function() { - var patchedCustomModule; + describe('POST /api/custom_modules/test_module.py/test', function() { + var result; beforeEach(function(done) { request(app) - .patch(`/api/custom_modules/${newCustomModule._id}`) - .send([ - { op: 'replace', path: '/name', value: 'Patched CustomModule' }, - { op: 'replace', path: '/info', value: 'This is the patched customModule!!!' } - ]) + .post('/api/custom_modules/test_module.py/test') + .set('authorization', `Bearer ${token}`) .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { + .send({ + ansibleEngine: { + host : '', + customModules: '/tmp' + }, + moduleArgs: {} + }) + //.expect('Content-Type', /json/) + .end((err, res) => { if(err) { return done(err); } - patchedCustomModule = res.body; + result = res; done(); }); }); - afterEach(function() { - patchedCustomModule = {}; - }); - - it('should respond with the patched customModule', function() { - expect(patchedCustomModule.name).to.equal('Patched CustomModule'); - expect(patchedCustomModule.info).to.equal('This is the patched customModule!!!'); + it('should respond with 200', function() { + expect(result.status).to.equal(200); }); }); - describe('DELETE /api/custom_modules/:id', function() { - it('should respond with 204 on successful removal', function(done) { - request(app) - .delete(`/api/custom_modules/${newCustomModule._id}`) - .expect(204) - .end(err => { - if(err) { - return done(err); - } - done(); - }); - }); + //TODO: Add more test cases - it('should respond with 404 when customModule does not exist', function(done) { - request(app) - .delete(`/api/custom_modules/${newCustomModule._id}`) - .expect(404) - .end(err => { - if(err) { - return done(err); - } - done(); - }); - }); - }); }); diff --git a/server/api/custom_module/index.js b/server/api/custom_module/index.js index 6bdc2d1..130f614 100644 --- a/server/api/custom_module/index.js +++ b/server/api/custom_module/index.js @@ -5,7 +5,7 @@ var controller = require('./custom_module.controller'); var router = express.Router(); -router.post('/query', controller.index); +router.post('/list', controller.index); router.post('/:custom_module/test', controller.testModule); router.post('/:custom_module/get', controller.show); router.post('/:custom_module', controller.create); diff --git a/server/api/custom_module/index.spec.js b/server/api/custom_module/index.spec.js index 08d2cf8..6dbd08f 100644 --- a/server/api/custom_module/index.spec.js +++ b/server/api/custom_module/index.spec.js @@ -36,10 +36,10 @@ describe('CustomModule API Router:', function() { expect(customModuleIndex).to.equal(routerStub); }); - describe('GET /api/custom_modules/query', function() { + describe('GET /api/custom_modules/list', function() { it('should route to customModule.controller.index', function() { expect(routerStub.post - .withArgs('/query', 'customModuleCtrl.index') + .withArgs('/list', 'customModuleCtrl.index') ).to.have.been.calledOnce; }); }); diff --git a/server/api/project/project.controller.js b/server/api/project/project.controller.js index 07b0bb4..bf53f14 100644 --- a/server/api/project/project.controller.js +++ b/server/api/project/project.controller.js @@ -13,6 +13,7 @@ import jsonpatch from 'fast-json-patch'; import Project from './project.model'; import config from '../../config/environment'; +const util = require('util'); var ansibleTool = require('../../components/ansible/ansible_tool'); function respondWithResult(res, statusCode) { @@ -50,6 +51,7 @@ function removeEntity(res) { } function handleEntityNotFound(res) { + console.log("Entity Not Found"); return function(entity) { if(!entity) { res.status(404).end(); @@ -62,6 +64,7 @@ function handleEntityNotFound(res) { function handleError(res, statusCode) { statusCode = statusCode || 500; return function(err) { + console.log("ERror " + err); res.status(statusCode).send(err); }; } @@ -93,13 +96,18 @@ export function show(req, res) { } -// Creates a new Project in the DB +/** + * Create New Project + * - If Ansible Engine information is provided use that, else consider localhost as Ansible Engine + * - Identify/generate project and library (custom modules location) + * - Get Ansible version and create projects folder + * @param req + * @param res + */ export function create(req, res) { var ansibleEngine = req.body.ansibleEngine; - console.log("Ansible Engine " + JSON.stringify(ansibleEngine)); - req.body.owner_id = req.user._id; req.body.owner_name = req.user.name; @@ -118,18 +126,22 @@ export function create(req, res) { }; } - + // If projectFolder is not passed, create a custom project folder if(!ansibleEngine.projectFolder){ - ansibleEngine.projectFolder = '/opt/ansible-projects/' + req.user._id + '_' + req.body.name; - ansibleEngine.customModules = '/opt/ansible-projects/' + req.user._id + '_' + req.body.name + '/library'; + let projectFolderName = util.format('%s_%s',req.user._id, req.body.name); + + ansibleEngine.projectFolder = util.format('/opt/ansible-projects/test_%s', projectFolderName); + ansibleEngine.customModules = util.format('/opt/ansible-projects/test_%s/library', projectFolderName); // Update project request body to save in db req.body.ansibleEngine.projectFolder = ansibleEngine.projectFolder; req.body.ansibleEngine.customModules = ansibleEngine.customModules; + req.body.ansibleEngine.projectFolderName = projectFolderName; } - + // Allow creating project if no host is passed. Then use the default Ansible Engine for all operations. + // If Ansible host is passed get Ansible version and create project folder if(ansibleEngine.ansibleHost){ ansibleTool.getAnsibleVersion( function(version){ @@ -184,10 +196,16 @@ export function patch(req, res) { .catch(handleError(res)); } + + // Deletes a Project from the DB export function destroy(req, res) { return Project.findById(req.params.id).exec() .then(handleEntityNotFound(res)) + .then(function(entity){ + if(!entity)return null; + return ansibleTool.deleteProjectFolder(entity); + }) .then(removeEntity(res)) .catch(handleError(res)); } diff --git a/server/api/project/project.integration.js b/server/api/project/project.integration.js index 5616f23..941cdd4 100644 --- a/server/api/project/project.integration.js +++ b/server/api/project/project.integration.js @@ -4,16 +4,92 @@ var app = require('../..'); import request from 'supertest'; +import Project from './project.model'; +import User from '../user/user.model'; var newProject; describe('Project API:', function() { + var token; + var user; + + // Clear users before testing + before(function() { + return User.remove().then(function() { + user = new User({ + name: 'Fake User', + email: 'test@example.com', + password: 'password' + }); + + return user.save(); + }); + }); + + // Clear users after testing + after(function() { + return User.remove(); + }); + + describe('GET /api/users/me', function() { + + before(function(done) { + request(app) + .post('/auth/local') + .send({ + email: 'test@example.com', + password: 'password' + }) + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + token = res.body.token; + done(); + }); + }); + + it('should respond with a user profile when authenticated', function(done) { + request(app) + .get('/api/users/me') + .set('authorization', `Bearer ${token}`) + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + expect(res.body._id.toString()).to.equal(user._id.toString()); + done(); + }); + }); + + it('should respond with a 401 when not authenticated', function(done) { + request(app) + .get('/api/users/me') + .expect(401) + .end(done); + }); + }); + describe('GET /api/projects', function() { var projects; + // Clear Projects before testing + before(function() { + return Project.remove().then(function() { + var project = new Project({ + name: 'FakeProject', + info: 'Test Project', + ansibleEngine: { + 'host' : '' + } + }); + + return project.save(); + }); + }); + beforeEach(function(done) { request(app) .get('/api/projects') + .set('authorization', `Bearer ${token}`) .expect(200) .expect('Content-Type', /json/) .end((err, res) => { @@ -34,9 +110,13 @@ describe('Project API:', function() { beforeEach(function(done) { request(app) .post('/api/projects') + .set('authorization', `Bearer ${token}`) .send({ - name: 'New Project', - info: 'This is the brand new project!!!' + name: 'NewProject', + info: 'This is the brand new project!!!', + ansibleEngine: { + 'host' : '' + } }) .expect(201) .expect('Content-Type', /json/) @@ -50,7 +130,7 @@ describe('Project API:', function() { }); it('should respond with the newly created project', function() { - expect(newProject.name).to.equal('New Project'); + expect(newProject.name).to.equal('NewProject'); expect(newProject.info).to.equal('This is the brand new project!!!'); }); }); @@ -61,6 +141,7 @@ describe('Project API:', function() { beforeEach(function(done) { request(app) .get(`/api/projects/${newProject._id}`) + .set('authorization', `Bearer ${token}`) .expect(200) .expect('Content-Type', /json/) .end((err, res) => { @@ -77,7 +158,7 @@ describe('Project API:', function() { }); it('should respond with the requested project', function() { - expect(project.name).to.equal('New Project'); + expect(project.name).to.equal('NewProject'); expect(project.info).to.equal('This is the brand new project!!!'); }); }); @@ -88,6 +169,7 @@ describe('Project API:', function() { beforeEach(function(done) { request(app) .put(`/api/projects/${newProject._id}`) + .set('authorization', `Bearer ${token}`) .send({ name: 'Updated Project', info: 'This is the updated project!!!' @@ -115,6 +197,7 @@ describe('Project API:', function() { it('should respond with the updated project on a subsequent GET', function(done) { request(app) .get(`/api/projects/${newProject._id}`) + .set('authorization', `Bearer ${token}`) .expect(200) .expect('Content-Type', /json/) .end((err, res) => { @@ -137,6 +220,7 @@ describe('Project API:', function() { beforeEach(function(done) { request(app) .patch(`/api/projects/${newProject._id}`) + .set('authorization', `Bearer ${token}`) .send([ { op: 'replace', path: '/name', value: 'Patched Project' }, { op: 'replace', path: '/info', value: 'This is the patched project!!!' } @@ -166,6 +250,7 @@ describe('Project API:', function() { it('should respond with 204 on successful removal', function(done) { request(app) .delete(`/api/projects/${newProject._id}`) + .set('authorization', `Bearer ${token}`) .expect(204) .end(err => { if(err) { @@ -178,6 +263,7 @@ describe('Project API:', function() { it('should respond with 404 when project does not exist', function(done) { request(app) .delete(`/api/projects/${newProject._id}`) + .set('authorization', `Bearer ${token}`) .expect(404) .end(err => { if(err) { diff --git a/server/api/user/user.controller.js b/server/api/user/user.controller.js index c1ab134..a83edb1 100644 --- a/server/api/user/user.controller.js +++ b/server/api/user/user.controller.js @@ -104,8 +104,11 @@ export function changePassword(req, res) { export function me(req, res, next) { var userId = req.user._id; + console.log("Find User =" + JSON.stringify(req.user)); + return User.findOne({ _id: userId }, '-salt -password').exec() .then(user => { // don't ever give out the password or salt + console.log("User =" + JSON.stringify(user)); if(!user) { return res.status(401).end(); } diff --git a/spec.js b/spec.js index 5b64e0c..f0d6189 100644 --- a/spec.js +++ b/spec.js @@ -8,5 +8,5 @@ require('angular-mocks'); require('./client/components/ui-router/ui-router.mock'); -testsContext = require.context('./client', true, /\.spec\.js$/); +testsContext = require.context('./client', true, /main.component.spec\.js$/); testsContext.keys().forEach(testsContext);