Skip to content

Commit eb667ec

Browse files
test(presets): cover catalog tags and resolve escapes
Addresses Copilot review feedback on #3826: two escapes added by the previous commit had no regression assertion, so they could be reverted with the suite still green. - `test_info_escapes_catalog_markup` asserted every catalog field except `tags`; the new tag assertion only exercised an installed preset. Assert the rendered tags join in the catalog branch too. - The escapes on `preset resolve`'s resolved path, layer source, and composition-error message were untested. Add three cases patching `PresetResolver` to feed markup through the top-layer line, the no-layer `resolve_with_source` fallback, and a markup-bearing `resolve_content` exception. Test-the-test: with `_commands.py` reverted to the pre-fix revision, 9 of the 10 markup tests fail (was 5); with the fix applied all 10 pass. A closing tag cannot be embedded in the mocked path — `Path` treats the `/` as a separator — so the path assertion uses an opening tag for the swallowing case and the unbalanced tag rides on the adjacent `source` field on the same line. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Assisted-by: Claude Code (model: claude-opus-5, under direct human supervision)
1 parent b5c1f85 commit eb667ec

1 file changed

Lines changed: 80 additions & 0 deletions

File tree

‎tests/test_presets.py‎

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12170,6 +12170,8 @@ def test_info_escapes_catalog_markup(self, project_dir):
1217012170
):
1217112171
value = self.MARKUP_PRESET[field]
1217212172
assert value in output
12173+
# Tags are joined into a single line, so assert on the rendered join.
12174+
assert ", ".join(self.MARKUP_PRESET["tags"]) in output
1217312175

1217412176

1217512177
class TestInstalledPresetRichMarkup:
@@ -12287,6 +12289,84 @@ def test_resolve_escapes_template_name(self, project_dir):
1228712289
assert result.exit_code == 0, (result.output, result.exception)
1228812290
assert "no[/red]such" in strip_ansi(result.output)
1228912291

12292+
def test_resolve_escapes_layer_path_and_source(self, project_dir):
12293+
"""The top-layer path/source lines must render markup literally.
12294+
12295+
A preset can be installed from any directory, so the resolved path can
12296+
contain ``[...]``; the layer source carries the pack id and version.
12297+
"""
12298+
from unittest.mock import patch
12299+
from specify_cli.presets import PresetResolver
12300+
12301+
# A closing tag cannot live inside a path segment: `Path` treats its
12302+
# `/` as a separator on POSIX and rewrites it to `\` on Windows. The
12303+
# opening tag covers the swallowing case for the path; the unbalanced
12304+
# closing tag rides on `source`, which is a plain string.
12305+
layer = {
12306+
"path": Path("/tmp/[red]dir/spec-template.md"),
12307+
"source": "pack [/red] v1.0.0",
12308+
"strategy": "replace",
12309+
}
12310+
with patch.object(PresetResolver, "collect_all_layers", return_value=[layer]):
12311+
result = self._invoke(project_dir, ["preset", "resolve", "spec-template"])
12312+
12313+
assert result.exit_code == 0, (result.output, result.exception)
12314+
output = " ".join(strip_ansi(result.output).split())
12315+
assert "[red]dir" in output, output
12316+
assert "pack [/red] v1.0.0" in output, output
12317+
12318+
def test_resolve_escapes_fallback_path_and_source(self, project_dir):
12319+
"""The no-layer fallback branch must escape ``resolve_with_source`` output."""
12320+
from unittest.mock import patch
12321+
from specify_cli.presets import PresetResolver
12322+
12323+
with patch.object(
12324+
PresetResolver, "collect_all_layers", return_value=[]
12325+
), patch.object(
12326+
PresetResolver,
12327+
"resolve_with_source",
12328+
return_value={
12329+
"path": "/tmp/[blue]fallback[/blue]/spec-template.md",
12330+
"source": "fallback [/red] source",
12331+
},
12332+
):
12333+
result = self._invoke(project_dir, ["preset", "resolve", "spec-template"])
12334+
12335+
assert result.exit_code == 0, (result.output, result.exception)
12336+
output = " ".join(strip_ansi(result.output).split())
12337+
assert "[blue]fallback[/blue]" in output, output
12338+
assert "fallback [/red] source" in output, output
12339+
12340+
def test_resolve_escapes_composition_error(self, project_dir):
12341+
"""A composition exception message must not be parsed as markup."""
12342+
from unittest.mock import patch
12343+
from specify_cli.presets import PresetResolver
12344+
12345+
layers = [
12346+
{
12347+
"path": Path("/tmp/top/spec-template.md"),
12348+
"source": "top-pack v1.0.0",
12349+
"strategy": "append",
12350+
},
12351+
{
12352+
"path": Path("/tmp/base/spec-template.md"),
12353+
"source": "base-pack v1.0.0",
12354+
"strategy": "append",
12355+
},
12356+
]
12357+
with patch.object(
12358+
PresetResolver, "collect_all_layers", return_value=layers
12359+
), patch.object(
12360+
PresetResolver,
12361+
"resolve_content",
12362+
side_effect=RuntimeError("compose failed: [/red] bad layer"),
12363+
):
12364+
result = self._invoke(project_dir, ["preset", "resolve", "spec-template"])
12365+
12366+
assert result.exit_code == 0, (result.output, result.exception)
12367+
output = " ".join(strip_ansi(result.output).split())
12368+
assert "compose failed: [/red] bad layer" in output, output
12369+
1229012370
def test_resolve_renders_composition_strategy_labels(self, temp_dir, project_dir):
1229112371
"""The composition chain's ``[<strategy>]`` label must not be eaten as a tag."""
1229212372
self._install(temp_dir, project_dir, strategy="replace",

0 commit comments

Comments
 (0)