This is an automated email from the ASF dual-hosted git repository.

sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-releases-client.git


The following commit(s) were added to refs/heads/main by this push:
     new 67656e2  Add comment syntax to test transcripts
67656e2 is described below

commit 67656e28d08864074bf667a8230e8e4155343778
Author: Sean B. Palmer <[email protected]>
AuthorDate: Fri Jul 11 14:21:21 2025 +0100

    Add comment syntax to test transcripts
---
 pyproject.toml       |   4 +-
 tests/cli_workflow.t |   1 +
 tests/test_all.py    | 192 +++++++++++++++++++++++++++++----------------------
 uv.lock              |   4 +-
 4 files changed, 114 insertions(+), 87 deletions(-)

diff --git a/pyproject.toml b/pyproject.toml
index e4404db..5437fca 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -11,7 +11,7 @@ build-backend = "hatchling.build"
 
 [project]
 name            = "apache-trusted-releases"
-version         = "0.20250711.1305"
+version         = "0.20250711.1320"
 description     = "ATR CLI and Python API"
 readme          = "README.md"
 requires-python = ">=3.13"
@@ -48,4 +48,4 @@ atr = "atrclient.client:main"
 packages = ["src/atrclient"]
 
 [tool.uv]
-exclude-newer = "2025-07-11T13:05:00Z"
+exclude-newer = "2025-07-11T13:20:00Z"
diff --git a/tests/cli_workflow.t b/tests/cli_workflow.t
index 48cc61c..1fd0375 100644
--- a/tests/cli_workflow.t
+++ b/tests/cli_workflow.t
@@ -13,6 +13,7 @@ $ atr dev pat
 $ atr set tokens.pat <!pat!>
 Set tokens.pat to "<!pat!>".
 
+<# Reset any existing draft, ignoring errors. #>
 * atr draft delete tooling-test-example 0.3+cli
 <.etc.>
 
diff --git a/tests/test_all.py b/tests/test_all.py
index 50523bc..a06c32a 100755
--- a/tests/test_all.py
+++ b/tests/test_all.py
@@ -24,12 +24,18 @@ import shlex
 
 import atrclient.client as client
 import pathlib
-from typing import Any
+from typing import Any, Final
 
 import aioresponses
 import pytest
 import pytest_console_scripts
 
+REGEX_CAPTURE: Final[re.Pattern[str]] = re.compile(
+    r"<\?([A-Za-z_]+)\?>|<.(skip).>|(.+?)"
+)
+REGEX_COMMENT: Final[re.Pattern[str]] = re.compile(r"<#.*?#>")
+REGEX_USE: Final[re.Pattern[str]] = re.compile(r"<!([A-Za-z_]+)!>")
+
 
 def decorator_transcript_file_paths() -> list[pathlib.Path]:
     parent = pathlib.Path(__file__).parent
@@ -202,88 +208,7 @@ def test_cli_transcripts(
     script_runner: pytest_console_scripts.ScriptRunner,
     fixture_config_env: pathlib.Path,
 ) -> None:
