@@ -33,6 +33,24 @@ def test_html_ok_when_all_checks_pass
3333 assert check_lines . any? { |t | t . include? ( "✅" ) }
3434 end
3535
36+ def test_html_escapes_check_name_and_message
37+ @config . check ( "<script>alert('x')</script>" ) { make_sure false , "<b>bad</b>" }
38+ get "/"
39+ doc = Nokogiri ::HTML . parse ( last_response . body )
40+ # Ensure escaped content appears literally
41+ assert_includes doc . text , "<script>alert('x')</script>"
42+ assert_includes doc . text , "<b>bad</b>"
43+ # Raw script tag should not be rendered
44+ refute doc . at_css ( 'script' )
45+ # The name is wrapped in a <b> by the view; ensure its content is the escaped name
46+ name_b = doc . at_css ( '.check b' )
47+ assert_equal "<script>alert('x')</script>" , name_b . text
48+ # The message is inside <i> and should not contain raw <b> elements
49+ msg_i = doc . at_css ( '.check i' )
50+ assert_includes msg_i . text , "<b>bad</b>"
51+ refute msg_i . at_css ( 'b' )
52+ end
53+
3654 def test_json_error_when_any_check_fails
3755 @config . check ( "Fail" ) { expect ( 1 ) . to_eq ( 2 ) }
3856 header "Accept" , "application/json"
@@ -52,6 +70,21 @@ def test_json_error_when_any_check_fails
5270 assert_kind_of Numeric , first [ "duration" ]
5371 end
5472
73+ def test_json_schema_for_skipped_check
74+ Rails . stub ( :env , ActiveSupport ::StringInquirer . new ( "test" ) ) do
75+ @config . check ( "Only prod" , only : :production ) { make_sure true }
76+ end
77+ header "Accept" , "application/json"
78+ get "/"
79+ assert_equal 200 , last_response . status
80+ json = JSON . parse ( last_response . body )
81+ skipped = json [ "checks" ] . find { |c | c [ "name" ] == "Only prod" }
82+ assert skipped [ "success" ]
83+ assert_equal true , skipped [ "skipped" ]
84+ assert_kind_of String , skipped [ "message" ]
85+ assert_equal 0 , skipped [ "duration" ]
86+ end
87+
5588 def test_timeout_is_handled
5689 @config . check ( "Timeout" , timeout : 0.01 ) { sleep 0.1 ; make_sure true }
5790
@@ -103,6 +136,37 @@ def test_rate_limit_resets_next_day
103136 end
104137 end
105138
139+ def test_rate_limit_error_persists_across_periods_and_blocks_execution
140+ travel_to Time . utc ( 2024 , 12 , 31 , 23 , 55 , 0 ) do
141+ # Define a check that fails first time, then passes after midnight
142+ attempts = 0
143+ @config . check ( "Flaky API" , run : "10 times per hour" ) do
144+ attempts += 1
145+ if attempts == 1
146+ make_sure false , "boom"
147+ else
148+ make_sure true , "ok"
149+ end
150+ end
151+
152+ # First run fails and stores error state
153+ get "/"
154+ assert_equal 503 , last_response . status
155+
156+ # Subsequent run within same hour should be skipped due to previous error
157+ get "/"
158+ assert last_response . ok? || last_response . status == 503
159+ assert_includes text_content ( last_response . body ) , "Rate limited"
160+
161+ # Even after period change, previous error should keep the check skipped until success
162+ travel 10 . minutes
163+ travel_to Time . utc ( 2025 , 1 , 1 , 0 , 5 , 0 )
164+ get "/"
165+ # Still rate limited due to previous error persistence
166+ assert_includes text_content ( last_response . body ) , "Rate limited"
167+ end
168+ end
169+
106170 def test_error_rescue_returns_500
107171 # Force an unexpected error in run_checks by stubbing configuration
108172 Allgood . stub ( :configuration , nil ) do
0 commit comments