From 265e10a036b545d9d4e15bebef17e38e4b013af3 Mon Sep 17 00:00:00 2001 From: thuva4 Date: Tue, 14 Nov 2017 15:38:56 +0530 Subject: Create functionality and e2e tests for project Implemented the create function for the projects. Wrote the e2e tests for the create function. Change-Id: Iceac650573ca31b6246350c4d60033b42e0ffb0f Signed-off-by: thuva4 --- testapi/opnfv_testapi/common/check.py | 8 +- testapi/opnfv_testapi/common/message.py | 4 + testapi/opnfv_testapi/handlers/base_handlers.py | 2 +- .../tests/UI/e2e/podsControllerSpec.js | 23 ++- .../tests/UI/e2e/projectControllerSpec.js | 224 +++++++++++++++++++++ .../opnfv_testapi/tests/unit/handlers/test_base.py | 8 + .../tests/unit/handlers/test_project.py | 30 ++- .../tests/unit/handlers/test_result.py | 6 +- .../tests/unit/handlers/test_testcase.py | 11 +- testapi/opnfv_testapi/ui/projects/projects.html | 38 ++++ .../ui/projects/projectsController.js | 74 +++++++ 11 files changed, 402 insertions(+), 26 deletions(-) create mode 100644 testapi/opnfv_testapi/tests/UI/e2e/projectControllerSpec.js create mode 100644 testapi/opnfv_testapi/ui/projects/projects.html create mode 100644 testapi/opnfv_testapi/ui/projects/projectsController.js (limited to 'testapi/opnfv_testapi') diff --git a/testapi/opnfv_testapi/common/check.py b/testapi/opnfv_testapi/common/check.py index 667578f..432a6c1 100644 --- a/testapi/opnfv_testapi/common/check.py +++ b/testapi/opnfv_testapi/common/check.py @@ -21,7 +21,7 @@ from opnfv_testapi.db import api as dbapi def is_authorized(method): @functools.wraps(method) def wrapper(self, *args, **kwargs): - if CONF.api_authenticate and self.table in ['pods']: + if CONF.api_authenticate and self.table in ['pods', 'projects']: testapi_id = self.get_secure_cookie(constants.TESTAPI_ID) if not testapi_id: raises.Unauthorized(message.not_login()) @@ -29,6 +29,12 @@ def is_authorized(method): if not user_info: raises.Unauthorized(message.not_lfid()) kwargs['owner'] = testapi_id + if self.table in ['projects']: + query = kwargs.get('query') + query_data = query() + group = "opnfv-gerrit-" + query_data['name'] + "-submitters" + if group not in user_info['groups']: + raises.Unauthorized(message.no_permission()) ret = yield gen.coroutine(method)(self, *args, **kwargs) raise gen.Return(ret) return wrapper diff --git a/testapi/opnfv_testapi/common/message.py b/testapi/opnfv_testapi/common/message.py index 3e14f72..b92b7f0 100644 --- a/testapi/opnfv_testapi/common/message.py +++ b/testapi/opnfv_testapi/common/message.py @@ -60,3 +60,7 @@ def no_update(): def must_int(name): return '{} must be int'.format(name) + + +def no_permission(): + return 'You do not have permission to perform this action' diff --git a/testapi/opnfv_testapi/handlers/base_handlers.py b/testapi/opnfv_testapi/handlers/base_handlers.py index a8ee3db..df7f520 100644 --- a/testapi/opnfv_testapi/handlers/base_handlers.py +++ b/testapi/opnfv_testapi/handlers/base_handlers.py @@ -75,10 +75,10 @@ class GenericApiHandler(web.RequestHandler): @web.asynchronous @gen.coroutine - @check.is_authorized @check.valid_token @check.no_body @check.miss_fields + @check.is_authorized @check.values_check @check.carriers_exist @check.new_not_exists diff --git a/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js index 66a57f2..8cf7467 100644 --- a/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js +++ b/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js @@ -12,7 +12,9 @@ describe('testing the Pods page for anonymous user', function () { }, response: { data: { - pods: [{role: "community-ci", name: "test", owner: "testUser", details: "DemoDetails", mode: "metal", _id: "59f02f099a07c84bfc5c7aed", creation_date: "2017-10-25 11:58:25.926168"}] + pods: [{role: "community-ci", name: "test", owner: "testUser", + details: "DemoDetails", mode: "metal", _id: "59f02f099a07c84bfc5c7aed", + creation_date: "2017-10-25 11:58:25.926168"}] } } }]); @@ -73,7 +75,8 @@ describe('testing the Pods page for anonymous user', function () { mock.teardown(); var buttonFilter = element(by.buttonText('Filter')); buttonFilter.click().then(function(){ - expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope')).isDisplayed()).toBe(true); + expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope')) + .isDisplayed()).toBe(true); }); }); @@ -116,7 +119,9 @@ describe('testing the Pods page for authorized user', function () { }, response: { data: { - "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", "user": "testUser", "groups": ["opnfv-testapi-users"], "email": "testuser@test.com" + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users", + "opnfv-gerrit-functest-submitters"], "email": "testuser@test.com" } } } @@ -136,7 +141,8 @@ describe('testing the Pods page for authorized user', function () { details.sendKeys('DemoDetails'); var buttonCreate = element(by.buttonText('Create')); buttonCreate.click().then(function(){ - expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope')).isDisplayed()).toBe(false); + expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope')) + .isDisplayed()).toBe(false); }); }); @@ -170,7 +176,9 @@ describe('testing the Pods page for authorized user', function () { }, response: { data: { - "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", "user": "testUser", "groups": ["opnfv-testapi-users"], "email": "testuser@test.com" + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users"], + "email": "testuser@test.com" } } } @@ -182,7 +190,8 @@ describe('testing the Pods page for authorized user', function () { details.sendKeys('DemoDetails'); var buttonCreate = element(by.buttonText('Create')); buttonCreate.click().then(function(){ - expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope')).isDisplayed()).toBe(true); + expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope')) + .isDisplayed()).toBe(true); }); - }) + }); }); \ No newline at end of file diff --git a/testapi/opnfv_testapi/tests/UI/e2e/projectControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/projectControllerSpec.js new file mode 100644 index 0000000..921625d --- /dev/null +++ b/testapi/opnfv_testapi/tests/UI/e2e/projectControllerSpec.js @@ -0,0 +1,224 @@ +'use strict'; + +var mock = require('protractor-http-mock'); +var baseURL = "http://localhost:8000" + +describe('testing the Project Link for anonymous user', function () { + + it( 'should not show the Project Link for anonymous user', function() { + mock.teardown(); + browser.get(baseURL); + var projectslink = element(by.linkText('Projects')); + expect(projectslink.isPresent()).toBe(true); + }); + + it( 'navigate anonymous user to project page', function() { + browser.get(baseURL+'#/projects'); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '/#/projects'), 10000); + }); + + it('create button is not visible for anonymous user ', function () { + browser.get(baseURL+'#/projects'); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBeFalsy(); + }); + +}); + +describe('testing the Project Link for user who is not in submitter group', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users"], + "email": "testuser@test.com" + } + } + } + ]); + }); + + it( 'should show the Project Link for user', function() { + browser.get(baseURL); + var projectslink = element(by.linkText('Projects')); + expect(projectslink.isPresent()).toBe(true); + }); + + it( 'should navigate the user to the Project page', function() { + browser.get(baseURL); + var projectslink = element(by.linkText('Projects')).click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '/#/projects'), 10000); + }); + + it('create button is not visible for user', function () { + browser.get(baseURL+'#/projects'); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBeFalsy(); + }); +}) + +describe('testing the Project Link for user who is in submitter group', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users", + "opnfv-gerrit-testProject1-submitters", + "opnfv-gerrit-testProject2-submitters" ], + "email": "testuser@test.com" + } + } + }, + { + request: { + path: '/api/v1/projects', + method: 'POST' + }, + response: { + data: { + href: baseURL+"/api/v1/projects/testProject1" + } + } + }, + { + request: { + path: '/api/v1/projects', + method: 'POST', + data: { + name: 'testProject2', + description : 'demoDescription', + } + }, + response: { + status : 403 + } + }, + { + request: { + path: '/api/v1/projects', + method: 'POST', + data: { + name: 'testProject3', + description : 'demoDescription', + } + }, + response: { + status : 403, + data : 'You do not have permission to perform this action' + } + } + ]); + }); + + it( 'should show the Project Link for user', function() { + browser.get(baseURL); + var projectslink = element(by.linkText('Projects')); + expect(projectslink.isPresent()).toBe(true); + }); + + it( 'should navigate the user to the Project page', function() { + browser.get(baseURL); + var projectslink = element(by.linkText('Projects')).click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '/#/projects'), 10000); + }); + + it('create button is visible for user', function () { + browser.get(baseURL+'#/projects'); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBe(true); + }); + + it('Show error when user click the create button with a empty name', function () { + browser.get(baseURL+ '/#/projects'); + var description = element(by.model('ctrl.description')); + description.sendKeys('DemoDescription'); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click(); + expect(element(by.cssContainingText(".alert","Name is missing.")) + .isDisplayed()).toBe(true); + }); + + it('Show error when user click the create button with an already existing name', function () { + browser.get(baseURL+ '/#/projects'); + var name = element(by.model('ctrl.name')); + var details = element(by.model('ctrl.description')); + name.sendKeys('testProject2'); + details.sendKeys('demoDescription'); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click(); + expect(element(by.cssContainingText(".alert", + "Error creating the new Project from server:undefined")) + .isDisplayed()).toBe(true); + }); + + it('Show error when user try to create a project which he is not belonged to ', function () { + browser.get(baseURL+ '/#/projects'); + var name = element(by.model('ctrl.name')); + var details = element(by.model('ctrl.description')); + name.sendKeys('testProject3'); + details.sendKeys('demoDescription'); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click(); + expect(element(by.cssContainingText(".alert", + 'Error creating the new Project from server:"You do not have permission to perform this action"')).isDisplayed()) + .toBe(true); + }); + + it('Do not show error if input is acceptable', function () { + var name = element(by.model('ctrl.name')); + var details = element(by.model('ctrl.description')); + name.sendKeys('testProject1'); + details.sendKeys('demoDescription'); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click().then(function(){ + expect(element(by.cssContainingText(".alert", + "Create Success")) + .isDisplayed()).toBe(true); + }); + }); + + it('If backend is not responding then show error when user click the create button',function(){ + mock.teardown(); + mock([ + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users", + "opnfv-gerrit-testProject1-submitters", + "opnfv-gerrit-testProject2-submitters" ], + "email": "testuser@test.com" + } + } + } + ]); + browser.get(baseURL+ '/#/projects'); + var name = element(by.model('ctrl.name')); + var details = element(by.model('ctrl.description')); + name.sendKeys('testProject1'); + details.sendKeys('demoDescription'); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click().then(function(){ + expect(element(by.css(".alert.alert-danger.ng-binding.ng-scope")).isDisplayed()).toBe(true); + }); + }); +}) diff --git a/testapi/opnfv_testapi/tests/unit/handlers/test_base.py b/testapi/opnfv_testapi/tests/unit/handlers/test_base.py index b7fabb9..eb147cc 100644 --- a/testapi/opnfv_testapi/tests/unit/handlers/test_base.py +++ b/testapi/opnfv_testapi/tests/unit/handlers/test_base.py @@ -16,6 +16,7 @@ from tornado import testing from opnfv_testapi.models import base_models from opnfv_testapi.models import pod_models +from opnfv_testapi.models import project_models from opnfv_testapi.tests.unit import fake_pymongo @@ -43,6 +44,12 @@ class TestBase(testing.AsyncHTTPTestCase): _id=str(ObjectId()), owner='ValidUser', create_date=str(datetime.now())) + self.project_e = project_models.Project( + name='functest', + description='functest test', + _id=str(ObjectId()), + create_date=str(datetime.now())) + self.req_d = None self.req_e = None self.addCleanup(self._clear) @@ -53,6 +60,7 @@ class TestBase(testing.AsyncHTTPTestCase): 'groups': [ 'opnfv-testapi-users', 'opnfv-gerrit-functest-submitters', + 'opnfv-gerrit-qtip-submitters', 'opnfv-gerrit-qtip-contributors'] }) diff --git a/testapi/opnfv_testapi/tests/unit/handlers/test_project.py b/testapi/opnfv_testapi/tests/unit/handlers/test_project.py index 939cc0d..2873ab0 100644 --- a/testapi/opnfv_testapi/tests/unit/handlers/test_project.py +++ b/testapi/opnfv_testapi/tests/unit/handlers/test_project.py @@ -1,3 +1,11 @@ +############################################################################## +# Copyright (c) 2016 ZTE Corporation +# feng.xiaowei@zte.com.cn +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## import httplib import unittest @@ -10,10 +18,10 @@ from opnfv_testapi.tests.unit.handlers import test_base as base class TestProjectBase(base.TestBase): def setUp(self): super(TestProjectBase, self).setUp() - self.req_d = project_models.ProjectCreateRequest('vping', - 'vping-ssh test') - self.req_e = project_models.ProjectCreateRequest('doctor', - 'doctor test') + self.req_d = project_models.ProjectCreateRequest('qtip', + 'qtip-ssh test') + self.req_e = project_models.ProjectCreateRequest('functest', + 'functest test') self.get_res = project_models.Project self.list_res = project_models.Projects self.update_res = project_models.Project @@ -29,22 +37,32 @@ class TestProjectBase(base.TestBase): class TestProjectCreate(TestProjectBase): + + @executor.create(httplib.BAD_REQUEST, message.not_login()) + def test_notlogin(self): + return self.req_d + + @executor.mock_valid_lfid() @executor.create(httplib.BAD_REQUEST, message.no_body()) def test_withoutBody(self): return None + @executor.mock_valid_lfid() @executor.create(httplib.BAD_REQUEST, message.missing('name')) def test_emptyName(self): return project_models.ProjectCreateRequest('') + @executor.mock_valid_lfid() @executor.create(httplib.BAD_REQUEST, message.missing('name')) def test_noneName(self): return project_models.ProjectCreateRequest(None) + @executor.mock_valid_lfid() @executor.create(httplib.OK, 'assert_create_body') def test_success(self): return self.req_d + @executor.mock_valid_lfid() @executor.create(httplib.FORBIDDEN, message.exist_base) def test_alreadyExist(self): self.create_d() @@ -52,6 +70,8 @@ class TestProjectCreate(TestProjectBase): class TestProjectGet(TestProjectBase): + + @executor.mock_valid_lfid() def setUp(self): super(TestProjectGet, self).setUp() self.create_d() @@ -78,6 +98,7 @@ class TestProjectGet(TestProjectBase): class TestProjectUpdate(TestProjectBase): + @executor.mock_valid_lfid() def setUp(self): super(TestProjectUpdate, self).setUp() _, d_body = self.create_d() @@ -115,6 +136,7 @@ class TestProjectUpdate(TestProjectBase): class TestProjectDelete(TestProjectBase): + @executor.mock_valid_lfid() def setUp(self): super(TestProjectDelete, self).setUp() self.create_d() diff --git a/testapi/opnfv_testapi/tests/unit/handlers/test_result.py b/testapi/opnfv_testapi/tests/unit/handlers/test_result.py index b9f9ede..bd482a6 100644 --- a/testapi/opnfv_testapi/tests/unit/handlers/test_result.py +++ b/testapi/opnfv_testapi/tests/unit/handlers/test_result.py @@ -15,7 +15,6 @@ import urllib import unittest from opnfv_testapi.common import message -from opnfv_testapi.models import project_models from opnfv_testapi.models import result_models from opnfv_testapi.models import testcase_models from opnfv_testapi.tests.unit import executor @@ -86,15 +85,12 @@ class TestResultBase(base.TestBase): self.list_res = result_models.TestResults self.update_res = result_models.TestResult self.basePath = '/api/v1/results' - self.req_project = project_models.ProjectCreateRequest( - self.project, - 'vping test') + fake_pymongo.projects.insert(self.project_e.format()) self.req_testcase = testcase_models.TestcaseCreateRequest( self.case, '/cases/vping', 'vping-ssh test') fake_pymongo.pods.insert(self.pod_d.format()) - self.create_help('/api/v1/projects', self.req_project) self.create_help('/api/v1/projects/%s/cases', self.req_testcase, self.project) diff --git a/testapi/opnfv_testapi/tests/unit/handlers/test_testcase.py b/testapi/opnfv_testapi/tests/unit/handlers/test_testcase.py index e4c668e..d5e32e3 100644 --- a/testapi/opnfv_testapi/tests/unit/handlers/test_testcase.py +++ b/testapi/opnfv_testapi/tests/unit/handlers/test_testcase.py @@ -11,15 +11,16 @@ import httplib import unittest from opnfv_testapi.common import message -from opnfv_testapi.models import project_models from opnfv_testapi.models import testcase_models from opnfv_testapi.tests.unit import executor +from opnfv_testapi.tests.unit import fake_pymongo from opnfv_testapi.tests.unit.handlers import test_base as base class TestCaseBase(base.TestBase): def setUp(self): super(TestCaseBase, self).setUp() + self.project = 'functest' self.req_d = testcase_models.TestcaseCreateRequest('vping_1', '/cases/vping_1', 'vping-ssh test') @@ -36,7 +37,7 @@ class TestCaseBase(base.TestBase): self.list_res = testcase_models.Testcases self.update_res = testcase_models.Testcase self.basePath = '/api/v1/projects/%s/cases' - self.create_project() + fake_pymongo.projects.insert(self.project_e.format()) def assert_body(self, case, req=None): if not req: @@ -56,12 +57,6 @@ class TestCaseBase(base.TestBase): self.assertIsNotNone(new._id) self.assertIsNotNone(new.creation_date) - def create_project(self): - req_p = project_models.ProjectCreateRequest('functest', - 'vping-ssh test') - self.create_help('/api/v1/projects', req_p) - self.project = req_p.name - def create_d(self): return super(TestCaseBase, self).create_d(self.project) diff --git a/testapi/opnfv_testapi/ui/projects/projects.html b/testapi/opnfv_testapi/ui/projects/projects.html new file mode 100644 index 0000000..62a968b --- /dev/null +++ b/testapi/opnfv_testapi/ui/projects/projects.html @@ -0,0 +1,38 @@ +

Projects

+

+ +
+
+

Create

+
+
+
+

+ + + + + + + +

+
+
+ +
+ +
+
+
+ + + + \ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/projects/projectsController.js b/testapi/opnfv_testapi/ui/projects/projectsController.js new file mode 100644 index 0000000..d2640b6 --- /dev/null +++ b/testapi/opnfv_testapi/ui/projects/projectsController.js @@ -0,0 +1,74 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('ProjectsController', ProjectsController); + + ProjectsController.$inject = [ + '$scope', '$http', '$filter', '$state', 'testapiApiUrl','raiseAlert' + ]; + + /** + * TestAPI Project Controller + * This controller is for the '/projects' page where a user can browse + * through projects declared in TestAPI. + */ + function ProjectsController($scope, $http, $filter, $state, testapiApiUrl, + raiseAlert) { + var ctrl = this; + ctrl.url = testapiApiUrl + '/projects'; + ctrl.create = create; + + ctrl.createRequirements = [ + {label: 'name', type: 'text', required: true}, + {label: 'description', type: 'textarea', required: false} + ]; + + ctrl.name = ''; + ctrl.details = ''; + + /** + * This will contact the TestAPI to create a new project. + */ + function create() { + ctrl.showError = false; + ctrl.showSuccess = false; + if(ctrl.name != ""){ + var projects_url = ctrl.url; + var body = { + name: ctrl.name, + description: ctrl.description + }; + ctrl.projectsRequest = + $http.post(projects_url, body).success(function (data){ + ctrl.showSuccess = true ; + }) + .error(function (data) { + ctrl.showError = true; + ctrl.error = 'Error creating the new Project from server:' + angular.toJson(data); + }); + ctrl.name = ""; + ctrl.description=""; + } + else{ + ctrl.showError = true; + ctrl.error = 'Name is missing.' + } + } + } +})(); -- cgit 1.2.3-korg