-    captures = {}
-    r_capture = re.compile(r"<\?([A-Za-z_]+)\?>|<.(skip).>|(.+?)")
-    r_use = re.compile(r"<!([A-Za-z_]+)!>")
-    env = os.environ.copy()
-    transcript_config_path = fixture_config_env
-
-    # transcript_config_path = 
fixture_config_env.with_suffix(f".{transcript_path.name}")
-    # if transcript_config_path.exists():
-    #     pytest.fail(f"Transcript config file already exists: 
{transcript_config_path}")
-    def substitute_uses(captures: dict[str, str], line: str) -> str:
-        return r_use.sub(lambda m: captures[m.group(1)], line)
-
-    env["ATR_CLIENT_CONFIG_PATH"] = str(transcript_config_path)
-    with open(transcript_path, "r", encoding="utf-8") as f:
-        actual_output = []
-        for line in f:
-            line = line.rstrip("\n")
-            if captures:
-                line = substitute_uses(captures, line)
-            if line.startswith("$ ") or line.startswith("! ") or 
line.startswith("* "):
-                match line[:1]:
-                    case "$":
-                        expected_code = 0
-                    case "!":
-                        expected_code = 1
-                    case "*":
-                        expected_code = None
-                    case _:
-                        pytest.fail(f"Unknown line prefix: {line[:1]!r}")
-                        return
-                command = line[2:]
-                if not command.startswith("atr"):
-                    pytest.fail(f"Command does not start with 'atr': 
{command}")
-                    return
-                print(f"Running: {command}")
-                result = script_runner.run(shlex.split(command), env=env)
-                if expected_code is not None:
-                    assert result.returncode == expected_code, (
-                        f"Command {command!r} returned {result.returncode}"
-                    )
-                actual_output[:] = result.stdout.splitlines()
-                if result.stderr:
-                    actual_output.append("<.stderr.>")
-                    actual_output.extend(result.stderr.splitlines())
-            elif actual_output:
-                if line == "<.etc.>":
-                    actual_output[:] = []
-                    continue
-                actual_output_line = actual_output.pop(0)
-                # Replace captures with (?P<name>.*?)
-                use_regex = False
-
-                def capture_sub(m: re.Match[str]) -> str:
-                    nonlocal use_regex
-                    if m.group(1):
-                        use_regex = True
-                        return f"(?P<{m.group(1)}>.*?)"
-                    if m.group(2) == "skip":
-                        use_regex = True
-                        return "(.*?)"
-                    return re.escape(m.group(3))
-
-                line_pattern = r"^" + r_capture.sub(capture_sub, line) + r"$"
-                if use_regex:
-                    success = re.match(line_pattern, actual_output_line)
-                    if success:
-                        captures.update(success.groupdict())
-                else:
-                    success = actual_output_line == line
-                if not success:
-                    # TODO: Improve this
-                    got = f"{actual_output_line!r}"
-                    if actual_output:
-                        got += f", {actual_output[:10]}"
-                        if len(actual_output) > 10:
-                            got += "..."
-                    pytest.fail(f"Expected {line!r} but got {got}")
-                    return
-            elif line:
-                pytest.fail(f"Unexpected line: {line!r}")
-                return
-        assert not actual_output
+    return transcript_capture(transcript_path, script_runner, 
fixture_config_env)
 
 
 def test_config_set_get_roundtrip() -> None:
@@ -329,3 +254,104 @@ async def test_web_fetch_failure() -> None:
         mock.post("https://error.invalid";, status=500, body="error")
         await client.web_fetch("https://error.invalid";, "uid", "pat", 
verify_ssl=False)
     assert (len(mock.requests)) == 1
+
+
+def transcript_capture(
+    transcript_path: pathlib.Path,
+    script_runner: pytest_console_scripts.ScriptRunner,
+    transcript_config_path: pathlib.Path,
+) -> None:
+    captures = {}
+    actual_output = []
+
+    env = os.environ.copy()
+
+    env["ATR_CLIENT_CONFIG_PATH"] = str(transcript_config_path)
+    with open(transcript_path, "r", encoding="utf-8") as f:
+        for line in f:
+            line = line.rstrip("\n")
+            if captures:
+                line = REGEX_USE.sub(lambda m: captures[m.group(1)], line)
+            line = REGEX_COMMENT.sub("", line)
+            if line.startswith("$ ") or line.startswith("! ") or 
line.startswith("* "):
+                actual_output = transcript_execute(
+                    actual_output, line, script_runner, env
+                )
+            elif actual_output:
+                captures, actual_output = transcript_match(
+                    captures, actual_output, line
+                )
+            elif line:
+                pytest.fail(f"Unexpected line: {line!r}")
+        assert not actual_output
+
+
+def transcript_execute(
+    actual_output: list[str],
+    line: str,
+    script_runner: pytest_console_scripts.ScriptRunner,
+    env: dict[str, str],
+) -> list[str]:
+    match line[:1]:
+        case "$":
+            expected_code = 0
+        case "!":
+            expected_code = 1
+        case "*":
+            expected_code = None
+        case _:
+            pytest.fail(f"Unknown line prefix: {line[:1]!r}")
+    command = line[2:]
+    if not command.startswith("atr"):
+        pytest.fail(f"Command does not start with 'atr': {command}")
+    print(f"Running: {command}")
+    result = script_runner.run(shlex.split(command), env=env)
+    if expected_code is not None:
+        assert result.returncode == expected_code, (
+            f"Command {command!r} returned {result.returncode}"
+        )
+    actual_output[:] = result.stdout.splitlines()
+    if result.stderr:
+        actual_output.append("<.stderr.>")
+        actual_output.extend(result.stderr.splitlines())
+    return actual_output
+
+
+def transcript_match(
+    captures: dict[str, str], actual_output: list[str], line: str
+) -> tuple[dict[str, str], list[str]]:
+    if line == "<.etc.>":
+        actual_output[:] = []
+        return captures, actual_output
+    actual_output_line = actual_output.pop(0)
+
+    # Replace captures with (?P<name>.*?)
+    # Can't lift this, because it uses use_regex
+    use_regex = False
+
+    def substitute(m: re.Match[str]) -> str:
+        nonlocal use_regex
+        if m.group(1):
+            use_regex = True
+            return f"(?P<{m.group(1)}>.*?)"
+        if m.group(2) == "skip":
+            use_regex = True
+            return "(.*?)"
+        return re.escape(m.group(3))
+
+    line_pattern = r"^" + REGEX_CAPTURE.sub(substitute, line) + r"$"
+    if use_regex:
+        success = re.match(line_pattern, actual_output_line)
+        if success:
+            captures.update(success.groupdict())
+    else:
+        success = actual_output_line == line
+    if not success:
+        # TODO: Improve this
+        got = f"{actual_output_line!r}"
+        if actual_output:
+            got += f", {actual_output[:10]}"
+            if len(actual_output) > 10:
+                got += "..."
+        pytest.fail(f"Expected {line!r} but got {got}")
+    return captures, actual_output
diff --git a/uv.lock b/uv.lock
index d8823f3..0b6858e 100644
--- a/uv.lock
+++ b/uv.lock
@@ -2,7 +2,7 @@ version = 1
 requires-python = ">=3.13"
 
 [options]
-exclude-newer = "2025-07-11T13:05:00Z"
+exclude-newer = "2025-07-11T13:20:00Z"
 
 [[package]]
 name = "aiohappyeyeballs"
@@ -74,7 +74,7 @@ wheels = [
 
 [[package]]
 name = "apache-trusted-releases"
-version = "0.20250711.1305"
+version = "0.20250711.1320"
 source = { editable = "." }
 dependencies = [
     { name = "aiohttp" },


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to