@@ -1746,6 +1746,7 @@ def test_http_smoke_checks_safe_404_and_writes_only_redacted_evidence(self) -> N
17461746 200 ,
17471747 noindex ,
17481748 b"Learn data skills. For free. Together."
1749+ b'<link rel="canonical" href="https://datatalks.club/courses/">'
17491750 b'<link rel="stylesheet" href="/static/courses.fixture.css">' ,
17501751 ),
17511752 Response (
@@ -1795,12 +1796,18 @@ def test_http_smoke_checks_safe_404_and_writes_only_redacted_evidence(self) -> N
17951796 )
17961797 persisted = path .read_text ()
17971798 self .assertEqual (__import__ ("json" ).loads (persisted ), evidence )
1799+ courses_check = next (
1800+ check for check in evidence ["checks" ] if check .get ("path" ) == "/courses/"
1801+ )
1802+ self .assertTrue (courses_check ["exact_canonical" ])
1803+ self .assertNotIn ("canonical_absent" , courses_check )
17981804 self .assertEqual (evidence ["checks" ][- 1 ]["runtime_group" ], "analytics" )
17991805 for forbidden in ("cookie" , "authorization" , "set-cookie" , "response_body" ):
18001806 self .assertNotIn (forbidden , persisted .lower ())
18011807
18021808 invalid_surface_responses = (
18031809 (
1810+ "home identity" ,
18041811 2 ,
18051812 Response (
18061813 200 ,
@@ -1811,6 +1818,7 @@ def test_http_smoke_checks_safe_404_and_writes_only_redacted_evidence(self) -> N
18111818 "home page lacks expected content" ,
18121819 ),
18131820 (
1821+ "course identity" ,
18141822 4 ,
18151823 Response (
18161824 200 ,
@@ -1821,8 +1829,8 @@ def test_http_smoke_checks_safe_404_and_writes_only_redacted_evidence(self) -> N
18211829 "course discovery lacks expected content" ,
18221830 ),
18231831 )
1824- for response_index , invalid_response , error_message in invalid_surface_responses :
1825- with self .subTest (error_message = error_message ):
1832+ for case , response_index , invalid_response , error_message in invalid_surface_responses :
1833+ with self .subTest (case = case ):
18261834 invalid_responses = [* responses ]
18271835 invalid_responses [response_index ] = invalid_response
18281836 with (
@@ -1831,6 +1839,38 @@ def test_http_smoke_checks_safe_404_and_writes_only_redacted_evidence(self) -> N
18311839 ):
18321840 run_http_smoke ("https://web.dtcdev.click" , SHA_A )
18331841
1842+ exact_courses_canonical = b'<link rel="canonical" href="https://datatalks.club/courses/">'
1843+ invalid_courses_canonicals = (
1844+ ("missing" , b"" ),
1845+ ("duplicate" , exact_courses_canonical * 2 ),
1846+ (
1847+ "wrong path" ,
1848+ b'<link rel="canonical" href="https://datatalks.club/courses/wrong/">' ,
1849+ ),
1850+ (
1851+ "external" ,
1852+ b'<link rel="canonical" href="https://example.com/courses/">' ,
1853+ ),
1854+ )
1855+ for case , rendered_canonical in invalid_courses_canonicals :
1856+ invalid_responses = [* responses ]
1857+ invalid_responses [4 ] = Response (
1858+ 200 ,
1859+ noindex ,
1860+ b"Learn data skills. For free. Together."
1861+ + rendered_canonical
1862+ + b'<link rel="stylesheet" href="/static/courses.fixture.css">' ,
1863+ )
1864+ with (
1865+ self .subTest (case = case ),
1866+ patch ("deploy.smoke._request" , side_effect = invalid_responses ),
1867+ self .assertRaisesMessage (
1868+ ReleaseContractError ,
1869+ "course discovery production canonical differs" ,
1870+ ),
1871+ ):
1872+ run_http_smoke ("https://web.dtcdev.click" , SHA_A )
1873+
18341874 invalid_admin_responses = (
18351875 (
18361876 Response (
0 commit comments