Skip to content

Commit

Permalink
fix(logging): ensure log files get opened in UTF-8 encoding
Browse files Browse the repository at this point in the history
whilst almost all Python APIs assume UTF-8, this one appears to have retained backwards compatibility and might end up creating the file in a locale encoding instead

fix(testing): fix unit tests on Windows, where an interactive console was being attempted to be creating due to an interaction between prompt_toolkit and pytest automatic stdout/in/err capture

fix(testing): fix approval test files being written with non-utf8 locale on windows

fix(testing): test 3.10 x {linux, windows} to get more relevant coverage given the limited build-minutes situation currently

security: update packages to address pip-audit discovered vulnerability
  • Loading branch information
achidlow committed Dec 8, 2022
1 parent dbe5fc0 commit bc666fe
Show file tree
Hide file tree
Showing 12 changed files with 90 additions and 44 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build-python.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 13 additions & 13 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/algokit/core/log_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
3 changes: 2 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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


Expand Down
2 changes: 1 addition & 1 deletion tests/goal/test_goal.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down
48 changes: 26 additions & 22 deletions tests/init/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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),
)


Expand All @@ -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):
Expand All @@ -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(
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand Down Expand Up @@ -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),
)


Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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())
2 changes: 1 addition & 1 deletion tests/sandbox/test_sandbox_console.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down
2 changes: 1 addition & 1 deletion tests/sandbox/test_sandbox_reset.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down
2 changes: 1 addition & 1 deletion tests/sandbox/test_sandbox_start.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down
2 changes: 1 addition & 1 deletion tests/sandbox/test_sandbox_status.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down
2 changes: 1 addition & 1 deletion tests/sandbox/test_sandbox_stop.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down
40 changes: 40 additions & 0 deletions tests/utils/approvals.py
Original file line number Diff line number Diff line change
@@ -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,
)

0 comments on commit bc666fe

Please sign in to comment.