Skip to content

Commit

Permalink
Test Aliases
Browse files Browse the repository at this point in the history
  • Loading branch information
couling committed Jan 15, 2024
1 parent 550db8f commit 470df40
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 25 deletions.
8 changes: 4 additions & 4 deletions source/mkdocs_deploy/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ class RedirectMechanismNotFound(Exception):
pass


class _RootDirType(Enum):
ROOT_DIR = 0
class _DefaultVersionType(Enum):
DEFAULT_VERSION = 0


ROOT_DIR = _RootDirType.ROOT_DIR
DEFAULT_VERSION = _DefaultVersionType.DEFAULT_VERSION


Version = str | _RootDirType
Version = str | _DefaultVersionType


class Source(Protocol):
Expand Down
23 changes: 10 additions & 13 deletions source/mkdocs_deploy/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
"""
import importlib.metadata
import logging
from typing import Iterable, Optional, Union
from typing import Iterable, Optional

from .abstract import Source, TargetSession, VersionNotFound
from .abstract import DEFAULT_VERSION, Source, TargetSession, Version, VersionNotFound
from .versions import DeploymentAlias

_logger = logging.getLogger(__name__)
Expand All @@ -33,7 +33,7 @@ def load_plugins() -> None:



def upload(source: Source, target: TargetSession, version_id: str, title: Optional[str]) -> None:
def upload(source: Source, target: TargetSession, version_id: str, title: str | None) -> None:
"""
Upload a file (to s3)
:param source: The site to upload. This may be a directory, or it may be zipped
Expand Down Expand Up @@ -82,7 +82,7 @@ def delete_version(target: TargetSession, version_id: str) -> None:


