diff --git a/refstack-ui/app/app.js b/refstack-ui/app/app.js
index eaa608c4..db76ea8c 100644
--- a/refstack-ui/app/app.js
+++ b/refstack-ui/app/app.js
@@ -26,8 +26,13 @@ refstackApp.config([
templateUrl: '/components/capabilities/capabilities.html',
controller: 'capabilitiesController'
}).
- state('results', {
- url: '/results',
+ state('community_results', {
+ url: '/community_results',
+ templateUrl: '/components/results/results.html',
+ controller: 'resultsController'
+ }).
+ state('user_results', {
+ url: '/user_results',
templateUrl: '/components/results/results.html',
controller: 'resultsController'
}).
@@ -45,19 +50,46 @@ refstackApp.config([
]);
/**
- * Try to authenticate user
+ * Injections in $rootscope
*/
-refstackApp.run(['$http', '$rootScope', 'refstackApiUrl',
- function($http, $rootScope, refstackApiUrl) {
+refstackApp.run(['$http', '$rootScope', '$window', 'refstackApiUrl',
+ function($http, $rootScope, $window, refstackApiUrl) {
'use strict';
+
+ /**
+ * This function injects sign in function in all scopes
+ */
+
+ $rootScope.auth = {};
+
+ var sign_in_url = refstackApiUrl + '/auth/signin';
+ $rootScope.auth.doSignIn = function () {
+ $window.location.href = sign_in_url;
+ };
+
+ /**
+ * This function injects sign out function in all scopes
+ */
+ var sign_out_url = refstackApiUrl + '/auth/signout';
+ $rootScope.auth.doSignOut = function () {
+ $rootScope.currentUser = null;
+ $rootScope.isAuthenticated = false;
+ $window.location.href = sign_out_url;
+ };
+
+ /**
+ * This block tries to authenticate user
+ */
var profile_url = refstackApiUrl + '/profile';
$http.get(profile_url, {withCredentials: true}).
success(function(data) {
- $rootScope.currentUser = data;
+ $rootScope.auth.currentUser = data;
+ $rootScope.auth.isAuthenticated = true;
}).
error(function() {
- $rootScope.currentUser = null;
+ $rootScope.auth.currentUser = null;
+ $rootScope.auth.isAuthenticated = false;
});
}
]);
diff --git a/refstack-ui/app/components/auth/authController.js b/refstack-ui/app/components/auth/authController.js
deleted file mode 100644
index 19ea009f..00000000
--- a/refstack-ui/app/components/auth/authController.js
+++ /dev/null
@@ -1,29 +0,0 @@
-var refstackApp = angular.module('refstackApp');
-
- /**
- * Refstack Auth Controller
- * This controller handles account authentication for users.
- */
-
-refstackApp.controller('authController',
- ['$scope', '$window', '$rootScope', 'refstackApiUrl',
- function($scope, $window, $rootScope, refstackApiUrl){
- 'use strict';
- var sign_in_url = refstackApiUrl + '/auth/signin';
- $scope.doSignIn = function () {
- $window.location.href = sign_in_url;
- };
-
- var sign_out_url = refstackApiUrl + '/auth/signout';
- $scope.doSignOut = function () {
- $rootScope.currentUser = null;
- $window.location.href = sign_out_url;
- };
-
- $scope.isAuthenticated = function () {
- if ($scope.currentUser) {
- return !!$scope.currentUser.openid;
- }
- return false;
- };
- }]);
diff --git a/refstack-ui/app/components/capabilities/capabilities.html b/refstack-ui/app/components/capabilities/capabilities.html
index 889fcc9f..26b42d6e 100644
--- a/refstack-ui/app/components/capabilities/capabilities.html
+++ b/refstack-ui/app/components/capabilities/capabilities.html
@@ -58,7 +58,7 @@
-
+
diff --git a/refstack-ui/app/components/profile/profile.html b/refstack-ui/app/components/profile/profile.html
index f7c46c21..e5223482 100644
--- a/refstack-ui/app/components/profile/profile.html
+++ b/refstack-ui/app/components/profile/profile.html
@@ -1,11 +1,11 @@
User profile
-
-
+
+
- User name | {{user.fullname}} |
- User OpenId | {{user.openid}} |
- Email | {{user.email}} |
+ User name | {{auth.currentUser.fullname}} |
+ User OpenId | {{auth.currentUser.openid}} |
+ Email | {{auth.currentUser.email}} |
@@ -22,8 +22,8 @@
-
-
+
+
{{pubKey.format}} |
@@ -33,4 +33,3 @@
-
diff --git a/refstack-ui/app/components/profile/profileController.js b/refstack-ui/app/components/profile/profileController.js
index 11e837cc..c139aee0 100644
--- a/refstack-ui/app/components/profile/profileController.js
+++ b/refstack-ui/app/components/profile/profileController.js
@@ -19,16 +19,6 @@ refstackApp.controller('profileController',
function($scope, $http, refstackApiUrl, $state,
PubKeys, $modal, raiseAlert) {
'use strict';
- $scope.updateProfile = function () {
- var profile_url = refstackApiUrl + '/profile';
- $http.get(profile_url, {withCredentials: true}).
- success(function(data) {
- $scope.user = data;
- }).
- error(function() {
- $state.go('home');
- });
- };
$scope.updatePubKeys = function (){
var keys = PubKeys.query(function(){
@@ -77,7 +67,6 @@ refstackApp.controller('profileController',
$scope.showRes = function(pubKey){
raiseAlert('success', '', pubKey.key);
};
- $scope.updateProfile();
$scope.updatePubKeys();
}
]);
diff --git a/refstack-ui/app/components/results/results.html b/refstack-ui/app/components/results/results.html
index 13935e91..487bbf3d 100644
--- a/refstack-ui/app/components/results/results.html
+++ b/refstack-ui/app/components/results/results.html
@@ -1,4 +1,4 @@
-Community Results
+{{pageHeader}}
The most recently uploaded community test results are listed here. Currently, these results are anonymous.
diff --git a/refstack-ui/app/components/results/resultsController.js b/refstack-ui/app/components/results/resultsController.js
index 126726e0..40a08207 100644
--- a/refstack-ui/app/components/results/resultsController.js
+++ b/refstack-ui/app/components/results/resultsController.js
@@ -6,8 +6,8 @@ var refstackApp = angular.module('refstackApp');
* a listing of community uploaded results.
*/
refstackApp.controller('resultsController',
- ['$scope', '$http', '$filter', 'refstackApiUrl',
- function ($scope, $http, $filter, refstackApiUrl) {
+ ['$scope', '$http', '$filter', '$state', 'refstackApiUrl',
+ function ($scope, $http, $filter, $state, refstackApiUrl) {
'use strict';
/** Initial page to be on. */
@@ -33,6 +33,9 @@ refstackApp.controller('resultsController',
/** The upload date upper limit to be used in filtering results. */
$scope.endDate = '';
+ $scope.isUserResults = $state.current.name === 'user_results';
+ $scope.pageHeader = $scope.isUserResults ?
+ 'Private test results' : 'Community test results';
/**
* This will contact the Refstack API to get a listing of test run
* results.
@@ -51,7 +54,9 @@ refstackApp.controller('resultsController',
if (end) {
content_url = content_url + '&end_date=' + end + ' 23:59:59';
}
-
+ if ($scope.isUserResults) {
+ content_url = content_url + '&signed';
+ }
$scope.resultsRequest =
$http.get(content_url).success(function (data) {
$scope.data = data;
diff --git a/refstack-ui/app/index.html b/refstack-ui/app/index.html
index f0ef0a9d..0a27a51e 100644
--- a/refstack-ui/app/index.html
+++ b/refstack-ui/app/index.html
@@ -38,12 +38,11 @@
+
-
-
diff --git a/refstack-ui/app/components/alerts/alertModal.html b/refstack-ui/app/shared/alerts/alertModal.html
similarity index 100%
rename from refstack-ui/app/components/alerts/alertModal.html
rename to refstack-ui/app/shared/alerts/alertModal.html
diff --git a/refstack-ui/app/components/alerts/alertModalFactory.js b/refstack-ui/app/shared/alerts/alertModalFactory.js
similarity index 94%
rename from refstack-ui/app/components/alerts/alertModalFactory.js
rename to refstack-ui/app/shared/alerts/alertModalFactory.js
index 8cb76e47..25f5e5e2 100644
--- a/refstack-ui/app/components/alerts/alertModalFactory.js
+++ b/refstack-ui/app/shared/alerts/alertModalFactory.js
@@ -5,7 +5,7 @@ refstackApp.factory('raiseAlert',
'use strict';
return function(mode, title, text) {
$modal.open({
- templateUrl: '/components/alerts/alertModal.html',
+ templateUrl: '/shared/alerts/alertModal.html',
controller: 'raiseAlertModalController',
backdrop: true,
keyboard: true,
diff --git a/refstack-ui/app/shared/header/header.html b/refstack-ui/app/shared/header/header.html
index 866aa7fc..94c822ba 100644
--- a/refstack-ui/app/shared/header/header.html
+++ b/refstack-ui/app/shared/header/header.html
@@ -18,14 +18,14 @@ Refstack
Home
About
DefCore Capabilities
-
Community Results
+
Community Results
-
-
diff --git a/refstack-ui/tests/unit/AuthSpec.js b/refstack-ui/tests/unit/AuthSpec.js
new file mode 100644
index 00000000..c02709be
--- /dev/null
+++ b/refstack-ui/tests/unit/AuthSpec.js
@@ -0,0 +1,40 @@
+describe('Auth', function () {
+ 'use strict';
+
+ var fakeApiUrl = 'http://foo.bar/v1';
+ var $window;
+ beforeEach(function () {
+ $window = {location: { href: jasmine.createSpy()} };
+ module(function ($provide) {
+ $provide.constant('refstackApiUrl', fakeApiUrl);
+ $provide.value('$window', $window);
+ });
+ module('refstackApp');
+ });
+
+ var $rootScope, $httpBackend;
+ beforeEach(inject(function (_$httpBackend_, _$rootScope_) {
+ $httpBackend = _$httpBackend_;
+ $rootScope = _$rootScope_;
+ }));
+
+ it('should show signin url for signed user', function () {
+ $httpBackend.expectGET(fakeApiUrl +
+ '/profile').respond({'openid': 'foo@bar.com',
+ 'email': 'foo@bar.com',
+ 'fullname': 'foo' });
+ $httpBackend.flush();
+ $rootScope.auth.doSignIn();
+ expect($window.location.href).toBe(fakeApiUrl + '/auth/signin');
+ expect($rootScope.auth.isAuthenticated).toBe(true);
+ });
+
+ it('should show signout url for not signed user', function () {
+ $httpBackend.expectGET(fakeApiUrl +
+ '/profile').respond(401);
+ $httpBackend.flush();
+ $rootScope.auth.doSignOut();
+ expect($window.location.href).toBe(fakeApiUrl + '/auth/signout');
+ expect($rootScope.auth.isAuthenticated).toBe(false);
+ });
+});
diff --git a/refstack-ui/tests/unit/ControllerSpec.js b/refstack-ui/tests/unit/ControllerSpec.js
index 37b929c1..3d0d9ede 100644
--- a/refstack-ui/tests/unit/ControllerSpec.js
+++ b/refstack-ui/tests/unit/ControllerSpec.js
@@ -3,11 +3,18 @@ describe('Refstack controllers', function () {
'use strict';
var fakeApiUrl = 'http://foo.bar/v1';
+ var $httpBackend;
beforeEach(function () {
module(function ($provide) {
$provide.constant('refstackApiUrl', fakeApiUrl);
});
module('refstackApp');
+ inject(function(_$httpBackend_){
+ $httpBackend = _$httpBackend_;
+ });
+ $httpBackend.whenGET(fakeApiUrl + '/profile').respond(401);
+ $httpBackend.whenGET('/components/home/home.html')
+ .respond('mock template
');
});
describe('headerController', function () {
@@ -36,42 +43,10 @@ describe('Refstack controllers', function () {
});
});
- describe('authController', function () {
- var scope, $httpBackend, $window;
-
- beforeEach(inject(function (_$httpBackend_, $rootScope, $controller) {
- $httpBackend = _$httpBackend_;
- scope = $rootScope.$new();
- $window = {location: { href: jasmine.createSpy()} };
- $controller('authController', {$scope: scope, $window: $window});
- }));
-
- it('should show signin url for signed user', function () {
- $httpBackend.expectGET(fakeApiUrl +
- '/profile').respond({'openid': 'foo@bar.com',
- 'email': 'foo@bar.com',
- 'fullname': 'foo' });
- $httpBackend.flush();
- scope.doSignIn();
- expect($window.location.href).toBe(fakeApiUrl + '/auth/signin');
- expect(scope.isAuthenticated()).toBe(true);
- });
-
- it('should show signout url for not signed user', function () {
- $httpBackend.expectGET(fakeApiUrl +
- '/profile').respond(401);
- $httpBackend.flush();
- scope.doSignOut();
- expect($window.location.href).toBe(fakeApiUrl + '/auth/signout');
- expect(scope.isAuthenticated()).toBe(false);
- });
- });
-
describe('capabilitiesController', function () {
- var scope, $httpBackend;
+ var scope;
- beforeEach(inject(function (_$httpBackend_, $rootScope, $controller) {
- $httpBackend = _$httpBackend_;
+ beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
$controller('capabilitiesController', {$scope: scope});
}));
@@ -102,8 +77,6 @@ describe('Refstack controllers', function () {
}
};
- $httpBackend.expectGET(fakeApiUrl +
- '/profile').respond(401);
$httpBackend.expectGET(fakeApiUrl +
'/capabilities').respond(['2015.03.json', '2015.04.json']);
// Should call request with latest version.
@@ -170,7 +143,7 @@ describe('Refstack controllers', function () {
});
describe('resultsController', function () {
- var scope, $httpBackend;
+ var scope;
var fakeResponse = {
'pagination': {'current_page': 1, 'total_pages': 2},
'results': [{
@@ -180,8 +153,7 @@ describe('Refstack controllers', function () {
}]
};
- beforeEach(inject(function (_$httpBackend_, $rootScope, $controller) {
- $httpBackend = _$httpBackend_;
+ beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
$controller('resultsController', {$scope: scope});
}));
@@ -189,9 +161,8 @@ describe('Refstack controllers', function () {
it('should fetch the first page of results with proper URL args',
function () {
// Initial results should be page 1 of all results.
- $httpBackend.expectGET(fakeApiUrl + '/profile').respond(401);
- $httpBackend.expectGET(fakeApiUrl +
- '/results?page=1').respond(fakeResponse);
+ $httpBackend.expectGET(fakeApiUrl + '/results?page=1')
+ .respond(fakeResponse);
$httpBackend.flush();
expect(scope.data).toEqual(fakeResponse);
expect(scope.currentPage).toBe(1);
@@ -211,7 +182,6 @@ describe('Refstack controllers', function () {
});
it('should set an error when results cannot be retrieved', function () {
- $httpBackend.expectGET(fakeApiUrl + '/profile').respond(401);
$httpBackend.expectGET(fakeApiUrl + '/results?page=1').respond(404,
{'detail': 'Not Found'});
$httpBackend.flush();
@@ -224,23 +194,16 @@ describe('Refstack controllers', function () {
it('should have an function to clear filters and update the view',
function () {
- $httpBackend.expectGET(fakeApiUrl + '/profile').respond(401);
- $httpBackend.expectGET(fakeApiUrl +
- '/results?page=1').respond(fakeResponse);
scope.startDate = 'some date';
scope.endDate = 'some other date';
scope.clearFilters();
expect(scope.startDate).toBe(null);
expect(scope.endDate).toBe(null);
- $httpBackend.expectGET(fakeApiUrl +
- '/results?page=1').respond(fakeResponse);
- $httpBackend.flush();
- expect(scope.data).toEqual(fakeResponse);
});
});
describe('resultsReportController', function () {
- var scope, $httpBackend, stateparams;
+ var scope, stateparams;
var fakeResultResponse = {'results': ['test_id_1']};
var fakeCapabilityResponse = {
'platform': {'required': ['compute']},
@@ -261,8 +224,7 @@ describe('Refstack controllers', function () {
}
};
- beforeEach(inject(function (_$httpBackend_, $rootScope, $controller) {
- $httpBackend = _$httpBackend_;
+ beforeEach(inject(function ($rootScope, $controller) {
stateparams = {testID: 1234};
scope = $rootScope.$new();
$controller('resultsReportController',
@@ -272,7 +234,6 @@ describe('Refstack controllers', function () {
it('should make all necessary API requests to get results ' +
'and capabilities',
function () {
- $httpBackend.expectGET(fakeApiUrl + '/profile').respond(401);
$httpBackend.expectGET(fakeApiUrl +
'/results/1234').respond(fakeResultResponse);
$httpBackend.expectGET(fakeApiUrl +
diff --git a/refstack/api/constants.py b/refstack/api/constants.py
index c4a22117..08522990 100644
--- a/refstack/api/constants.py
+++ b/refstack/api/constants.py
@@ -19,6 +19,7 @@ START_DATE = 'start_date'
END_DATE = 'end_date'
CPID = 'cpid'
PAGE = 'page'
+SIGNED = 'signed'
# OpenID parameters
OPENID_MODE = 'openid.mode'
@@ -36,3 +37,6 @@ OPENID_ERROR = 'openid.error'
# User session parameters
CSRF_TOKEN = 'csrf_token'
USER_OPENID = 'user_openid'
+
+# Test metadata fields
+PUBLIC_KEY = 'public_key'
diff --git a/refstack/api/controllers/results.py b/refstack/api/controllers/results.py
index e14ea55b..9ec41229 100644
--- a/refstack/api/controllers/results.py
+++ b/refstack/api/controllers/results.py
@@ -55,7 +55,7 @@ class ResultsController(validation.BaseRestControllerWithValidation):
if pecan.request.headers.get('X-Public-Key'):
if 'metadata' not in item:
item['metadata'] = {}
- item['metadata']['public_key'] = \
+ item['metadata'][const.PUBLIC_KEY] = \
pecan.request.headers.get('X-Public-Key')
test_id = db.store_results(item)
LOG.debug(item)
@@ -79,6 +79,7 @@ class ResultsController(validation.BaseRestControllerWithValidation):
const.START_DATE,
const.END_DATE,
const.CPID,
+ const.SIGNED
]
try:
@@ -88,9 +89,6 @@ class ResultsController(validation.BaseRestControllerWithValidation):
api_utils.get_page_number(records_count)
except api_utils.ParseInputsError as ex:
pecan.abort(400, 'Reason: %s' % ex)
- except Exception as ex:
- LOG.debug('An error occurred: %s' % ex)
- pecan.abort(500)
try:
per_page = CONF.api.results_per_page
@@ -102,7 +100,8 @@ class ResultsController(validation.BaseRestControllerWithValidation):
'test_id': r.id,
'created_at': r.created_at,
'cpid': r.cpid,
- 'url': CONF.api.test_results_url % r.id
+ 'url': parse.urljoin(CONF.ui_url,
+ CONF.api.test_results_url) % r.id
})
page = {'results': results,
diff --git a/refstack/api/utils.py b/refstack/api/utils.py
index 40fc3b68..72f297a0 100644
--- a/refstack/api/utils.py
+++ b/refstack/api/utils.py
@@ -86,6 +86,16 @@ def parse_input_params(expected_input_params):
'start': const.START_DATE,
'end': const.END_DATE
})
+ if const.SIGNED in filters:
+ if is_authenticated():
+ filters['openid'] = get_user_id()
+ filters['pubkeys'] = [
+ ' '.join((pk['format'], pk['key']))
+ for pk in db.get_user_pubkeys(filters['openid'])
+ ]
+ else:
+ raise ParseInputsError('To see signed test '
+ 'results you need to authenticate')
return filters
@@ -176,6 +186,11 @@ def get_user():
return db.user_get(get_user_id())
+def get_user_public_keys():
+ """Return db record for authenticated user."""
+ return db.get_user_pubkeys(get_user_id())
+
+
def is_authenticated():
"""Return True if user is authenticated."""
if get_user_id():
diff --git a/refstack/db/migrations/alembic/versions/534e20be9964_create_pubkey_table.py b/refstack/db/migrations/alembic/versions/534e20be9964_create_pubkey_table.py
index 5f8185e9..a6e5a12e 100644
--- a/refstack/db/migrations/alembic/versions/534e20be9964_create_pubkey_table.py
+++ b/refstack/db/migrations/alembic/versions/534e20be9964_create_pubkey_table.py
@@ -34,6 +34,7 @@ def upgrade():
sa.ForeignKeyConstraint(['openid'], ['user.openid'], ),
mysql_charset=MYSQL_CHARSET
)
+ op.create_index('indx_meta_value', 'meta', ['value'], mysql_length=32)
def downgrade():
diff --git a/refstack/db/sqlalchemy/api.py b/refstack/db/sqlalchemy/api.py
index ad57842c..fea0b30a 100644
--- a/refstack/db/sqlalchemy/api.py
+++ b/refstack/db/sqlalchemy/api.py
@@ -117,6 +117,19 @@ def _apply_filters_for_query(query, filters):
if cpid:
query = query.filter(models.Test.cpid == cpid)
+ signed = api_const.SIGNED in filters
+ if signed:
+ query = (query
+ .join(models.Test.meta)
+ .filter(models.TestMeta.meta_key == api_const.PUBLIC_KEY)
+ .filter(models.TestMeta.value.in_(filters['pubkeys']))
+ )
+ else:
+ signed_results = (query.session
+ .query(models.TestMeta.test_id)
+ .filter_by(meta_key=api_const.PUBLIC_KEY))
+ query = query.filter(models.Test.id.notin_(signed_results))
+
return query
diff --git a/refstack/tests/unit/test_api.py b/refstack/tests/unit/test_api.py
index faa10304..44bde7e8 100644
--- a/refstack/tests/unit/test_api.py
+++ b/refstack/tests/unit/test_api.py
@@ -134,7 +134,7 @@ class ResultsControllerTestCase(base.BaseTestCase):
'url': self.test_results_url % 'fake_test_id'})
self.assertEqual(mock_response.status, 201)
mock_store_results.assert_called_once_with(
- {'answer': 42, 'metadata': {'public_key': 'fake-key'}}
+ {'answer': 42, 'metadata': {const.PUBLIC_KEY: 'fake-key'}}
)
@mock.patch('pecan.abort')
@@ -216,6 +216,7 @@ class ResultsControllerTestCase(base.BaseTestCase):
const.START_DATE,
const.END_DATE,
const.CPID,
+ const.SIGNED
]
page_number = 1
total_pages_number = 10
diff --git a/refstack/tests/unit/test_db.py b/refstack/tests/unit/test_db.py
index 71975887..a629412c 100644
--- a/refstack/tests/unit/test_db.py
+++ b/refstack/tests/unit/test_db.py
@@ -193,9 +193,12 @@ class DBBackendTestCase(base.BaseTestCase):
self.assertEqual(expected_result, actual_result)
@mock.patch('refstack.db.sqlalchemy.models.Test')
- def test_apply_filters_for_query(self, mock_model):
+ @mock.patch('refstack.db.sqlalchemy.models.TestMeta')
+ def test_apply_filters_for_query_unsigned(self, mock_meta,
+ mock_test):
query = mock.Mock()
- mock_model.created_at = six.text_type()
+ mock_test.created_at = six.text_type()
+ mock_meta.test_id = six.text_type()
filters = {
api_const.START_DATE: 'fake1',
@@ -205,19 +208,30 @@ class DBBackendTestCase(base.BaseTestCase):
result = api._apply_filters_for_query(query, filters)
- query.filter.assert_called_once_with(mock_model.created_at >=
+ query.filter.assert_called_once_with(mock_test.created_at >=
filters[api_const.START_DATE])
query = query.filter.return_value
- query.filter.assert_called_once_with(mock_model.created_at <=
+ query.filter.assert_called_once_with(mock_test.created_at <=
filters[api_const.END_DATE])
query = query.filter.return_value
- query.filter.assert_called_once_with(mock_model.cpid ==
+ query.filter.assert_called_once_with(mock_test.cpid ==
filters[api_const.CPID])
query = query.filter.return_value
- self.assertEqual(result, query)
+
+ query.session.query.assert_called_once_with(mock_meta.test_id)
+ meta_query = query.session.query.return_value
+
+ meta_query.filter_by.\
+ assert_called_once_with(meta_key=api_const.PUBLIC_KEY)
+ unsigned_test_id_query = meta_query.filter_by.return_value
+ mock_test.id.notin_.assert_called_once_with(unsigned_test_id_query)
+ query.filter.assert_called_once_with(mock_test.id.notin_.return_value)
+
+ filtered_query = query.filter.return_value
+ self.assertEqual(result, filtered_query)
@mock.patch.object(api, '_apply_filters_for_query')
@mock.patch.object(api, 'get_session')
diff --git a/test-requirements.txt b/test-requirements.txt
index 40387cb2..3b4cf5d3 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -8,6 +8,5 @@ oslotest>=1.2.0 # Apache-2.0
python-subunit>=0.0.18
testrepository>=0.0.18
testtools>=0.9.34
-mysqlclient
six>=1.7.0
pep257>=0.5.0
\ No newline at end of file