Setting the file. One moment.
Test Validate Heroku Migration Report Cost · Heroku To AWS · aws/agent-toolkit-for-aws · Skills Docs
ContentsBack to the top of the page 28.3
Clarify
81
Creating Amazon Aurora Db Cluster With Instances
104
Routing Traffic With Route53 And CloudFront
Resilience Program Design
Creating API Gateway Stage
— line 133
This file
Number 28.47
Position 47 of 58
Type Python
Size 17 KB
Lines 387 scripts/ test_validate_heroku_migration_report_cost.py
Python · 387 lines · 17 KB
).resolve().parent
/
"validate-heroku-migration-report.py"
13
14 GOOD = """<!DOCTYPE html>
15 <html lang="en"><body><div class="report">
16 <section id="decision-summary"><p class="verdict-headline">Go</p></section>
17 <section id="exec-costs"><p>Balanced <span data-cost-key="aws_monthly_balanced">$112/mo</span></p></section>
18 <section id="cost-optimization"><p>No 1-year/3-year commitment product applies to this architecture.</p></section>
19 <section id="next-steps"><ol><li>See MIGRATION_GUIDE.md</li></ol></section>
20 <footer>draft for review</footer>
21 </div></body></html>
22 """
23
24
25 def run (html: str , migration_dir: Path | None , mode: str | None = None ) -> tuple[ int , str ]:
26 with tempfile.TemporaryDirectory() as tmp:
27 report = Path(tmp) / "migration-report.html"
28 report.write_text(html, encoding = "utf-8" )
29 cmd = [sys.executable, str ( SCRIPT ), str (report)]
30 if migration_dir is not None :
31 cmd += [ "--migration-dir" , str (migration_dir)]
32 if mode is not None :
33 cmd += [ "--mode" , mode]
34 result = subprocess.run(cmd, capture_output = True , text = True ) # nosec B603
35 return result.returncode, result.stdout + result.stderr
36
37
38 def _est_dir (tmp: str , balanced: int ) -> Path:
39 d = Path(tmp)
40 (d / "estimation-infra.json" ).write_text(
41 json.dumps({ "projected_costs" : { "aws_monthly_balanced" : balanced}}),
42 encoding = "utf-8" ,
43 )
44 return d
45
46
47 def test_cost_figure_match_passes () -> None :
48 with tempfile.TemporaryDirectory() as tmp:
49 code, out = run( GOOD , migration_dir = _est_dir(tmp, 112 ))
50 assert code == 0 , out
51 assert "cost figure mismatch" not in out
52
53
54 def test_decision_report_checks_cost_figures () -> None :
55 html = GOOD .replace( 'id="next-steps"' , 'id="decision-cta"' )
56 with tempfile.TemporaryDirectory() as tmp:
57 migration_dir = _est_dir(tmp, 112 )
58 code, out = run(html, migration_dir, mode = "decision" )
59 assert code == 0 , out
60 code, out = run(html.replace( "$112/mo" , "$999/mo" ), migration_dir, mode = "decision" )
61 assert code == 1 , out
62 assert "cost figure mismatch" in out
63
64
65 def test_cost_figure_mismatch_fails () -> None :
66 with tempfile.TemporaryDirectory() as tmp:
67 code, out = run( GOOD , migration_dir = _est_dir(tmp, 999 ))
68 assert code == 1 , out
69 assert "cost figure mismatch" in out
70 assert "aws_monthly_balanced" in out
71
72
73 def test_cost_figure_skipped_without_estimation () -> None :
74 # No --migration-dir -> no estimation-infra.json -> the cost check is skipped entirely,
75 # so the otherwise-valid report passes.
76 code, out = run( GOOD , migration_dir = None )
77 assert code == 0 , out
78 assert "cost figure mismatch" not in out
79 assert "missing data-cost-key" not in out
80
81
82 def test_missing_required_balanced_anchor_fails () -> None :
83 # The core P1-C guarantee: an un-anchored balanced figure must FAIL, not pass —
84 # otherwise a wrong un-anchored number reaches the reader unchecked.
85 html = GOOD .replace(
86 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
87 "$999/mo" ,
88 )
89 with tempfile.TemporaryDirectory() as tmp:
90 code, out = run(html, migration_dir = _est_dir(tmp, 112 ))
91 assert code == 1 , out
92 assert 'missing data-cost-key="aws_monthly_balanced"' in out
93
94
95 def test_nested_markup_reads () -> None :
96 html = GOOD .replace(
97 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
98 '<span data-cost-key="aws_monthly_balanced"><strong>$112/mo</strong></span>' ,
99 )
100 with tempfile.TemporaryDirectory() as tmp:
101 code, out = run(html, migration_dir = _est_dir(tmp, 112 ))
102 assert code == 0 , out
103 assert "cost figure mismatch" not in out
104
105
106 def test_nested_section_anchor_is_preserved () -> None :
107 html = GOOD .replace(
108 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
109 '<section data-cost-key="aws_monthly_balanced">$112/mo</section>' ,
110 )
111 with tempfile.TemporaryDirectory() as tmp:
112 migration_dir = _est_dir(tmp, 112 )
113 for mode in ( "full" , "decision" ):
114 report = html if mode == "full" else html.replace( 'id="next-steps"' , 'id="decision-cta"' )
115 code, out = run(report, migration_dir, mode = mode)
116 assert code == 0 , out
117
118
119 def test_hidden_nested_section_does_not_supply_required_anchor () -> None :
120 html = GOOD .replace(
121 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
122 '<section hidden><span data-cost-key="aws_monthly_balanced">$112/mo</span></section>$999/mo' ,
123 )
124 with tempfile.TemporaryDirectory() as tmp:
125 migration_dir = _est_dir(tmp, 112 )
126 for mode in ( "full" , "decision" ):
127 report = html if mode == "full" else html.replace( 'id="next-steps"' , 'id="decision-cta"' )
128 code, out = run(report, migration_dir, mode = mode)
129 assert code == 1 , out
130 assert 'missing data-cost-key="aws_monthly_balanced"' in out
131
132
133 def test_non_numeric_json_value_fails_not_crash () -> None :
134 with tempfile.TemporaryDirectory() as tmp:
135 d = Path(tmp)
136 (d / "estimation-infra.json" ).write_text(
137 json.dumps({ "projected_costs" : { "aws_monthly_balanced" : "$112" }}),
138 encoding = "utf-8" ,
139 )
140 code, out = run( GOOD , migration_dir = d)
141 assert code == 1 , out
142 assert "not a numeric dollar amount" in out
143
144
145 def test_cost_anchor_inside_html_comment_does_not_satisfy_requirement () -> None :
146 # A commented-out anchor is not rendered content — it must not satisfy the
147 # required-anchor check, even if the (invisible) figure would have matched.
148 html = GOOD .replace(
149 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
150 '<!-- <span data-cost-key="aws_monthly_balanced">$112/mo</span> -->$999/mo' ,
151 )
152 with tempfile.TemporaryDirectory() as tmp:
153 code, out = run(html, migration_dir = _est_dir(tmp, 112 ))
154 assert code == 1 , out
155 assert 'missing data-cost-key="aws_monthly_balanced"' in out
156
157
158 def test_cost_anchor_outside_exec_costs_does_not_satisfy_requirement () -> None :
159 # A correct anchor placed in decision-summary (not exec-costs) must not let an
160 # unanchored, wrong figure inside exec-costs itself pass silently.
161 html = GOOD .replace(
162 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
163 "$999/mo" ,
164 ).replace(
165 '<p class="verdict-headline">Go</p>' ,
166 '<p class="verdict-headline">Go</p>'
167 '<p><span data-cost-key="aws_monthly_balanced">$112/mo</span></p>' ,
168 )
169 with tempfile.TemporaryDirectory() as tmp:
170 code, out = run(html, migration_dir = _est_dir(tmp, 112 ))
171 assert code == 1 , out
172 assert 'missing data-cost-key="aws_monthly_balanced" anchor inside' in out
173 assert "exec-costs" in out
174
175
176 def test_cost_anchor_inside_template_does_not_satisfy_requirement () -> None :
177 # <template> content is inert (never rendered) even though the parser still
178 # walks its tags — an anchor placed there must not stand in for the visible
179 # (wrong) figure sitting right next to it.
180 html = GOOD .replace(
181 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
182 '<template><span data-cost-key="aws_monthly_balanced">$112/mo</span></template>$999/mo' ,
183 )
184 with tempfile.TemporaryDirectory() as tmp:
185 code, out = run(html, migration_dir = _est_dir(tmp, 112 ))
186 assert code == 1 , out
187 assert 'missing data-cost-key="aws_monthly_balanced"' in out
188
189
190 def test_split_nested_markup_reads_full_text () -> None :
191 # A figure split across nested markup within the SAME anchored element
192 # (<span data-cost-key="x"><span>$</span>112/mo</span>) must be read as one
193 # value through the anchor's own matching close tag, not truncated at the
194 # first inner </span>.
195 html = GOOD .replace(
196 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
197 '<span data-cost-key="aws_monthly_balanced"><span>$</span>112/mo</span>' ,
198 )
199 with tempfile.TemporaryDirectory() as tmp:
200 code, out = run(html, migration_dir = _est_dir(tmp, 112 ))
201 assert code == 0 , out
202 assert "cost figure mismatch" not in out
203 assert "renders no dollar amount" not in out
204
205
206 def test_trailing_text_outside_inner_tag_is_included () -> None :
207 # Text sitting after a nested child tag, but still inside the anchor's own
208 # close tag, must be included — not dropped at the child's </strong>. Here
209 # the anchor's full rendered text is "$1120", which must NOT be truncated
210 # to "$112" (that would silently accept a wrong ten-times-off figure).
211 html = GOOD .replace(
212 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
213 '<span data-cost-key="aws_monthly_balanced"><strong>$112</strong>0/mo</span>' ,
214 )
215 with tempfile.TemporaryDirectory() as tmp:
216 code, out = run(html, migration_dir = _est_dir(tmp, 112 ))
217 assert code == 1 , out
218 assert "cost figure mismatch" in out
219 assert '"$1120"' in out
220
221
222 # exec-costs deliberately placed LAST among the <section> elements (only <footer>
223 # follows) so a non-greedy `.*?</section>` regex has no LATER </section> to fall
224 # through to — this is what makes the closing-tag-spelling bug observable as a
225 # clean `_section_html(...) is None` skip, rather than the regex silently
226 # matching through to some other section's close tag and returning overrun
227 # (but still exec-costs-containing) content that coincidentally still fails.
228 GOOD_EXEC_COSTS_LAST = """<!DOCTYPE html>
229 <html lang="en"><body><div class="report">
230 <section id="decision-summary"><p class="verdict-headline">Go</p></section>
231 <section id="cost-optimization"><p>No 1-year/3-year commitment product applies to this architecture.</p></section>
232 <section id="next-steps"><ol><li>See MIGRATION_GUIDE.md</li></ol></section>
233 <section id="exec-costs"><p>Balanced <span data-cost-key="aws_monthly_balanced">$112/mo</span></p></section>
234 <footer>draft for review</footer>
235 </div></body></html>
236 """
237
238
239 def test_whitespace_before_exec_costs_closing_angle_bracket_still_gates_required_anchor () -> None :
240 # Regression: _section_html previously matched only the literal string
241 # `</section>`. `</section >` (whitespace before `>`) is equally valid
242 # HTML, but the old regex returned None for it, and the required-anchor
243 # check was gated behind `if exec_costs_html is not None:` — so a
244 # recognized exec-costs section silently skipped the required-anchor
245 # requirement entirely. Remove the Balanced anchor, keep an unanchored
246 # wrong figure, and serialize exec-costs' closing tag with a trailing
247 # space: this must still FAIL.
248 html = GOOD_EXEC_COSTS_LAST .replace(
249 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
250 "$999/mo" ,
251 ).replace(
252 "</section> \n <footer>" ,
253 "</section > \n <footer>" ,
254 )
255 with tempfile.TemporaryDirectory() as tmp:
256 code, out = run(html, migration_dir = _est_dir(tmp, 112 ))
257 assert code == 1 , out
258 assert 'missing data-cost-key="aws_monthly_balanced" anchor inside' in out
259
260
261 def test_newline_before_exec_costs_closing_angle_bracket_still_gates_required_anchor () -> None :
262 # Same bypass, different whitespace: a newline before the closing `>` is
263 # also valid HTML and must not defeat the required-anchor check either.
264 html = GOOD_EXEC_COSTS_LAST .replace(
265 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
266 "$999/mo" ,
267 ).replace(
268 "</section> \n <footer>" ,
269 "</section \n > \n <footer>" ,
270 )
271 with tempfile.TemporaryDirectory() as tmp:
272 code, out = run(html, migration_dir = _est_dir(tmp, 112 ))
273 assert code == 1 , out
274 assert 'missing data-cost-key="aws_monthly_balanced" anchor inside' in out
275
276
277 def test_escaped_anchor_example_inside_section_does_not_satisfy_requirement () -> None :
278 # Regression: _SectionScopeParser decodes character references before
279 # appending text into the returned section HTML. An escaped code example
280 # like `<code><span data-cost-key="x">$112</span></code>`
281 # decodes to the literal text `<span data-cost-key="x">$112</span>` —
282 # reparsing that string then finds a "real" anchor that never actually
283 # existed in the source. Remove the real anchor, leave a visible wrong
284 # figure, and add an escaped example with the correct anchor+value: this
285 # must still FAIL.
286 html = GOOD .replace(
287 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
288 "$999/mo "
289 '<code><span data-cost-key="aws_monthly_balanced">$112/mo</span></code>' ,
290 )
291 with tempfile.TemporaryDirectory() as tmp:
292 code, out = run(html, migration_dir = _est_dir(tmp, 112 ))
293 assert code == 1 , out
294 assert 'missing data-cost-key="aws_monthly_balanced"' in out
295
296
297 # --- Rendered-reality regressions (parity with the GCP validator): inert/hidden
298 # anchors do not count, nested anchors are collected independently, and the display
299 # precision rounds rather than truncates. ---
300
301
302 def test_nested_inner_anchor_is_collected_not_dropped () -> None :
303 # An inner data-cost-key nested in an outer one must be read as its own anchor,
304 # not swallowed by the outer element. Inner $999 != estimate 112 -> FAIL.
305 html = GOOD .replace(
306 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
307 '<span data-cost-key="aws_monthly_balanced">$112/mo '
308 '<strong data-cost-key="aws_monthly_balanced">$999</strong></span>' ,
309 )
310 with tempfile.TemporaryDirectory() as tmp:
311 code, out = run(html, migration_dir = _est_dir(tmp, 112 ))
312 assert code == 1 , out
313 assert "999" in out and "mismatch" in out.lower()
314
315
316 def test_hidden_anchor_does_not_satisfy_requirement () -> None :
317 # A hidden anchor is not rendered; a visible $999 stands instead. estimate 112.
318 html = GOOD .replace(
319 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
320 '<span hidden data-cost-key="aws_monthly_balanced">$112/mo</span>$999' ,
321 )
322 with tempfile.TemporaryDirectory() as tmp:
323 code, out = run(html, migration_dir = _est_dir(tmp, 112 ))
324 assert code == 1 , out
325 assert 'missing data-cost-key="aws_monthly_balanced"' in out
326
327
328 def test_hidden_false_is_still_hidden () -> None :
329 # In HTML `hidden="false"` is still the Hidden state (invalid-value default),
330 # so the anchor is not rendered and must not satisfy the requirement.
331 html = GOOD .replace(
332 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
333 '<span hidden="false" data-cost-key="aws_monthly_balanced">$112/mo</span>$999' ,
334 )
335 with tempfile.TemporaryDirectory() as tmp:
336 code, out = run(html, migration_dir = _est_dir(tmp, 112 ))
337 assert code == 1 , out
338 assert 'missing data-cost-key="aws_monthly_balanced"' in out
339
340
341 def test_script_anchor_does_not_satisfy_requirement () -> None :
342 # An anchor inside an inert <script> subtree is never rendered.
343 html = GOOD .replace(
344 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
345 '<script type="application/json">'
346 '<span data-cost-key="aws_monthly_balanced">$112</span></script>$999' ,
347 )
348 with tempfile.TemporaryDirectory() as tmp:
349 code, out = run(html, migration_dir = _est_dir(tmp, 112 ))
350 assert code == 1 , out
351 assert 'missing data-cost-key="aws_monthly_balanced"' in out
352
353
354 def test_rounded_cents_figure_matches_not_truncated () -> None :
355 # estimate 112.90 renders as $113 (nearest dollar); the validator must accept
356 # it, not reject it because int(112.90) == 112.
357 html = GOOD .replace(
358 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
359 '<span data-cost-key="aws_monthly_balanced">$113/mo</span>' ,
360 )
361 with tempfile.TemporaryDirectory() as tmp:
362 d = Path(tmp)
363 (d / "estimation-infra.json" ).write_text(
364 json.dumps({ "projected_costs" : { "aws_monthly_balanced" : 112.90 }}),
365 encoding = "utf-8" ,
366 )
367 code, out = run(html, migration_dir = d)
368 assert code == 0 , out
369 assert "REPORT_OK" in out
370
371
372 def test_two_dollar_boundary_canonicalizes_consistently () -> None :
373 # estimate 1.999 canonicalizes to "2" and a displayed $2 canonicalizes to "2",
374 # so they match — the $2 rounding-threshold boundary must not spuriously fail.
375 html = GOOD .replace(
376 '<span data-cost-key="aws_monthly_balanced">$112/mo</span>' ,
377 '<span data-cost-key="aws_monthly_balanced">$2/mo</span>' ,
378 )
379 with tempfile.TemporaryDirectory() as tmp:
380 d = Path(tmp)
381 (d / "estimation-infra.json" ).write_text(
382 json.dumps({ "projected_costs" : { "aws_monthly_balanced" : 1.999 }}),
383 encoding = "utf-8" ,
384 )
385 code, out = run(html, migration_dir = d)
386 assert code == 0 , out
387 assert "REPORT_OK" in out