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 d47e2b1  Improve the transcript test format
d47e2b1 is described below

commit d47e2b1685aa5a38ea4de49f7c4a4319d7d69e61
Author: Sean B. Palmer <[email protected]>
AuthorDate: Thu Jul 10 19:55:12 2025 +0100

    Improve the transcript test format
---
 pyproject.toml          |  4 ++--
 src/atrclient/client.py | 14 ++++++++++++++
 tests/cli_config.t      | 16 ++++++++--------
 tests/cli_version.t     |  2 +-
 tests/test_all.py       | 39 ++++++++++++++++++++++++++++++++-------
 uv.lock                 |  4 ++--
 6 files changed, 59 insertions(+), 20 deletions(-)

diff --git a/pyproject.toml b/pyproject.toml
index 597ead7..540e8c2 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -11,7 +11,7 @@ build-backend = "hatchling.build"
 
 [project]
 name            = "apache-trusted-releases"
-version         = "0.20250710.1806"
+version         = "0.20250710.1854"
 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-10T18:06:00Z"
+exclude-newer = "2025-07-10T18:54:00Z"
diff --git a/src/atrclient/client.py b/src/atrclient/client.py
index 9f13868..f996750 100755
--- a/src/atrclient/client.py
+++ b/src/atrclient/client.py
@@ -221,6 +221,20 @@ def app_dev_stamp() -> None:
         print("Updated tests/cli_version.t.")
 
 
+@APP_DEV.command(name="token", help="Generate a random alphabetical token.")
+def app_dev_token() -> None:
+    import secrets
+
+    label = ""
+    # int(math.log2(26 ** 16)) == 75
+    while len(label) < 16:
+        i = secrets.randbits(5)
+        # Do not use modulo here
+        if i < 26:
+            label += chr(i + 97)
+    print(label)
+
+
 @APP_DRAFT.command(name="delete", help="Delete a draft release.")
 def app_draft_delete(project: str, version: str, /) -> None:
     jwt_value = config_jwt_usable()
diff --git a/tests/cli_config.t b/tests/cli_config.t
index 86cfbbd..8b04b37 100644
--- a/tests/cli_config.t
+++ b/tests/cli_config.t
@@ -1,13 +1,13 @@
 $ atr dev env
-ATR_CLIENT_CONFIG_PATH="<!CONFIG_PATH!>"
-<!...!>
+ATR_CLIENT_CONFIG_PATH="<.skip.>"
+<.etc.>
 
 ! atr config file
-<!stderr!>
+<.stderr.>
 atr: error: No configuration file found.
 
 $ atr config path
-/<!ROOT_REL_PATH!>
+/<.skip.>
 
 $ atr set asf.uid example
 Set asf.uid to "example".
@@ -17,13 +17,13 @@ asf:
   uid: example
 
 ! atr drop --path asf.uid
-╭─ Error 
──────────────────────────────────────────────────────────────────────╮
-│ Unknown option: "--path".                                                    
│
-╰──────────────────────────────────────────────────────────────────────────────╯
+<.skip.>
+<.skip.>Unknown option: "--path"<.skip.>
+<.etc.>
 
 $ atr drop asf.uid
 Removed asf.uid.
 
 ! atr config file
-<!stderr!>
+<.stderr.>
 atr: error: No configuration file found.
diff --git a/tests/cli_version.t b/tests/cli_version.t
index de10014..2c98271 100644
--- a/tests/cli_version.t
+++ b/tests/cli_version.t
@@ -1,2 +1,2 @@
 $ atr --version
-0.20250710.1806
+0.20<.skip.>.<.skip.>
diff --git a/tests/test_all.py b/tests/test_all.py
index b9af31e..57c0607 100755
--- a/tests/test_all.py
+++ b/tests/test_all.py
@@ -202,38 +202,63 @@ def test_cli_transcripts(
     script_runner: pytest_console_scripts.ScriptRunner,
     fixture_config_env: pathlib.Path,
 ) -> None:
-    r_variable = re.compile(r"<!([A-Z_]+)!>")
+    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("! "):
                 expected_code = 0 if line.startswith("$ ") else 1
                 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)
-                assert result.returncode == expected_code
+                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.append("<.stderr.>")
                     actual_output.extend(result.stderr.splitlines())
             elif actual_output:
-                if line == "<!...!>":
+                if line == "<.etc.>":
                     actual_output[:] = []
                     continue
                 actual_output_line = actual_output.pop(0)
-                # Replace variables with (?P<variable>.*?)
-                line_pattern = r_variable.sub(r"(?P<\1>.*?)", line)
-                if line_pattern != line:
+                # 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:
diff --git a/uv.lock b/uv.lock
index 20dd570..439b332 100644
--- a/uv.lock
+++ b/uv.lock
@@ -2,7 +2,7 @@ version = 1
 requires-python = ">=3.13"
 
 [options]
-exclude-newer = "2025-07-10T18:06:00Z"
+exclude-newer = "2025-07-10T18:54:00Z"
 
 [[package]]
 name = "aiohappyeyeballs"
@@ -74,7 +74,7 @@ wheels = [
 
 [[package]]
 name = "apache-trusted-releases"
-version = "0.20250710.1806"
+version = "0.20250710.1854"
 source = { editable = "." }
 dependencies = [
     { name = "aiohttp" },


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

Reply via email to