diff --git a/.github/workflows/build-python.yaml b/.github/workflows/build-python.yaml index 89e15b0c..8f51ea02 100644 --- a/.github/workflows/build-python.yaml +++ b/.github/workflows/build-python.yaml @@ -19,8 +19,8 @@ jobs: matrix: #os: ["ubuntu-latest", "macos-latest", "windows-latest"] # Mac and Windows chew through build minutes - waiting until repo is public to enable - os: ["ubuntu-latest"] - python: ["3.10", "3.11"] + os: ["ubuntu-latest", "windows-latest"] + python: ["3.10"] runs-on: ${{ matrix.os }} steps: - name: Checkout source code diff --git a/poetry.lock b/poetry.lock index 6d878199..3a5adb5b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -181,7 +181,7 @@ redis = ["redis (>=2.10.5)"] [[package]] name = "certifi" -version = "2022.9.24" +version = "2022.12.7" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -728,7 +728,7 @@ pip = "*" [[package]] name = "pip-audit" -version = "2.4.7" +version = "2.4.8" description = "A tool for scanning Python environments for known vulnerabilities" category = "dev" optional = false @@ -738,7 +738,7 @@ python-versions = ">=3.7" CacheControl = {version = ">=0.12.10", extras = ["filecache"]} cyclonedx-python-lib = ">=2.0.0,<2.5.0 || >2.5.0" html5lib = ">=1.1" -packaging = ">=21.0.0" +packaging = ">=21.0.0,<22.0.0" pip-api = ">=0.0.28" pip-requirements-parser = ">=31.2.0" resolvelib = ">=0.8.0" @@ -778,7 +778,7 @@ testing = ["pytest", "pytest-cov"] [[package]] name = "platformdirs" -version = "2.5.4" +version = "2.6.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false @@ -818,7 +818,7 @@ ssh = ["paramiko"] [[package]] name = "prompt-toolkit" -version = "3.0.33" +version = "3.0.36" description = "Library for building powerful interactive command lines in Python" category = "main" optional = false @@ -1502,8 +1502,8 @@ cachecontrol = [ {file = "CacheControl-0.12.11.tar.gz", hash = "sha256:a5b9fcc986b184db101aa280b42ecdcdfc524892596f606858e0b7a8b4d9e144"}, ] certifi = [ - {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"}, - {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"}, + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, ] cffi = [ {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, @@ -2111,8 +2111,8 @@ pip-api = [ {file = "pip_api-0.0.30-py3-none-any.whl", hash = "sha256:2a0314bd31522eb9ffe8a99668b0d07fee34ebc537931e7b6483001dbedcbdc9"}, ] pip-audit = [ - {file = "pip_audit-2.4.7-py3-none-any.whl", hash = "sha256:a99f825ee431a89b89981c4e9e6eaacff5af3233783f2f5d79fe03306dc378ce"}, - {file = "pip_audit-2.4.7.tar.gz", hash = "sha256:f87b37b6db5317a3f5ecebc202b5d4401958b5e4bd05b39d7b230bdc6f63c34b"}, + {file = "pip_audit-2.4.8-py3-none-any.whl", hash = "sha256:40876d6ad6adf5ac64fba1ecef034db9804c35c2c18f7c22cb7b11b382c10df9"}, + {file = "pip_audit-2.4.8.tar.gz", hash = "sha256:a45540ab0c5a9311315ca42c78fa8f72cf3598d5968a67d883d2d6194eda598c"}, ] pip-requirements-parser = [ {file = "pip-requirements-parser-31.2.0.tar.gz", hash = "sha256:8c2a6f8e091ac2693824a5ef4e3b250226e34f74a20a91a87b9ab0714b47788f"}, @@ -2123,8 +2123,8 @@ pkginfo = [ {file = "pkginfo-1.9.2.tar.gz", hash = "sha256:ac03e37e4d601aaee40f8087f63fc4a2a6c9814dda2c8fa6aab1b1829653bdfa"}, ] platformdirs = [ - {file = "platformdirs-2.5.4-py3-none-any.whl", hash = "sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10"}, - {file = "platformdirs-2.5.4.tar.gz", hash = "sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7"}, + {file = "platformdirs-2.6.0-py3-none-any.whl", hash = "sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca"}, + {file = "platformdirs-2.6.0.tar.gz", hash = "sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e"}, ] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, @@ -2135,8 +2135,8 @@ plumbum = [ {file = "plumbum-1.8.0.tar.gz", hash = "sha256:f1da1f167a2afe731a85de3f56810f424926c0a1a8fd1999ceb2ef20b618246d"}, ] prompt-toolkit = [ - {file = "prompt_toolkit-3.0.33-py3-none-any.whl", hash = "sha256:ced598b222f6f4029c0800cefaa6a17373fb580cd093223003475ce32805c35b"}, - {file = "prompt_toolkit-3.0.33.tar.gz", hash = "sha256:535c29c31216c77302877d5120aef6c94ff573748a5b5ca5b1b1f76f5e700c73"}, + {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"}, + {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"}, ] pycparser = [ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, diff --git a/src/algokit/core/log_handlers.py b/src/algokit/core/log_handlers.py index 02120082..e2de4c35 100644 --- a/src/algokit/core/log_handlers.py +++ b/src/algokit/core/log_handlers.py @@ -94,6 +94,7 @@ def initialise_logging() -> None: filename=get_app_state_dir() / "cli.log", maxBytes=1 * 1024 * 1024, backupCount=5, + encoding="utf-8", ) file_log_handler.setLevel(logging.DEBUG) file_log_handler.formatter = logging.Formatter( diff --git a/tests/conftest.py b/tests/conftest.py index bfecfbeb..37ecb257 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,6 +9,7 @@ from approvaltests.reporters.generic_diff_reporter_factory import GenericDiffReporter # type: ignore from prompt_toolkit.application import create_app_session from prompt_toolkit.input import PipeInput, create_pipe_input +from prompt_toolkit.output import DummyOutput from pytest_mock import MockerFixture from utils.app_dir_mock import AppDirs, tmp_app_dir from utils.proc_mock import ProcMock @@ -31,7 +32,7 @@ def app_dir_mock(mocker: MockerFixture, tmp_path: Path) -> AppDirs: @pytest.fixture(scope="function") def mock_questionary_input() -> typing.Iterator[PipeInput]: with create_pipe_input() as pipe_input: - with create_app_session(input=pipe_input): + with create_app_session(input=pipe_input, output=DummyOutput()): yield pipe_input diff --git a/tests/goal/test_goal.py b/tests/goal/test_goal.py index bb0d69bc..dd8bc3d9 100644 --- a/tests/goal/test_goal.py +++ b/tests/goal/test_goal.py @@ -1,8 +1,8 @@ from subprocess import CompletedProcess -from approvaltests import verify # type: ignore from pytest_mock import MockerFixture from utils.app_dir_mock import AppDirs +from utils.approvals import verify from utils.click_invoker import invoke from utils.proc_mock import ProcMock diff --git a/tests/init/test_init.py b/tests/init/test_init.py index b427be30..7b99f5b2 100644 --- a/tests/init/test_init.py +++ b/tests/init/test_init.py @@ -2,18 +2,24 @@ import subprocess from pathlib import Path +import click import pytest from _pytest.tmpdir import TempPathFactory -from approvaltests import verify -from click import unstyle from prompt_toolkit.input import PipeInput from pytest_mock import MockerFixture +from utils.approvals import TokenScrubber, combine_scrubbers, verify from utils.click_invoker import invoke PARENT_DIRECTORY = Path(__file__).parent GIT_BUNDLE_PATH = PARENT_DIRECTORY / "copier-script-v0.1.0.gitbundle" +def make_output_scrubber(**extra_tokens: str) -> TokenScrubber: + default_tokens = {"test_parent_directory": str(PARENT_DIRECTORY)} + tokens = default_tokens | extra_tokens + return combine_scrubbers(click.unstyle, TokenScrubber(tokens=tokens)) + + @pytest.fixture(autouse=True, scope="module") def supress_copier_dependencies_debug_output(): logging.getLogger("plumbum.local").setLevel("INFO") @@ -43,7 +49,7 @@ def test_init_minimal_interaction_required_no_git_no_network( assert result.exit_code == 0 paths = {p.relative_to(cwd) for p in cwd.rglob("*")} assert paths == {Path("myapp"), Path("myapp") / "script.sh"} - verify(unstyle(result.output).replace(str(PARENT_DIRECTORY), "{test_parent_directory}")) + verify(result.output, scrubber=make_output_scrubber()) def test_init_minimal_interaction_required_yes_git_no_network( @@ -75,9 +81,8 @@ def test_init_minimal_interaction_required_yes_git_no_network( assert git_rev_list.returncode == 0 git_initial_commit_hash = git_rev_list.stdout[:7] verify( - unstyle(result.output) - .replace(git_initial_commit_hash, "{git_initial_commit_hash}") - .replace(str(PARENT_DIRECTORY), "{test_parent_directory}") + result.output, + scrubber=make_output_scrubber(git_initial_commit_hash=git_initial_commit_hash), ) @@ -93,7 +98,7 @@ def test_init_do_not_use_existing_folder(tmp_path_factory: TempPathFactory, mock ) assert result.exit_code == 1 - verify(unstyle(result.output).replace(str(PARENT_DIRECTORY), "{test_parent_directory}")) + verify(result.output, scrubber=make_output_scrubber()) def test_init_use_existing_folder(tmp_path_factory: TempPathFactory, mock_questionary_input: PipeInput): @@ -109,7 +114,7 @@ def test_init_use_existing_folder(tmp_path_factory: TempPathFactory, mock_questi ) assert result.exit_code == 0 - verify(unstyle(result.output).replace(str(PARENT_DIRECTORY), "{test_parent_directory}")) + verify(result.output, scrubber=make_output_scrubber()) def test_init_existing_filename_same_as_folder_name( @@ -127,7 +132,7 @@ def test_init_existing_filename_same_as_folder_name( ) assert result.exit_code == 1 - verify(unstyle(result.output).replace(str(PARENT_DIRECTORY), "{test_parent_directory}")) + verify(result.output, scrubber=make_output_scrubber()) def test_init_template_selection(tmp_path_factory: TempPathFactory, mock_questionary_input: PipeInput): @@ -141,7 +146,7 @@ def test_init_template_selection(tmp_path_factory: TempPathFactory, mock_questio ) assert result.exit_code == 0 - verify(unstyle(result.output).replace(str(PARENT_DIRECTORY), "{test_parent_directory}")) + verify(result.output, scrubber=make_output_scrubber()) def test_init_invalid_template_url(tmp_path_factory: TempPathFactory, mock_questionary_input: PipeInput): @@ -154,7 +159,7 @@ def test_init_invalid_template_url(tmp_path_factory: TempPathFactory, mock_quest ) assert result.exit_code == 1 - verify(unstyle(result.output).replace(str(PARENT_DIRECTORY), "{test_parent_directory}")) + verify(result.output, scrubber=make_output_scrubber()) def test_init_project_name(tmp_path_factory: TempPathFactory, mock_questionary_input: PipeInput): @@ -170,7 +175,7 @@ def test_init_project_name(tmp_path_factory: TempPathFactory, mock_questionary_i assert result.exit_code == 0 paths = {p.relative_to(cwd) for p in cwd.rglob("*")} assert paths == {Path(project_name), Path(project_name) / "script.sh"} - verify(unstyle(result.output).replace(str(PARENT_DIRECTORY), "{test_parent_directory}")) + verify(result.output, scrubber=make_output_scrubber()) def test_init_project_name_not_empty(tmp_path_factory: TempPathFactory, mock_questionary_input: PipeInput): @@ -187,7 +192,7 @@ def test_init_project_name_not_empty(tmp_path_factory: TempPathFactory, mock_que assert result.exit_code == 0 paths = {p.relative_to(cwd) for p in cwd.rglob("*")} assert paths == {Path(project_name), Path(project_name) / "script.sh"} - verify(unstyle(result.output).replace(str(PARENT_DIRECTORY), "{test_parent_directory}")) + verify(result.output, scrubber=make_output_scrubber()) def test_init_project_name_reenter_folder_name(tmp_path_factory: TempPathFactory, mock_questionary_input: PipeInput): @@ -208,7 +213,7 @@ def test_init_project_name_reenter_folder_name(tmp_path_factory: TempPathFactory assert result.exit_code == 0 paths = {p.relative_to(cwd) for p in cwd.rglob("*")} assert paths == {Path(project_name_2), Path(project_name_2) / "script.sh", Path(project_name)} - verify(unstyle(result.output).replace(str(PARENT_DIRECTORY), "{test_parent_directory}")) + verify(result.output, scrubber=make_output_scrubber()) def test_init_ask_about_git(tmp_path_factory: TempPathFactory, mock_questionary_input: PipeInput): @@ -239,9 +244,8 @@ def test_init_ask_about_git(tmp_path_factory: TempPathFactory, mock_questionary_ assert git_rev_list.returncode == 0 git_initial_commit_hash = git_rev_list.stdout[:7] verify( - unstyle(result.output) - .replace(git_initial_commit_hash, "{git_initial_commit_hash}") - .replace(str(PARENT_DIRECTORY), "{test_parent_directory}") + result.output, + scrubber=make_output_scrubber(git_initial_commit_hash=git_initial_commit_hash), ) @@ -258,7 +262,7 @@ def test_init_template_url_and_template_name(tmp_path_factory: TempPathFactory, ) assert result.exit_code == 1 - verify(unstyle(result.output).replace(str(PARENT_DIRECTORY), "{test_parent_directory}")) + verify(result.output, scrubber=make_output_scrubber()) def test_init_no_community_template(tmp_path_factory: TempPathFactory, mock_questionary_input: PipeInput): @@ -271,7 +275,7 @@ def test_init_no_community_template(tmp_path_factory: TempPathFactory, mock_ques ) assert result.exit_code == 1 - verify(unstyle(result.output).replace(str(PARENT_DIRECTORY), "{test_parent_directory}")) + verify(result.output, scrubber=make_output_scrubber()) def test_init_input_template_url(tmp_path_factory: TempPathFactory, mock_questionary_input: PipeInput): @@ -288,7 +292,7 @@ def test_init_input_template_url(tmp_path_factory: TempPathFactory, mock_questio ) assert result.exit_code == 0 - verify(unstyle(result.output).replace(str(PARENT_DIRECTORY), "{test_parent_directory}")) + verify(result.output, scrubber=make_output_scrubber()) def test_init_with_defaults(tmp_path_factory: TempPathFactory, mock_questionary_input: PipeInput): @@ -303,7 +307,7 @@ def test_init_with_defaults(tmp_path_factory: TempPathFactory, mock_questionary_ assert result.exit_code == 0 paths = {p.relative_to(cwd) for p in cwd.rglob("*")} assert paths == {Path("myapp"), Path("myapp") / "none"} - verify(unstyle(result.output).replace(str(PARENT_DIRECTORY), "{test_parent_directory}")) + verify(result.output, scrubber=make_output_scrubber()) def test_init_with_official_template_name(tmp_path_factory: TempPathFactory, mock_questionary_input: PipeInput): @@ -323,4 +327,4 @@ def test_init_with_official_template_name(tmp_path_factory: TempPathFactory, moc Path("myapp") / "smart_contracts", } ) - verify(unstyle(result.output).replace(str(PARENT_DIRECTORY), "{test_parent_directory}")) + verify(result.output, scrubber=make_output_scrubber()) diff --git a/tests/sandbox/test_sandbox_console.py b/tests/sandbox/test_sandbox_console.py index 1f302a71..1ab0c685 100644 --- a/tests/sandbox/test_sandbox_console.py +++ b/tests/sandbox/test_sandbox_console.py @@ -1,7 +1,7 @@ from subprocess import CompletedProcess -from approvaltests import verify # type: ignore from pytest_mock import MockerFixture +from utils.approvals import verify from utils.click_invoker import invoke from utils.proc_mock import ProcMock diff --git a/tests/sandbox/test_sandbox_reset.py b/tests/sandbox/test_sandbox_reset.py index d011a681..fb705f27 100644 --- a/tests/sandbox/test_sandbox_reset.py +++ b/tests/sandbox/test_sandbox_reset.py @@ -1,6 +1,6 @@ from algokit.core.sandbox import get_docker_compose_yml -from approvaltests import verify # type: ignore from utils.app_dir_mock import AppDirs +from utils.approvals import verify from utils.click_invoker import invoke from utils.proc_mock import ProcMock diff --git a/tests/sandbox/test_sandbox_start.py b/tests/sandbox/test_sandbox_start.py index 8f084af9..4d56f6bb 100644 --- a/tests/sandbox/test_sandbox_start.py +++ b/tests/sandbox/test_sandbox_start.py @@ -1,8 +1,8 @@ import json from algokit.core.sandbox import get_docker_compose_yml -from approvaltests import verify # type: ignore from utils.app_dir_mock import AppDirs +from utils.approvals import verify from utils.click_invoker import invoke from utils.proc_mock import ProcMock diff --git a/tests/sandbox/test_sandbox_status.py b/tests/sandbox/test_sandbox_status.py index 6c6872f5..f9b0f93c 100644 --- a/tests/sandbox/test_sandbox_status.py +++ b/tests/sandbox/test_sandbox_status.py @@ -1,9 +1,9 @@ import json import httpx -from approvaltests import verify from pytest_httpx import HTTPXMock from utils.app_dir_mock import AppDirs +from utils.approvals import verify from utils.click_invoker import invoke from utils.proc_mock import ProcMock diff --git a/tests/sandbox/test_sandbox_stop.py b/tests/sandbox/test_sandbox_stop.py index 9cc71aed..4aa89c7d 100644 --- a/tests/sandbox/test_sandbox_stop.py +++ b/tests/sandbox/test_sandbox_stop.py @@ -1,5 +1,5 @@ -from approvaltests import verify # type: ignore from utils.app_dir_mock import AppDirs +from utils.approvals import verify from utils.click_invoker import invoke from utils.proc_mock import ProcMock diff --git a/tests/utils/approvals.py b/tests/utils/approvals.py new file mode 100644 index 00000000..f14c9c72 --- /dev/null +++ b/tests/utils/approvals.py @@ -0,0 +1,40 @@ +from typing import Any + +import approvaltests +from approvaltests.scrubbers.scrubbers import Scrubber, combine_scrubbers + +__all__ = [ + "TokenScrubber", + "Scrubber", + "combine_scrubbers", + "verify", +] + + +class TokenScrubber(Scrubber): + def __init__(self, tokens: dict[str, str]): + self._tokens = tokens + + def __call__(self, data: str) -> str: + result = data + for token, search in self._tokens.items(): + result = result.replace(search, "{" + token + "}") + return result + + +def verify( + data: Any, # noqa: ANN401 + *, + options: approvaltests.Options | None = None, + scrubber: Scrubber | None = None, + **kwargs: Any +) -> None: + options = options or approvaltests.Options() + if scrubber is not None: + options = options.add_scrubber(scrubber) + kwargs.setdefault("encoding", "utf-8") + approvaltests.verify( + data=data, + options=options, + **kwargs, + )