From b9bd450f91b02e3ae258cc9720a256fe9a4ec4dd Mon Sep 17 00:00:00 2001 From: eeshsaxena Date: Tue, 11 Aug 2026 18:11:54 +0530 Subject: [PATCH] Fix TestSpec serialization dropping test ids that contain quotes to_fail_to_pass_str and to_pass_to_pass_str built the list string by hand with single quotes around each test id. A test id containing a quote (for example a parametrized pytest id like test_x[can't]) produced invalid Python, so when load_task_from_directory parses instance_info.txt and evaluates the string it raises, falls into the except branch, and silently replaces the whole list with []. The task then loses its fail_to_pass/pass_to_pass spec. Use repr(list(...)) so any test id is emitted as a valid literal that round trips. Output is unchanged for ids without quotes. Adds tests for the round trip (common, quoted, and empty cases) plus a pytest dev dependency and config, since the repo had no test setup yet. --- pyproject.toml | 9 +++++++++ src/anvil/wizard/models.py | 8 ++++++-- tests/test_wizard_models.py | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 tests/test_wizard_models.py diff --git a/pyproject.toml b/pyproject.toml index 04e371f..c09681c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,5 +22,14 @@ dependencies = [ [project.scripts] anvil = "anvil.cli:app" +[dependency-groups] +dev = [ + "pytest>=8.0", +] + [tool.uv] package = true + +[tool.pytest.ini_options] +pythonpath = ["src"] +testpaths = ["tests"] diff --git a/src/anvil/wizard/models.py b/src/anvil/wizard/models.py index 476b178..a6c516b 100644 --- a/src/anvil/wizard/models.py +++ b/src/anvil/wizard/models.py @@ -15,11 +15,15 @@ class TestSpec: def to_fail_to_pass_str(self) -> str: """Format fail_to_pass as a string list for instance_info.txt.""" - return "[" + ", ".join(f"'{t}'" for t in self.fail_to_pass) + "]" + # Use repr so test IDs containing quotes (e.g. parametrized pytest ids + # like "test_x[can't]") stay valid Python literals that round-trip + # through the instance_info.txt parser. Manual quoting produced invalid + # syntax and silently dropped the whole list on parse. + return repr(list(self.fail_to_pass)) def to_pass_to_pass_str(self) -> str: """Format pass_to_pass as a string list for instance_info.txt.""" - return "[" + ", ".join(f"'{t}'" for t in self.pass_to_pass) + "]" + return repr(list(self.pass_to_pass)) @dataclass diff --git a/tests/test_wizard_models.py b/tests/test_wizard_models.py new file mode 100644 index 0000000..82a77a1 --- /dev/null +++ b/tests/test_wizard_models.py @@ -0,0 +1,34 @@ +"""Tests for anvil.wizard.models.""" + +from __future__ import annotations + +import ast + +from anvil.wizard import models + + +def test_test_spec_str_roundtrip_common_case(): + spec = models.TestSpec( + fail_to_pass=["tests/test_a.py::test_one"], + pass_to_pass=["tests/test_a.py::test_two", "tests/test_b.py::test_three"], + ) + assert ast.literal_eval(spec.to_fail_to_pass_str()) == spec.fail_to_pass + assert ast.literal_eval(spec.to_pass_to_pass_str()) == spec.pass_to_pass + + +def test_test_spec_str_roundtrip_with_quotes_in_test_id(): + # Parametrized pytest ids can contain apostrophes, e.g. test_x[can't]. + # These used to produce invalid Python and get silently dropped when the + # instance_info.txt parser evaluated the string. + fail = ["tests/test_x.py::test_quote[can't]"] + pass_ = ['tests/test_y.py::test_dquote[say "hi"]'] + spec = models.TestSpec(fail_to_pass=fail, pass_to_pass=pass_) + + assert ast.literal_eval(spec.to_fail_to_pass_str()) == fail + assert ast.literal_eval(spec.to_pass_to_pass_str()) == pass_ + + +def test_test_spec_str_empty(): + spec = models.TestSpec() + assert ast.literal_eval(spec.to_fail_to_pass_str()) == [] + assert ast.literal_eval(spec.to_pass_to_pass_str()) == []