Coverage for tests/conftest.py: 99%
73 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-09 12:09 +0100
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-09 12:09 +0100
1from __future__ import annotations
3import os
4from copy import copy
5from functools import partial
6from pathlib import Path
7from typing import Any
8from typing import Generator
9from typing import IO
10from typing import Mapping
11from typing import Protocol
13import click
14import pytest
15import typer
16from _pytest.logging import LogCaptureFixture
17from loguru import logger
18from pydantic import BaseModel
19from typer.testing import CliRunner
20from typer.testing import Result
22from harbor_cli.main import app as main_app # noreorder
24# We can't import these before main is imported, because of circular imports
25from harbor_cli.config import HarborCLIConfig
26from harbor_cli.format import OutputFormat
27from harbor_cli import state
28from ._utils import compact_renderables
31runner = CliRunner()
34@pytest.fixture(scope="session")
35def app():
36 return main_app
39@pytest.fixture(scope="session", autouse=True)
40def dumb_terminal():
41 """Test in a dumb terminal, so that we don't get ANSI escape codes in the output."""
42 os.environ["TERM"] = "dumb"
43 os.environ["NO_COLOR"] = "1"
44 os.environ.pop("COLORTERM", None)
45 os.environ.pop("FORCE_COLOR", None)
48@pytest.fixture(scope="session")
49def config() -> HarborCLIConfig:
50 conf = HarborCLIConfig()
51 # These are required to run commands
52 conf.harbor.url = "https://harbor.example.com"
53 conf.harbor.username = "admin"
54 conf.harbor.secret = "password"
55 return conf
58@pytest.fixture()
59def config_file(tmp_path: Path, config: HarborCLIConfig) -> Path: # type: ignore
60 """Setup the CLI config for testing."""
61 conf_path = tmp_path / "config.toml"
62 config.save(conf_path)
63 yield conf_path
66class PartialInvoker(Protocol):
67 """Protocol for a partial function that invokes a CLI command."""
69 def __call__(
70 self,
71 args: str | list[str] | None,
72 input: bytes | str | IO | None = None,
73 env: Mapping[str, str] | None = None,
74 catch_exceptions: bool = True,
75 color: bool = False,
76 **extra: Any,
77 ) -> Result:
78 ...
81@pytest.fixture
82def invoke(app: typer.Typer, config_file: Path) -> PartialInvoker:
83 """Partial function for invoking a CLI command with the app as the entrypoint,
84 and the temp config as the config file."""
85 p = partial(runner.invoke, app, env={"HARBOR_CLI_CONFIG": str(config_file)})
86 return p
89@pytest.fixture
90def mock_ctx() -> typer.Context:
91 """Create a mock context."""
92 return typer.Context(click.Command(name="mock"))
95@pytest.fixture(name="output_format", scope="function", params=list(OutputFormat))
96def _output_format(request: pytest.FixtureRequest) -> OutputFormat:
97 """Fixture for testing all output formats."""
98 return request.param
101@pytest.fixture(scope="function", params=list(OutputFormat))
102def output_format_arg(output_format: OutputFormat) -> list[str]:
103 """Parametrized fixture that returns the CLI argument for all output formats."""
104 return ["--format", output_format.value]
107_ORIGINAL_STATE = copy(state.state)
110@pytest.fixture(scope="function", autouse=True)
111def revert_state() -> Generator[None, None, None]:
112 """Reverts the global state back to its original value after the test is run."""
113 yield
114 state.state = _ORIGINAL_STATE
117@pytest.fixture(scope="function", params=compact_renderables)
118def compact_table_renderable(request: pytest.FixtureRequest) -> BaseModel:
119 """Fixture for testing compact table renderables that can be instantiated with no arguments."""
120 return request.param()
123@pytest.fixture
124def caplog(caplog: LogCaptureFixture):
125 handler_id = logger.add(caplog.handler, format="{message}")
126 yield caplog
127 logger.remove(handler_id)