Skip to content

Commit fa61962

Browse files
committed
test(golden): normalize() — deterministic YAML rendering
Strips volatile Metadata.SamTransformMetrics, sort_keys=True, single trailing newline. Used by both the harness and the regenerator so they cannot diverge on serialization.
1 parent e0280c2 commit fa61962

2 files changed

Lines changed: 136 additions & 0 deletions

File tree

tests/golden/normalize.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""Deterministic YAML rendering for golden-template comparison.
2+
3+
The harness and update_goldens.py both call normalize() so the bytes
4+
written to disk match the bytes the test compares. Determinism rules:
5+
6+
- Resources / Mappings keys sorted alphabetically.
7+
- Metadata.SamTransformMetrics dropped (varies by run).
8+
- Empty Metadata block dropped.
9+
- yaml.safe_dump with sort_keys=True, default_flow_style=False.
10+
- Single trailing newline.
11+
"""
12+
13+
from __future__ import annotations
14+
15+
from typing import Any, Dict
16+
17+
import yaml
18+
19+
_VOLATILE_METADATA_KEYS = frozenset({"SamTransformMetrics"})
20+
21+
22+
def _filter_metadata(template: Dict[str, Any]) -> None:
23+
metadata = template.get("Metadata")
24+
if not isinstance(metadata, dict):
25+
return
26+
for key in _VOLATILE_METADATA_KEYS:
27+
metadata.pop(key, None)
28+
if not metadata:
29+
template.pop("Metadata", None)
30+
31+
32+
def normalize(template: Dict[str, Any]) -> str:
33+
"""Render template to deterministic YAML string."""
34+
# Mutate a copy so we don't surprise the caller.
35+
template = {k: v for k, v in template.items()}
36+
if isinstance(template.get("Metadata"), dict):
37+
template["Metadata"] = dict(template["Metadata"])
38+
_filter_metadata(template)
39+
40+
rendered = yaml.safe_dump(
41+
template,
42+
sort_keys=True,
43+
default_flow_style=False,
44+
width=10**9, # don't wrap long strings
45+
allow_unicode=True,
46+
)
47+
if not rendered.endswith("\n"):
48+
rendered += "\n"
49+
return rendered

tests/golden/test_normalize.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
"""Unit tests for normalize() — deterministic YAML serialization."""
2+
3+
import pytest
4+
5+
from tests.golden.normalize import normalize
6+
7+
8+
def test_normalize_produces_trailing_newline():
9+
out = normalize({"Resources": {}})
10+
assert out.endswith("\n")
11+
assert not out.endswith("\n\n")
12+
13+
14+
def test_normalize_sorts_resources_keys():
15+
template = {
16+
"Resources": {
17+
"Zeta": {"Type": "AWS::Lambda::Function"},
18+
"Alpha": {"Type": "AWS::Lambda::Function"},
19+
},
20+
}
21+
out = normalize(template)
22+
assert out.index("Alpha") < out.index("Zeta")
23+
24+
25+
def test_normalize_sorts_mappings_keys():
26+
template = {
27+
"Mappings": {
28+
"Z": {"k": {"v": "1"}},
29+
"A": {"k": {"v": "1"}},
30+
},
31+
}
32+
out = normalize(template)
33+
assert out.index("A:") < out.index("Z:")
34+
35+
36+
def test_normalize_drops_sam_transform_metrics():
37+
template = {
38+
"Metadata": {
39+
"SamTransformMetrics": {"foo": "bar"},
40+
"Other": "keep",
41+
},
42+
"Resources": {},
43+
}
44+
out = normalize(template)
45+
assert "SamTransformMetrics" not in out
46+
assert "Other" in out
47+
48+
49+
def test_normalize_drops_metadata_block_if_empty_after_filter():
50+
template = {
51+
"Metadata": {"SamTransformMetrics": {"foo": "bar"}},
52+
"Resources": {},
53+
}
54+
out = normalize(template)
55+
assert "Metadata" not in out
56+
57+
58+
def test_normalize_is_idempotent():
59+
template = {"Resources": {"A": {"Type": "T"}}}
60+
once = normalize(template)
61+
import yaml
62+
twice = normalize(yaml.safe_load(once))
63+
assert once == twice
64+
65+
66+
def test_normalize_preserves_intrinsic_function_dicts():
67+
template = {
68+
"Resources": {
69+
"F": {
70+
"Type": "AWS::Lambda::Function",
71+
"Properties": {
72+
"Code": {"ZipFile": {"Fn::Sub": "x-${AWS::Region}"}},
73+
},
74+
}
75+
}
76+
}
77+
out = normalize(template)
78+
assert "Fn::Sub" in out
79+
assert "x-${AWS::Region}" in out
80+
81+
82+
def test_normalize_uses_block_style_not_flow_style():
83+
template = {"Resources": {"A": {"Type": "T", "Properties": {"k": "v"}}}}
84+
out = normalize(template)
85+
# block style uses indentation, not braces
86+
assert "{" not in out
87+
assert "}" not in out

0 commit comments

Comments
 (0)