"""Tests for the leak-free breaker (503) and rate-limit (429) error handlers.""" from types import SimpleNamespace from flask import Flask from flask_limiter.errors import RateLimitExceeded from core.hardening.breaker import CircuitBreakerError from core.hardening.errors import register_hardening_error_handlers def _client(view): app = Flask(__name__) register_hardening_error_handlers(app) app.add_url_rule('/boom', 'boom', view) return app.test_client() def test_breaker_open_renders_503_without_resource_name(): """A breaker-open error renders 503 with no circuit or resource name in the body.""" def boom(): raise CircuitBreakerError('snowflake') res = _client(boom).get('/boom') assert res.status_code == 503 assert b'snowflake' not in res.data assert b'circuit' not in res.data def test_rate_limited_renders_429_without_policy_string(): """A rate-limit error renders 429 with no limit policy string in the body.""" # RateLimitExceeded derives its default description from str(limit.limit), # which is the policy string we must never surface to clients. limit = SimpleNamespace(error_message=None, limit='5 per 1 minute') def boom(): raise RateLimitExceeded(limit) res = _client(boom).get('/boom') assert res.status_code == 429 assert b'5 per 1 minute' not in res.data assert b'minute' not in res.data assert b'per' not in res.data