def create_alias(
target: TargetSession, alias_id: Union[str, type(...)], version: str, mechanisms: Optional[Iterable[str]] = None
target: TargetSession, alias_id: Version, version: str, mechanisms: Iterable[str] | None = None
) -> None:
"""
Create a new alias for a version.
Expand Down Expand Up @@ -152,9 +152,7 @@ def create_alias(
target.set_alias(alias_id, alias)


def delete_alias(
target: TargetSession, alias_id: Union[str, type(...)], mechanisms: Optional[Iterable[str]] = None
) -> None:
def delete_alias(target: TargetSession, alias_id: Version, mechanisms: Iterable[str] | None = None) -> None:
"""
Delete an alias.
Expand All @@ -166,7 +164,7 @@ def delete_alias(
:param mechanisms: Optional iterable of mechanisms to remove.
"""
_logger.info("Deleting alias %s mechanism %s", alias_id, "default" if mechanisms is None else list(mechanisms))
if alias_id is ...:
if alias_id is DEFAULT_VERSION:
alias = target.deployment_spec.default_version
if alias is None:
_logger.debug("Default alias not set")
Expand Down Expand Up @@ -198,9 +196,7 @@ def delete_alias(
target.set_alias(alias_id, None)


def refresh_alias(
target: TargetSession, alias_id: Union[str, type(...)], mechanisms: Optional[Iterable[str]] = None
) -> None:
def refresh_alias(target: TargetSession, alias_id: Version, mechanisms: Iterable[str] | None = None) -> None:
"""
Refresh redirects.
Expand All @@ -211,14 +207,15 @@ def refresh_alias(
:param mechanisms: Optional list of mechanisms to refresh. If None (default) all will be refreshed.
"""
_logger.info("Refreshing alias %s mechanisms %s", alias_id, "all" if mechanisms is None else list(mechanisms))
if alias_id is ...:
if alias_id is DEFAULT_VERSION:
alias = target.deployment_spec.default_version
else:
alias = target.deployment_spec.aliases.get(alias_id, None)
if alias is None:
_logger.warning("Cannot refresh alias %s, it doesn't exist", alias_id)
return
if mechanisms is not None:
to_refresh = [mechanism for mechanism in mechanisms if mechanism in alias.redirect_mechanisms]
to_refresh = {mechanism for mechanism in mechanisms if mechanism in alias.redirect_mechanisms}
else:
to_refresh = alias.redirect_mechanisms
available_mechanisms = target.available_redirect_mechanisms
Expand Down
8 changes: 7 additions & 1 deletion source/tests/mock_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,14 @@ class MockTargetSession(abstract.TargetSession):
closed: bool = False
close_success: bool = False
aliases: dict[Version, versions.DeploymentAlias]
redirect_mechanisms: dict[str, abstract.RedirectMechanism] = {'mock': MockRedirectMechanism()}

def __init__(self):
self.files = {}
self.deleted_files = set()
self.internal_deployment_spec = abstract.DeploymentSpec()
self.aliases = {}
self.redirect_mechanisms = self.redirect_mechanisms.copy()

def start_version(self, version_id: str, title: str) -> None:
self.internal_deployment_spec.versions[version_id] = versions.DeploymentVersion(title=title)
Expand Down Expand Up @@ -88,10 +90,14 @@ def set_alias(self, alias_id: Version, alias: DeploymentAlias | None) -> None:
del self.aliases[alias_id]
return
self.aliases[alias_id] = deepcopy(alias)
if alias_id is abstract.DEFAULT_VERSION:
self.internal_deployment_spec.default_version = self.aliases[alias_id]
else:
self.internal_deployment_spec.aliases[alias_id] = self.aliases[alias_id]

@property
def available_redirect_mechanisms(self) -> dict[str, abstract.RedirectMechanism]:
return {"mock": MockRedirectMechanism()}
return self.redirect_mechanisms

@property
def deployment_spec(self) -> DeploymentSpec:
Expand Down
9 changes: 5 additions & 4 deletions source/tests/mock_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ class MethodCall(NamedTuple):
kwargs: dict[str, Any]


def mock_wrapper(to_wrap: _T) -> tuple[_T, list[MethodCall]]:
def mock_wrapper(to_wrap: _T, name: str | None = None) -> tuple[_T, list[MethodCall]]:
method_calls: list[MethodCall] = []
return _MockWrapper(to_wrap, method_calls), method_calls # type: ignore
return _MockWrapper(to_wrap, method_calls, name), method_calls # type: ignore


class _MockWrapper:
Expand All @@ -21,14 +21,15 @@ class _MockWrapper:
_method_calls = None

def __init__(self, wrapped: Any, method_calls: list[MethodCall], name: str | None = None):
self._name = name
self._name = name if name is not None else type(wrapped).__name__
self._wrapped = wrapped
self._method_calls = method_calls

def __getattr__(self, item):
result = getattr(self._wrapped, item)
if hasattr(result, "__call__"):
return type(self)(wrapped=result, method_calls=self._method_calls, name=item)
name = f"{self._name}.{item}" if self._name is not None else item
return type(self)(wrapped=result, method_calls=self._method_calls, name=name)
return result

def __setattr__(self, key, value):
Expand Down
115 changes: 112 additions & 3 deletions source/tests/test_modules/test_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest

from mkdocs_deploy import actions, versions
from mkdocs_deploy import abstract, actions, versions
from ..mock_plugin import MockSource, MockTargetSession
from ..mock_wrapper import mock_wrapper

Expand All @@ -21,7 +21,7 @@ def test_upload(title: str | None):
try:
actions.upload(source=source, target=session, version_id=VERSION, title=title)

assert session_method_calls[0].name == "start_version"
assert session_method_calls[0].name == "MockTargetSession.start_version"

assert session.files == {(VERSION, file): content for file, content in source.files.items()}

Expand Down Expand Up @@ -59,4 +59,113 @@ def test_upload_explicit_title_overrides_existing_one():

assert session.deployment_spec.versions == {
VERSION: versions.DeploymentVersion(title=VERSION_TITLE),
}
}


@pytest.mark.parametrize("alias", ["latest", abstract.DEFAULT_VERSION], ids=["named", "default"])
def test_refresh_all_redirects_on_alias(alias: abstract.Version):
session = MockTargetSession()
for version in ("1.0", "1.1", "2.0"):
session.start_version(version, version)
session.available_redirect_mechanisms['mock'].create_redirect(session, abstract.DEFAULT_VERSION, "1.1")
session.set_alias(alias, abstract.DeploymentAlias(version_id="1.1", redirect_mechanisms={'mock'}))

method_1, method_calls_1 = mock_wrapper(session.redirect_mechanisms['mock'])
method_2, method_calls_2 = mock_wrapper(session.redirect_mechanisms['mock'])
session.redirect_mechanisms['mock'] = method_1
session.redirect_mechanisms['mock_2'] = method_2

actions.refresh_alias(session, alias)
assert not method_calls_2 # Only existing aliases are refreshed
assert len(method_calls_1) == 1
assert method_calls_1[0].name == 'MockRedirectMechanism.refresh_redirect'
assert method_calls_1[0].kwargs["alias"] == alias
assert method_calls_1[0].kwargs["version_id"] == "1.1"


@pytest.mark.parametrize("alias", ["latest", abstract.DEFAULT_VERSION], ids=["Named", "Default"])
def test_refresh_specific_redirect_on_alias(alias: abstract.Version):
session = MockTargetSession()
for version in ("1.0", "1.1", "2.0"):
session.start_version(version, version)
session.set_alias(alias, abstract.DeploymentAlias(version_id="1.1", redirect_mechanisms={'mock_2'}))
session.available_redirect_mechanisms['mock'].create_redirect(session, abstract.DEFAULT_VERSION, "1.1")

method_1, method_calls_1 = mock_wrapper(session.redirect_mechanisms['mock'])
method_2, method_calls_2 = mock_wrapper(session.redirect_mechanisms['mock'])
session.redirect_mechanisms['mock'] = method_1
session.redirect_mechanisms['mock_2'] = method_2

actions.refresh_alias(session, alias, {"mock_2"})
assert not method_calls_1 # Only named aliases are refreshed
assert len(method_calls_2) == 1
assert method_calls_2[0].name == 'MockRedirectMechanism.refresh_redirect'
assert method_calls_2[0].kwargs["alias"] == alias
assert method_calls_2[0].kwargs["version_id"] == "1.1"



@pytest.mark.parametrize("alias", ["latest", abstract.DEFAULT_VERSION], ids=["named", "default"])
def test_delete_all_redirects_on_alias(alias: abstract.Version):
session = MockTargetSession()
for version in ("1.0", "1.1", "2.0"):
session.start_version(version, version)
session.available_redirect_mechanisms['mock'].create_redirect(session, abstract.DEFAULT_VERSION, "1.1")
session.set_alias(alias, abstract.DeploymentAlias(version_id="1.1", redirect_mechanisms={'mock'}))

method_1, method_calls_1 = mock_wrapper(session.redirect_mechanisms['mock'])
method_2, method_calls_2 = mock_wrapper(session.redirect_mechanisms['mock'])
session.redirect_mechanisms['mock'] = method_1
session.redirect_mechanisms['mock_2'] = method_2

actions.delete_alias(session, alias)
assert not method_calls_2 # Only existing aliases are refreshed
assert len(method_calls_1) == 1
assert method_calls_1[0].name == 'MockRedirectMechanism.delete_redirect'
assert method_calls_1[0].kwargs["alias"] == alias

assert alias not in session.aliases


@pytest.mark.parametrize("alias", ["latest", abstract.DEFAULT_VERSION], ids=["Named", "Default"])
def test_delete_last_redirect_on_alias(alias: abstract.Version):
session = MockTargetSession()
for version in ("1.0", "1.1", "2.0"):
session.start_version(version, version)
session.set_alias(alias, abstract.DeploymentAlias(version_id="1.1", redirect_mechanisms={'mock_2'}))
session.available_redirect_mechanisms['mock'].create_redirect(session, abstract.DEFAULT_VERSION, "1.1")

method_1, method_calls_1 = mock_wrapper(session.redirect_mechanisms['mock'])
method_2, method_calls_2 = mock_wrapper(session.redirect_mechanisms['mock'])
session.redirect_mechanisms['mock'] = method_1
session.redirect_mechanisms['mock_2'] = method_2

actions.delete_alias(session, alias, {"mock_2"})
assert not method_calls_1 # Only named aliases are refreshed
assert len(method_calls_2) == 1
assert method_calls_2[0].name == 'MockRedirectMechanism.delete_redirect'
assert method_calls_2[0].kwargs["alias"] == alias

assert alias not in session.aliases


@pytest.mark.parametrize("alias", ["latest", abstract.DEFAULT_VERSION], ids=["Named", "Default"])
def test_delete_one_of_two_redirects_on_alias(alias: abstract.Version):
session = MockTargetSession()
for version in ("1.0", "1.1", "2.0"):
session.start_version(version, version)
session.set_alias(alias, abstract.DeploymentAlias(version_id="1.1", redirect_mechanisms={'mock' ,'mock_2'}))
session.available_redirect_mechanisms['mock'].create_redirect(session, abstract.DEFAULT_VERSION, "1.1")

method_1, method_calls_1 = mock_wrapper(session.redirect_mechanisms['mock'])
method_2, method_calls_2 = mock_wrapper(session.redirect_mechanisms['mock'])
session.redirect_mechanisms['mock'] = method_1
session.redirect_mechanisms['mock_2'] = method_2

actions.delete_alias(session, alias, {"mock_2"})
assert not method_calls_1 # Only named aliases are refreshed
assert len(method_calls_2) == 1
assert method_calls_2[0].name == 'MockRedirectMechanism.delete_redirect'
assert method_calls_2[0].kwargs["alias"] == alias

assert alias in session.aliases

0 comments on commit 470df40

Please sign in to comment.