Skip to content

Commit

Permalink
Remaining endpoint tests (#138)
Browse files Browse the repository at this point in the history
* added slack endpoint tests

* added more tests

* added auth tests
  • Loading branch information
AndreasJJ authored Apr 1, 2023
1 parent fa59f26 commit f0530c4
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 18 deletions.
20 changes: 4 additions & 16 deletions application/backend/app/api/auth.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import requests
import json
import os
from flask import views, request, redirect, jsonify, current_app
from flask_smorest import Blueprint, abort
from app.repositories.user_repository import UserRepository
Expand All @@ -13,21 +12,9 @@
bp = Blueprint("auth", "auth", url_prefix="/auth", description="Authentication")


def get_slack_user_info(token):
response = requests.post("https://slack.com/api/users.identity", headers={"Authorization": f"Bearer {token}"})

if response.status_code == 200:
return response.json()
else:
abort(400, message=f"Failed to retrieve Slack user info: {response.json()['error']}")

def get_slack_provider_cfg():
return requests.get(current_app.config["SLACK_DISCOVERY_URL"]).json()

@bp.route("/logout")
class Auth(views.MethodView):
def get(self):
pass

@bp.route("/refresh")
class Auth(views.MethodView):
Expand All @@ -45,6 +32,7 @@ def post(self):
access_token = create_access_token(identity=user, additional_claims=additional_claims)
return jsonify(access_token=access_token)


@bp.route("/login")
class Auth(views.MethodView):
def get(self):
Expand All @@ -53,8 +41,7 @@ def get(self):

# Use library to construct the request for Google login and provide
# scopes that let you retrieve user's profile from Google
base_url = os.environ.get("FRONTEND_URI").rstrip('/')
client_id = current_app.config["SLACK_CLIENT_ID"]
base_url = current_app.config["FRONTEND_URI"].rstrip('/')
request_uri = auth.client.prepare_request_uri(
authorization_endpoint,
redirect_uri = f'{base_url}/login/callback',
Expand All @@ -64,10 +51,11 @@ def get(self):
'auth_url': request_uri
})


@bp.route("/login/callback")
class Auth(views.MethodView):
def get(self):
base_url = os.environ.get("FRONTEND_URI").rstrip('/')
base_url = current_app.config["FRONTEND_URI"].rstrip('/')
code = request.args.get("code")
authorization_response = f'{base_url}/login/callback?'
for key in request.args.keys():
Expand Down
4 changes: 2 additions & 2 deletions application/backend/app/api/slack.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def get(self):
'users:read.email',
'commands'
]
frontend_base_url = os.environ.get("FRONTEND_URI").rstrip('/')
frontend_base_url = current_app.config["FRONTEND_URI"].rstrip('/')
callback_redirect_uri = f'{frontend_base_url}/slack/callback'
client_id = current_app.config["SLACK_CLIENT_ID"]
if client_id == None:
Expand All @@ -55,7 +55,7 @@ def post(self):
logger.warn("client_id or client_secret is None")
return abort(500)

frontend_base_url = os.environ.get("FRONTEND_URI").rstrip('/')
frontend_base_url = current_app.config["FRONTEND_URI"].rstrip('/')
callback_redirect_uri = f'{frontend_base_url}/slack/callback'

