Merged in issue-30-add-api-support-for-site-manipulation (pull request #15)
Issue 30 add api support for site manipulation
This commit is contained in:
commit
79c2a3a484
|
@ -2,6 +2,8 @@
|
||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
* :feature:`30` Add support to create, modify and delete sites, menus and pages via API.
|
||||||
|
* :feature:`30` Link pages to individual sites.
|
||||||
* :support:`0` Convert rote build infrastructure to use simpler Makefile and scripts setup.
|
* :support:`0` Convert rote build infrastructure to use simpler Makefile and scripts setup.
|
||||||
* :support:`28` Make invoke the main entry point for everything in the API server.
|
* :support:`28` Make invoke the main entry point for everything in the API server.
|
||||||
* :bug:`24` Enable upgrade to the latest version of Flask, by fixing the way exception handlers are registered.
|
* :bug:`24` Enable upgrade to the latest version of Flask, by fixing the way exception handlers are registered.
|
||||||
|
|
|
@ -15,10 +15,14 @@ def pytest_addoption(parser):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def server_hostname(request):
|
def api_base_uri(request):
|
||||||
return request.config.getoption("--server-hostname")
|
"""
|
||||||
|
The base URI of the API server to build request to API. The base URI is the format http://hostname:port, provided
|
||||||
|
by the server hostname and server port pytest parameters.
|
||||||
|
|
||||||
|
:param request: The request for the test.
|
||||||
@pytest.fixture
|
:return: The base URI of the API server.
|
||||||
def server_port(request):
|
"""
|
||||||
return request.config.getoption("--server-port")
|
server_hostname = request.config.getoption("--server-hostname")
|
||||||
|
server_port = request.config.getoption("--server-port")
|
||||||
|
return "http://{hostname}:{port}".format(hostname=server_hostname, port=server_port)
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
Feature: Site Management
|
||||||
|
The site endpoints allows an app admin user to create, modify and delete sites.
|
||||||
|
|
||||||
|
|
||||||
|
Scenario: Admin user can create a new site
|
||||||
|
Given I am an admin user
|
||||||
|
When I create a site
|
||||||
|
Then I get a valid site
|
||||||
|
And I get a valid site menu
|
||||||
|
|
||||||
|
Scenario: Editor user can not create a new site
|
||||||
|
Given I am an editor user
|
||||||
|
When I create a site
|
||||||
|
Then I get an unauthorized response
|
||||||
|
|
||||||
|
Scenario: Any user can get an existing site
|
||||||
|
Given I am an unauthenticated user
|
||||||
|
When I get an existing site
|
||||||
|
Then I get a valid site
|
||||||
|
And I get a valid site menu
|
||||||
|
|
||||||
|
Scenario: Admin user can modify a site
|
||||||
|
Given I am an admin user
|
||||||
|
When I get an existing site
|
||||||
|
And I modify the site
|
||||||
|
Then my updates are reflected in the site
|
||||||
|
And I get a valid site menu
|
||||||
|
|
||||||
|
Scenario: Admin user can modify a site's menu
|
||||||
|
Given I am an admin user
|
||||||
|
When I get an existing site menu
|
||||||
|
And I modify the site menu
|
||||||
|
Then my updates are reflected in the site menu
|
||||||
|
|
||||||
|
Scenario: Editor user can not modify a site
|
||||||
|
Given I am an editor user
|
||||||
|
When I get an existing site
|
||||||
|
And I modify the site
|
||||||
|
Then I get an unauthorized response
|
||||||
|
|
||||||
|
Scenario: Editor user can not modify a site's menu
|
||||||
|
Given I am an editor user
|
||||||
|
When I get an existing site menu
|
||||||
|
And I modify the site menu
|
||||||
|
Then I get an unauthorized response
|
||||||
|
|
||||||
|
Scenario: Admin user can delete a site
|
||||||
|
Given I am an admin user
|
||||||
|
When I get an existing site
|
||||||
|
And I delete the site
|
||||||
|
Then I get an error that the site is missing
|
||||||
|
And I get an error that the site menu is missing
|
||||||
|
|
||||||
|
Scenario: Editor user can not delete a site
|
||||||
|
Given I am an editor user
|
||||||
|
When I get an existing site
|
||||||
|
And I delete the site
|
||||||
|
Then I get an unauthorized response
|
|
@ -0,0 +1,99 @@
|
||||||
|
"""
|
||||||
|
Functional tests for the managing sites.
|
||||||
|
|
||||||
|
:copyright: Copyright 2013-2016, Dorian Pula <dorian.pula@amber-penguin-software.ca>
|
||||||
|
:license: AGPL v3+
|
||||||
|
"""
|
||||||
|
|
||||||
|
import http
|
||||||
|
|
||||||
|
from pytest import mark
|
||||||
|
import pytest_bdd as bdd
|
||||||
|
from pytest_bdd import parsers
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Figure out how to generate a JWT using the correct hash.
|
||||||
|
USER_ROLE_JWT_MAPPING = {
|
||||||
|
'admin': 'password',
|
||||||
|
'editor': 'some_other_login',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@mark.skip(reason="Test scenarios need work")
|
||||||
|
@bdd.scenario('site_management.feature', 'Admin user can create a new site')
|
||||||
|
def test_site_creation_by_admin():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@mark.skip(reason="Test scenarios need work")
|
||||||
|
@bdd.scenario('site_management.feature', 'Editor user can not create a new site')
|
||||||
|
def test_site_creation_permissions_for_editor():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@mark.skip(reason="Test scenarios need work")
|
||||||
|
@bdd.scenario('site_management.feature', 'Any user can get an existing site')
|
||||||
|
def test_site_fetch():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@mark.skip(reason="Test scenarios need work")
|
||||||
|
@bdd.scenario('site_management.feature', 'Admin user can modify a site')
|
||||||
|
def test_site_modification_by_admin():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@mark.skip(reason="Test scenarios need work")
|
||||||
|
@bdd.scenario('site_management.feature', 'Editor user can not modify a site')
|
||||||
|
def test_site_modifications_permissions_for_editor():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@mark.skip(reason="Test scenarios need work")
|
||||||
|
@bdd.scenario('site_management.feature', 'Admin user can modify a site\'s menu')
|
||||||
|
def test_site_menu_modification_by_admin():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@mark.skip(reason="Test scenarios need work")
|
||||||
|
@bdd.scenario('site_management.feature', 'Editor user can not modify a site\'s menu')
|
||||||
|
def test_site_menu_modifications_permissions_for_editor():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@mark.skip(reason="Test scenarios need work")
|
||||||
|
@bdd.scenario('site_management.feature', 'Admin user can delete a site')
|
||||||
|
def test_site_deletion_by_admin():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@mark.skip(reason="Test scenarios need work")
|
||||||
|
@bdd.scenario('site_management.feature', 'Editor user can not delete a site')
|
||||||
|
def test_site_deletion_permissions_for_editor():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@bdd.given(parsers.parse('I am an {user_role} user'))
|
||||||
|
def user_credentials(user_role):
|
||||||
|
return USER_ROLE_JWT_MAPPING[user_role]
|
||||||
|
|
||||||
|
|
||||||
|
@bdd.when('I create a site')
|
||||||
|
def posted_response_from_auth_endpoint(user_credentials, api_base_uri):
|
||||||
|
# TODO: Needs work
|
||||||
|
response = requests.post("{}/auth".format(api_base_uri), json={
|
||||||
|
'username': user_credentials.username,
|
||||||
|
'password': user_credentials.password,
|
||||||
|
})
|
||||||
|
user_credentials.response['status'] = response.status_code
|
||||||
|
user_credentials.response['json'] = response.json()
|
||||||
|
|
||||||
|
|
||||||
|
@bdd.then('I get an unauthorized response')
|
||||||
|
def assert_unauthorized_response(user_credentials):
|
||||||
|
# TODO: Needs work
|
||||||
|
assert user_credentials.response['status'] == http.HTTPStatus.UNAUTHORIZED
|
||||||
|
assert user_credentials.response['json']['status_code'] == http.HTTPStatus.UNAUTHORIZED
|
||||||
|
assert user_credentials.response['json']['error'] == 'Bad Request'
|
||||||
|
assert user_credentials.response['json']['description'] == 'Invalid credentials'
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
Feature tests for the user authentication feature.
|
Functional tests for the user authentication feature.
|
||||||
|
|
||||||
:copyright: Copyright 2013-2016, Dorian Pula <dorian.pula@amber-penguin-software.ca>
|
:copyright: Copyright 2013-2016, Dorian Pula <dorian.pula@amber-penguin-software.ca>
|
||||||
:license: AGPL v3+
|
:license: AGPL v3+
|
||||||
|
@ -8,48 +8,51 @@ Feature tests for the user authentication feature.
|
||||||
import collections
|
import collections
|
||||||
import http
|
import http
|
||||||
|
|
||||||
|
import jwt
|
||||||
import pytest_bdd as bdd
|
import pytest_bdd as bdd
|
||||||
from pytest_bdd import parsers
|
from pytest_bdd import parsers
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
||||||
TEST_USERS = {
|
TEST_USERS = {
|
||||||
'admin': {
|
'valid': {
|
||||||
'correct': 'password',
|
'valid': 'password',
|
||||||
'incorrect': '123',
|
'invalid': '123',
|
||||||
},
|
},
|
||||||
'non-registered': {'correct': 'loging'},
|
'invalid': {'some': 'login'},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Credentials = collections.namedtuple('Credentials', ['username', 'password', 'response'])
|
Credentials = collections.namedtuple('Credentials', ['username', 'password', 'response'])
|
||||||
|
|
||||||
|
|
||||||
@bdd.scenario('user_authentication.feature', 'Allowed admin user can authenticate against site')
|
@bdd.scenario('user_authentication.feature', 'Valid user can authenticate against site with valid credentials')
|
||||||
def test_authentication_with_valid_credentials():
|
def test_authentication_with_valid_credentials():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@bdd.scenario('user_authentication.feature', 'Allowed admin user cannot authenticate against site with bad credentials')
|
@bdd.scenario('user_authentication.feature', 'Valid user cannot authenticate against site with invalid credentials')
|
||||||
def test_authentication_with_invalid_credentials():
|
def test_authentication_with_invalid_credentials():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@bdd.scenario('user_authentication.feature', 'Non-register user cannot authenticate against site with credentials')
|
@bdd.scenario('user_authentication.feature', 'Invalid user cannot authenticate against site with credentials')
|
||||||
def test_authentication_with_invalid_user():
|
def test_authentication_with_invalid_user():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@bdd.given(parsers.parse('I am the {user_name} user with the {login_type} credentials'))
|
@bdd.given(parsers.parse('I am a {user_type} user with {login_type} credentials'))
|
||||||
def user_credentials(user_name, login_type):
|
def user_credentials(user_type, login_type):
|
||||||
password = TEST_USERS.get(user_name, {}).get(login_type, '')
|
user_name = 'random_user'
|
||||||
|
if user_type == 'valid':
|
||||||
|
user_name = 'admin'
|
||||||
|
password = TEST_USERS.get(user_type, {}).get(login_type, '')
|
||||||
return Credentials(username=user_name, password=password, response={})
|
return Credentials(username=user_name, password=password, response={})
|
||||||
|
|
||||||
|
|
||||||
@bdd.when('I post my credentials against the auth endpoint')
|
@bdd.when('I post my credentials against the auth endpoint')
|
||||||
def posted_response_from_auth_endpoint(user_credentials, server_hostname, server_port):
|
def posted_response_from_auth_endpoint(user_credentials, api_base_uri):
|
||||||
uri_base = "http://{}:{}".format(server_hostname, server_port)
|
response = requests.post("{}/auth".format(api_base_uri), json={
|
||||||
response = requests.post("{}/auth".format(uri_base), json={
|
|
||||||
'username': user_credentials.username,
|
'username': user_credentials.username,
|
||||||
'password': user_credentials.password,
|
'password': user_credentials.password,
|
||||||
})
|
})
|
||||||
|
@ -61,15 +64,18 @@ def posted_response_from_auth_endpoint(user_credentials, server_hostname, server
|
||||||
def assert_valid_user_json_web_token(user_credentials):
|
def assert_valid_user_json_web_token(user_credentials):
|
||||||
assert user_credentials.response['status'] == http.HTTPStatus.OK
|
assert user_credentials.response['status'] == http.HTTPStatus.OK
|
||||||
|
|
||||||
assert user_credentials.response['json']['token'] is not None
|
jwt_token = user_credentials.response['json']['token']
|
||||||
|
assert jwt_token is not None
|
||||||
|
|
||||||
validated_user_credentials = user_credentials.response['json']['user']
|
validated_user_credentials = user_credentials.response['json']['user']
|
||||||
assert user_credentials.username == validated_user_credentials.get('username')
|
assert user_credentials.username == validated_user_credentials.get('username')
|
||||||
|
decoded_jwt = jwt.decode(jwt_token, verify=False)
|
||||||
|
assert 'identity' in decoded_jwt
|
||||||
|
|
||||||
|
|
||||||
@bdd.then('I get an unauthorized response')
|
@bdd.then('I get an unauthorized response')
|
||||||
def assert_unauthorized_response(user_credentials):
|
def assert_unauthorized_response(user_credentials):
|
||||||
assert user_credentials.response['status'] == http.HTTPStatus.UNAUTHORIZED
|
assert user_credentials.response['status'] == http.HTTPStatus.UNAUTHORIZED
|
||||||
|
|
||||||
assert user_credentials.response['json']['status_code'] == http.HTTPStatus.UNAUTHORIZED
|
assert user_credentials.response['json']['status_code'] == http.HTTPStatus.UNAUTHORIZED
|
||||||
assert user_credentials.response['json']['error'] == 'Bad Request'
|
assert user_credentials.response['json']['error'] == 'Bad Request'
|
||||||
assert user_credentials.response['json']['description'] == 'Invalid credentials'
|
assert user_credentials.response['json']['description'] == 'Invalid credentials'
|
||||||
|
|
|
@ -2,19 +2,19 @@ Feature: User Authentication
|
||||||
The user endpoint allows user to authenticate against the system
|
The user endpoint allows user to authenticate against the system
|
||||||
|
|
||||||
|
|
||||||
Scenario: Allowed admin user can authenticate against site
|
Scenario: Valid user can authenticate against site with valid credentials
|
||||||
Given I am the admin user with the correct credentials
|
Given I am a valid user with valid credentials
|
||||||
When I post my credentials against the auth endpoint
|
When I post my credentials against the auth endpoint
|
||||||
Then I get a valid JWT
|
Then I get a valid JWT
|
||||||
|
|
||||||
|
|
||||||
Scenario: Allowed admin user cannot authenticate against site with bad credentials
|
Scenario: Valid user cannot authenticate against site with invalid credentials
|
||||||
Given I am the admin user with the wrong credentials
|
Given I am a valid user with invalid credentials
|
||||||
When I post my credentials against the auth endpoint
|
When I post my credentials against the auth endpoint
|
||||||
Then I get an unauthorized response
|
Then I get an unauthorized response
|
||||||
|
|
||||||
|
|
||||||
Scenario: Non-register user cannot authenticate against site with credentials
|
Scenario: Invalid user cannot authenticate against site with credentials
|
||||||
Given I am the non-registered user with the correct credentials
|
Given I am a invalid user with some credentials
|
||||||
When I post my credentials against the auth endpoint
|
When I post my credentials against the auth endpoint
|
||||||
Then I get an unauthorized response
|
Then I get an unauthorized response
|
||||||
|
|
Loading…
Reference in New Issue