forked from data-platform-hq/mlflow-oidc-auth
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
build(tests): lint, unit tests, and ci for that (data-platform-hq#32)
Co-authored-by: Aleksandr Chikovani <[email protected]> Co-authored-by: Alexander Kharkevich <[email protected]>
- Loading branch information
1 parent
2144744
commit e706e86
Showing
25 changed files
with
912 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
[run] | ||
source = ./ | ||
omit = mlflow_oidc_auth/tests/*,mlflow_oidc_auth/db/migrations/versions/* | ||
omit = mlflow_oidc_auth/tests/*,mlflow_oidc_auth/db/migrations/versions/*,mlflow_oidc_auth/views/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
name: Run pre-commit | ||
on: | ||
pull_request: | ||
types: | ||
- opened | ||
- edited | ||
- reopened | ||
- synchronize | ||
jobs: | ||
pre-commit: | ||
name: Run pre-commit | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 | ||
with: | ||
fetch-depth: 0 | ||
- name: Set up Python | ||
uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 | ||
with: | ||
python-version: 3.11 | ||
- name: Run pre-commit | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install pre-commit | ||
pre-commit install | ||
pre-commit run --all-files --show-diff-on-failure |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
name: Unit tests | ||
on: | ||
pull_request: | ||
types: | ||
- opened | ||
- edited | ||
- reopened | ||
- synchronize | ||
push: | ||
branches: | ||
- main | ||
jobs: | ||
python-tests: | ||
name: Run python tests | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 | ||
with: | ||
fetch-depth: 0 | ||
- name: Set up Python | ||
uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 | ||
with: | ||
python-version: 3.11 | ||
- name: Run tests | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install tox | ||
tox -e py | ||
- name: Override Coverage Source Path for Sonar | ||
run: sed -i "s@<source>/home/runner/work/mlflow-oidc-auth/mlflow-oidc-auth</source>@<source>/github/workspace</source>@g" /home/runner/work/mlflow-oidc-auth/mlflow-oidc-auth/coverage.xml | ||
- name: SonarCloud Scan | ||
uses: SonarSource/sonarcloud-github-action@e44258b109568baa0df60ed515909fc6c72cba92 # v2.3.0 | ||
env: | ||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -176,3 +176,4 @@ flask_session/ | |
node_modules/ | ||
.angular/ | ||
mlflow_oidc_auth/ui | ||
pytest-coverage.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,4 +4,3 @@ | |
|
||
|
||
## Permissions hierarchy | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
from unittest.mock import patch, MagicMock | ||
from mlflow_oidc_auth.auth import ( | ||
get_oauth_instance, | ||
_get_oidc_jwks, | ||
validate_token, | ||
authenticate_request_basic_auth, | ||
authenticate_request_bearer_token, | ||
) | ||
import importlib | ||
|
||
|
||
class TestAuth: | ||
@patch("mlflow_oidc_auth.auth.OAuth") | ||
@patch("mlflow_oidc_auth.auth.config") | ||
def test_get_oauth_instance(self, mock_config, mock_oauth): | ||
mock_app = MagicMock() | ||
mock_oauth_instance = MagicMock() | ||
mock_oauth.return_value = mock_oauth_instance | ||
|
||
mock_config.OIDC_CLIENT_ID = "mock_client_id" | ||
mock_config.OIDC_CLIENT_SECRET = "mock_client_secret" | ||
mock_config.OIDC_DISCOVERY_URL = "mock_discovery_url" | ||
mock_config.OIDC_SCOPE = "mock_scope" | ||
|
||
result = get_oauth_instance(mock_app) | ||
|
||
mock_oauth.assert_called_once_with(mock_app) | ||
mock_oauth_instance.register.assert_called_once_with( | ||
name="oidc", | ||
client_id="mock_client_id", | ||
client_secret="mock_client_secret", | ||
server_metadata_url="mock_discovery_url", | ||
client_kwargs={"scope": "mock_scope"}, | ||
) | ||
assert result == mock_oauth_instance | ||
|
||
@patch("mlflow_oidc_auth.auth.requests") | ||
def test__get_oidc_jwks(self, mock_requests): | ||
mock_cache = MagicMock() | ||
mock_app = MagicMock() | ||
mock_app.logger.debug = MagicMock() | ||
mock_requests.get.return_value.json.return_value = {"jwks_uri": "mock_jwks_uri"} | ||
mock_cache.get.return_value = None | ||
|
||
# cache and app are imported within the _get_oidc_jwks function | ||
mlflow_oidc_app = importlib.import_module("mlflow_oidc_auth.app") | ||
with patch.object(mlflow_oidc_app, "cache", mock_cache): | ||
with patch.object(mlflow_oidc_app, "app", mock_app): | ||
result = _get_oidc_jwks() | ||
|
||
assert len(mock_requests.get.call_args) == 2 | ||
|
||
assert mock_requests.get.call_args[0][0] == "mock_jwks_uri" | ||
assert mock_requests.get.call_args[1] == {} # TODO: proper patch for first .get() return_value | ||
|
||
mock_cache.set.assert_called_once_with("jwks", mock_requests.get.return_value.json.return_value, timeout=3600) | ||
assert result == mock_requests.get.return_value.json.return_value | ||
|
||
@patch("mlflow_oidc_auth.auth._get_oidc_jwks") | ||
@patch("mlflow_oidc_auth.auth.jwt.decode") | ||
def test_validate_token(self, mock_jwt_decode, mock_get_oidc_jwks): | ||
mock_jwks = {"keys": "mock_keys"} | ||
mock_get_oidc_jwks.return_value = mock_jwks | ||
mock_payload = MagicMock() | ||
mock_jwt_decode.return_value = mock_payload | ||
|
||
token = "mock_token" | ||
result = validate_token(token) | ||
|
||
mock_get_oidc_jwks.assert_called_once() | ||
mock_jwt_decode.assert_called_once_with(token, mock_jwks) | ||
mock_payload.validate.assert_called_once() | ||
assert result == mock_payload | ||
|
||
@patch("mlflow_oidc_auth.auth.store") | ||
def test_authenticate_request_basic_auth_uses_authenticate_user(self, mock_store): | ||
mock_request = MagicMock() | ||
mock_request.authorization.username = "mock_username" | ||
mock_request.authorization.password = "mock_password" | ||
mock_store.authenticate_user.return_value = True | ||
|
||
with patch("mlflow_oidc_auth.auth.request", mock_request): | ||
# for some reason decorator doesn't mock flask | ||
result = authenticate_request_basic_auth() | ||
|
||
mock_store.authenticate_user.assert_called_once_with("mock_username", "mock_password") | ||
assert result == True | ||
|
||
@patch("mlflow_oidc_auth.auth.validate_token") | ||
def test_authenticate_request_bearer_token_uses_validate_token(self, mock_validate_token): | ||
mock_request = MagicMock() | ||
mock_request.authorization.token = "mock_token" | ||
mock_validate_token.return_value = MagicMock() | ||
with patch("mlflow_oidc_auth.auth.request", mock_request): | ||
# for some reason decorator doesn't mock flask | ||
result = authenticate_request_bearer_token() | ||
|
||
mock_validate_token.assert_called_once_with("mock_token") | ||
assert result == True | ||
|
||
@patch("mlflow_oidc_auth.auth.validate_token") | ||
def test_authenticate_request_bearer_token_exception_returns_false(self, mock_validate_token): | ||
mock_request = MagicMock() | ||
mock_request.authorization.token = "mock_token" | ||
mock_validate_token.side_effect = Exception() | ||
with patch("mlflow_oidc_auth.auth.request", mock_request): | ||
# for some reason decorator doesn't mock flask | ||
result = authenticate_request_bearer_token() | ||
|
||
mock_validate_token.assert_called_once_with("mock_token") | ||
assert result == False |
Oops, something went wrong.