data = {
Expand Down
1 change: 1 addition & 0 deletions application/backend/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class Test(Base):
DEBUG = True
FRONTEND_URI = 'localhost'
SLACK_CLIENT_ID = 'dontCareSlackClientId'
SLACK_CLIENT_SECRET = 'dontCareSlackClientSecret'
MQ_EVENT_QUEUE = 'dontCareMqEventQueue'
MQ_EVENT_KEY = 'dontCareMqEventKey'
CLOUDINARY_CLOUD_NAME = 'dontCareCloudinaryCloudName'
Expand Down
123 changes: 123 additions & 0 deletions application/backend/tests/api/test_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import pytest
from app.models.user import User
from flask_jwt_extended import create_refresh_token, decode_token


@pytest.fixture
def mocked_requests(mocker):
post_mock = mocker.MagicMock()
mocker.patch("requests.post", post_mock)
get_mock = mocker.MagicMock()
mocker.patch("requests.get", get_mock)
yield post_mock, get_mock


@pytest.mark.usefixtures('client_class')
class TestAuthSuit:
def test_refresh(self, slack_organizations, users, ):
user = users.get(slack_organizations[0].team_id)

token = create_refresh_token(identity=user)
headers = {"Authorization": f"Bearer {token}"}
response = self.client.post('/api/auth/refresh', method='post', headers=headers)
decoded_token = decode_token(response.get_json()['access_token'])
assert response.status_code == 200
assert decoded_token['user']['id'] == user.id

def test_login(self):
response = self.client.get('/api/auth/login', method='get')
response_data = response.get_json()
auth_url = response_data['auth_url']
assert response.status_code == 200
assert isinstance(auth_url, str)

def test_login_callback_successful(self, db, mocker, mocked_requests, slack_organizations):
slack_organization = slack_organizations[0]
slack_provider_cfg_mock = mocker.MagicMock()
mocker.patch("app.api.auth.get_slack_provider_cfg", slack_provider_cfg_mock)

auth_client_mock = mocker.MagicMock()
mocker.patch("app.api.auth.auth.client", auth_client_mock)
auth_client_mock.prepare_token_request.return_value = mocker.MagicMock(), mocker.MagicMock(), mocker.MagicMock()
auth_client_mock.add_token.return_value = mocker.MagicMock(), mocker.MagicMock(), mocker.MagicMock()

post_mock, get_mock = mocked_requests
token_response = mocker.Mock()
token_response.json.return_value = {"access_token": "fake-token"}
post_mock.return_value = token_response
userinfo_response = mocker.Mock()
userinfo_response.json.return_value = {
"sub": "userId",
"email": "some@email.invalid",
"email_verified": True,
"picture": "https://example.com/picture.jpg",
"given_name": "someName",
"https://slack.com/team_id": slack_organization.team_id,
}
get_mock.return_value = userinfo_response

response = self.client.get("/api/auth/login/callback?code=abc123")

assert response.status_code == 200
assert decode_token(response.json['access_token'])['user']['id'] == "userId"
user = db.session.get(User, "userId")
assert user.email == "some@email.invalid"
assert user.name == "someName"
assert user.slack_organization_id == slack_organization.team_id

def test_login_callback_email_not_verified(self, mocker, mocked_requests, slack_organizations):
slack_organization = slack_organizations[0]
slack_provider_cfg_mock = mocker.MagicMock()
mocker.patch("app.api.auth.get_slack_provider_cfg", slack_provider_cfg_mock)

auth_client_mock = mocker.MagicMock()
mocker.patch("app.api.auth.auth.client", auth_client_mock)
auth_client_mock.prepare_token_request.return_value = mocker.MagicMock(), mocker.MagicMock(), mocker.MagicMock()
auth_client_mock.add_token.return_value = mocker.MagicMock(), mocker.MagicMock(), mocker.MagicMock()

post_mock, get_mock = mocked_requests
token_response = mocker.Mock()
token_response.json.return_value = {"access_token": "fake-token"}
post_mock.return_value = token_response
userinfo_response = mocker.Mock()
userinfo_response.json.return_value = {
"sub": "userId",
"email": "some@email.invalid",
"email_verified": False,
"picture": "https://example.com/picture.jpg",
"given_name": "someName",
"https://slack.com/team_id": slack_organization.team_id,
}
get_mock.return_value = userinfo_response

response = self.client.get("/api/auth/login/callback?code=abc123")

assert response.status_code == 401

def test_login_callback_slack_organization_not_installed(self, db, mocker, mocked_requests):
slack_provider_cfg_mock = mocker.MagicMock()
mocker.patch("app.api.auth.get_slack_provider_cfg", slack_provider_cfg_mock)

auth_client_mock = mocker.MagicMock()
mocker.patch("app.api.auth.auth.client", auth_client_mock)
auth_client_mock.prepare_token_request.return_value = mocker.MagicMock(), mocker.MagicMock(), mocker.MagicMock()
auth_client_mock.add_token.return_value = mocker.MagicMock(), mocker.MagicMock(), mocker.MagicMock()

post_mock, get_mock = mocked_requests
token_response = mocker.Mock()
token_response.json.return_value = {"access_token": "fake-token"}
post_mock.return_value = token_response
userinfo_response = mocker.Mock()
userinfo_response.json.return_value = {
"sub": "userId",
"email": "some@email.invalid",
"email_verified": True,
"picture": "https://example.com/picture.jpg",
"given_name": "someName",
"https://slack.com/team_id": "doesntExistTeamId",
}
get_mock.return_value = userinfo_response

response = self.client.get("/api/auth/login/callback?code=abc123")

assert response.status_code == 403
46 changes: 46 additions & 0 deletions application/backend/tests/api/test_slack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import pytest
from app.models.slack_organization import SlackOrganization


@pytest.mark.usefixtures('client_class')
class TestSlackSuit:
def test_install(self, app):
response = self.client.get('/api/slack/install', method='get')
response_data = response.get_json()
redirect_url = response_data['redirect_url']
assert response.status_code == 200
assert redirect_url == f'https://slack.com/oauth/v2/authorize?scope=channels:read,channels:history,' \
f'channels:join,channels:manage,groups:read,chat:write,files:read,im:history,im:write,' \
f'users:read,users:read.email,commands&client_id=' \
f'{app.config["SLACK_CLIENT_ID"]}&redirect_uri=' \
f'{app.config["FRONTEND_URI"]}/slack/callback'

def test_callback(self, db, mock_broker, mocker):
# Mock requests post
requests_mocker = mocker.MagicMock()
mocker.patch('app.api.slack.requests.post', requests_mocker)
# Mock requests post function call return value
requests_post_mock = mocker.MagicMock()
requests_mocker.return_value = requests_post_mock
# Mock requests posts function call json call return value
requests_post_mock.json.return_value = {
'ok': True,
'is_enterprise_install': False,
'team': {
'id': 'dontCareNewTeamId',
'name': 'dontCareNewTeamName'
},
'app_id': 'dontCareAppId',
'bot_user_id': 'dontCareUserId',
'access_token': 'dontCareAccessToken'
}

response = self.client.post('/api/slack/callback', method='post', json={'code': 'dontCareCode'})

test_slack_organization = db.session.query(SlackOrganization).get('dontCareNewTeamId')

assert test_slack_organization is not None
mock_broker.send.assert_called()
assert len(mock_broker.send.call_args_list) == 1
assert mock_broker.send.call_args_list[0].kwargs['body']['type'] == 'new_slack_organization_event'
assert response.status_code == 200

0 comments on commit f0530c4

Please sign in to comment.