Skip to content

Commit

Permalink
Merge pull request #523 from eduvic-mozilla/feature/IAM-1403
Browse files Browse the repository at this point in the history
Unit tests for the glory of Mozilla
  • Loading branch information
dividehex authored Sep 4, 2024
2 parents 5d7555c + 78152ef commit 32b710b
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ node_modules
.vscode/*
build/*
envfile
.coverage
3 changes: 0 additions & 3 deletions dashboard/op/yaml_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ def __init__(self, app_dict):
self._render_data()
self._alphabetize()

def _load_authorized(self, session):
pass

def _load_data(self):
try:
stream = yaml.safe_load(self.app_dict)
Expand Down
Empty file added tests/api/__init__.py
Empty file.
Empty file added tests/api/test_idp.py
Empty file.
Empty file added tests/models/__init__.py
Empty file.
152 changes: 152 additions & 0 deletions tests/models/test_tile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import pytest
import os
import urllib3
from unittest import mock
from dashboard.models.tile import __file__ as module_file
from dashboard.models.tile import CDNTransfer


class MockAppConfig:
CDN = "http://mock-cdn.com"


@pytest.fixture
def cdn_transfer():
app_config = MockAppConfig()
return CDNTransfer(app_config)


def test_is_updated_etag_mismatch(mocker, cdn_transfer):
mock_request = mocker.patch("urllib3.PoolManager.request")
mock_request.return_value.headers = {"ETag": "new-etag"}
mocker.patch.object(cdn_transfer, "_etag", return_value="old-etag")

assert cdn_transfer.is_updated() is True


def test_is_updated_etag_match(mocker, cdn_transfer):
mock_request = mocker.patch("urllib3.PoolManager.request")
mock_request.return_value.headers = {"ETag": "matching-etag"}
mocker.patch.object(cdn_transfer, "_etag", return_value="matching-etag")

assert cdn_transfer.is_updated() is False


def test_update_etag(mocker, cdn_transfer):
mock_open = mocker.patch("builtins.open", mocker.mock_open())

cdn_transfer._update_etag("new-etag")

mock_open.assert_called_once_with(os.path.join(os.path.dirname(module_file), "../data/apps.yml-etag"), "w+")
mock_open().write.assert_called_once_with("new-etag")


def test_etag_file_exists(mocker, cdn_transfer):
mock_open = mocker.patch("builtins.open", mocker.mock_open(read_data="stored-etag"))

assert cdn_transfer._etag() == "stored-etag"
mock_open.assert_called_once_with(os.path.join(os.path.dirname(module_file), "../data/apps.yml-etag"), "r")


def test_etag_file_missing(mocker, cdn_transfer):
mock_open = mocker.patch("builtins.open", mocker.mock_open())
mock_open.side_effect = FileNotFoundError

assert cdn_transfer._etag() == "12345678"


# NOTE: Temporarily disabling this test until we have a reliable means of patching out
# `urllib3` calls. Even after multiple attempts, the call to `request` still
# tries to resolve the mock domain in the mocked CDN object.
# References:
# - https://mozilla-hub.atlassian.net/jira/software/c/projects/IAM/issues/IAM-1403
#
@pytest.mark.skip(reason="Cannot properly mock `urllib3`'s `request` call.")
def test_download_config(mocker, cdn_transfer):
mock_response = mock.Mock()
mock_response.status = 200
mock_response.headers = {"ETag": "new-etag"}
mock_response.data = b"mock apps.yml content"
with mock.patch("urllib3.PoolManager") as mock_pool_manager, mock.patch("os.fsync") as mock_fsync, mock.patch(
"builtins.open", mock.mock_open()
) as mock_open_yml, mock.patch("builtins.open", mock.mock_open()) as mock_open_etag:
mock_http = mock_pool_manager.return_value
mock_http.request.return_value = mock_response
mock_open_yml.return_value.fileno.return_value = 3
cdn_transfer._download_config()

mock_open_yml.assert_any_call(os.path.join(os.path.dirname(module_file), "../data/apps.yml"), "wb")
mock_open_yml().write.assert_called_once_with(b"mock apps.yml content")
mock_fsync().assert_called_once_with(3)
mock_open_etag.assert_any_call(os.path.join(os.path.dirname(module_file), "../data/apps.yml-etag"), "w+")
mock_open_etag().write.assert_called_once_with("new-etag")


def test_download_config_http_error(mocker, cdn_transfer):
mocker.patch("urllib3.PoolManager.request", side_effect=urllib3.exceptions.HTTPError)

with pytest.raises(urllib3.exceptions.HTTPError):
cdn_transfer._download_config()


def test_load_apps_yml(mocker, cdn_transfer):
mock_open = mocker.patch("builtins.open", mocker.mock_open(read_data="mock apps.yml content"))

cdn_transfer._load_apps_yml()

mock_open.assert_called_once_with(os.path.join(os.path.dirname(module_file), "../data/apps.yml"), "r")
assert cdn_transfer.apps_yml == "mock apps.yml content"


# NOTE: Temporarily disabling this test until we have a reliable means of patching out
# `urllib3` calls. Even after multiple attempts, the call to `request` still
# tries to resolve the mock domain in the mocked CDN object.
# References:
# - https://mozilla-hub.atlassian.net/jira/software/c/projects/IAM/issues/IAM-1403
#
@pytest.mark.skip(reason="Cannot properly mock `urllib3`'s `request` call.")
def test_sync_config_update(mocker, cdn_transfer):
mocker.patch.object(CDNTransfer, "is_updated", return_value=True)
mock_download = mocker.patch.object(CDNTransfer, "_download_config")
mock_load = mocker.patch.object(CDNTransfer, "_load_apps_yml")

cdn_transfer.sync_config()

mock_download.assert_called_once()
mock_load.assert_called_once()


# NOTE: Temporarily disabling this test until we have a reliable means of patching out
# `urllib3` calls. Even after multiple attempts, the call to `request` still
# tries to resolve the mock domain in the mocked CDN object.
# References:
# - https://mozilla-hub.atlassian.net/jira/software/c/projects/IAM/issues/IAM-1403
#
@pytest.mark.skip(reason="Cannot properly mock `urllib3`'s `request` call.")
def test_sync_config_no_update(mocker, cdn_transfer):
mocker.patch.object(CDNTransfer, "is_updated", return_value=False)
mock_download = mocker.patch.object(CDNTransfer, "_download_config")
mock_load = mocker.patch.object(CDNTransfer, "_load_apps_yml")

cdn_transfer.sync_config()

mock_download.assert_not_called()
mock_load.assert_called_once()


# NOTE: Temporarily disabling this test until we have a reliable means of patching out
# `urllib3` calls. Even after multiple attempts, the call to `request` still
# tries to resolve the mock domain in the mocked CDN object.
# References:
# - https://mozilla-hub.atlassian.net/jira/software/c/projects/IAM/issues/IAM-1403
#
@pytest.mark.skip(reason="Cannot properly mock `urllib3`'s `request` call.")
def test_sync_config_download_error(mocker, cdn_transfer):
mocker.patch.object(CDNTransfer, "is_updated", return_value=True)
mock_download = mocker.patch.object(CDNTransfer, "_download_config", side_effect=Exception("Test Exception"))
mock_load = mocker.patch.object(CDNTransfer, "_load_apps_yml")

cdn_transfer.sync_config()

mock_download.assert_called_once()
mock_load.assert_not_called() # if download fails, it shouldn't try to load
Empty file added tests/op/__init__.py
Empty file.
76 changes: 76 additions & 0 deletions tests/op/test_yaml_loader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import pytest
import yaml
from unittest.mock import patch, MagicMock
from dashboard.op.yaml_loader import Application

# Testing data
apps = """
apps:
- application:
name: "Test Application"
url: "https://example.com"
vanity_url:
- "/test-app"
- application:
name: "Long Application Name That Exceeds Limit"
url: "https://example-long.com"
vanity_url:
- "/long-app"
"""


@pytest.fixture
def valid_application():
return Application(apps)


def test_load_data(valid_application):
assert valid_application.apps is not None
assert len(valid_application.apps["apps"]) == 2
assert valid_application.apps["apps"][1]["application"]["name"] == "Test Application"


def test_load_data_invalid_yaml():
invalid_app = "invalid: : : yaml"
with pytest.raises(TypeError):
Application(invalid_app)


def test_render_data(valid_application):
assert valid_application.apps["apps"][0]["application"]["name"] == "Long Application.."
assert valid_application.apps["apps"][1]["application"]["alt_text"] == "Test Application"


def test_alphabetize(valid_application):
assert valid_application.apps["apps"][0]["application"]["name"] == "Long Application.."
assert valid_application.apps["apps"][1]["application"]["name"] == "Test Application"


def test_truncate(valid_application):
assert valid_application._truncate("Short Name") == "Short Name"
assert valid_application._truncate("This is a very long application name") == "This is a very l.."


def test_vanity_urls(valid_application):
redirects = valid_application.vanity_urls()
assert len(redirects) == 2
assert redirects[0] == {"/long-app": "https://example-long.com"}
assert redirects[1] == {"/test-app": "https://example.com"}


def test_vanity_urls_no_vanity():
app_no_vanity = """
apps:
- application:
name: "No Vanity App"
url: "https://no-vanity.com"
"""
app = Application(app_no_vanity)
redirects = app.vanity_urls()
assert len(redirects) == 0


def test_no_apps_present(valid_application):
del valid_application.apps["apps"]

assert len(valid_application.vanity_urls()) == 0
3 changes: 0 additions & 3 deletions tests/test_tile.py

This file was deleted.

1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package = wheel
wheel_build_env = .pkg
deps =
pytest>=6
pytest-mock>=3
-rrequirements.txt
commands =
pytest {tty:--color=yes} {posargs}
Expand Down

0 comments on commit 32b710b

Please sign in to comment.