@@ -85,6 +85,21 @@ class ScriptedStep(BaseModel):
8585)
8686
8787
88+ def _default_ratelimit_headers () -> dict [str , str ]:
89+ # Mirrors the backend's update_user_quota(): every response — including the
90+ # 404 from custom_404 — carries the four x-api-ratelimit-* headers the SDK's
91+ # RateLimitHeaders.parse() reads. Parse is all-or-nothing, so always emit all
92+ # four. Scripts can override any of these (e.g. remaining=0 to drive the
93+ # §10.3 preflight / exhausted-credits path) — see catch_all's setdefault.
94+ reset_epoch = int (time .time ()) + 24 * 3600 # next daily quota reset
95+ return {
96+ "x-api-ratelimit-limit" : "100000" ,
97+ "x-api-ratelimit-remaining" : "99999" ,
98+ "x-api-ratelimit-reset" : str (reset_epoch ),
99+ "x-api-ratelimit-consumed" : "1" ,
100+ }
101+
102+
88103def _default_status_body () -> str :
89104 now_epoch = int (time .time ())
90105 return json .dumps (
@@ -185,6 +200,9 @@ async def catch_all(full_path: str, request: Request) -> Response:
185200 # unless the script explicitly overrode it.
186201 response_headers .setdefault ("cf-ray" , f"mock-{ int (time .time () * 1000 )} " )
187202 response_headers .setdefault ("Content-Type" , "application/json" )
203+ # Rate-limit headers, unless the script set its own (e.g. remaining=0).
204+ for _k , _v in _default_ratelimit_headers ().items ():
205+ response_headers .setdefault (_k , _v )
188206 _request_log .append (
189207 {"path" : path , "method" : method , "status" : step .status , "scripted" : True }
190208 )
@@ -206,6 +224,7 @@ async def catch_all(full_path: str, request: Request) -> Response:
206224 headers = {
207225 "Content-Type" : "application/json" ,
208226 "cf-ray" : f"mock-{ int (time .time () * 1000 )} " ,
227+ ** _default_ratelimit_headers (),
209228 },
210229 )
211230
@@ -223,4 +242,7 @@ def _default_response_for(path: str) -> tuple[str, int]:
223242 return _DEFAULT_HEADERS , 200
224243 if path in ("/status/" , "/status" ):
225244 return _default_status_body (), 200
226- return json .dumps ({"s" : "error" , "errmsg" : f"unknown endpoint { path } " }), 404
245+ # Unknown routes mirror the backend's custom_404: {"s":"no_data"} at 404,
246+ # NOT an error envelope. The SDK treats 404+no_data as a successful empty
247+ # response (Response.isNoData()), so this keeps the mock faithful.
248+ return json .dumps ({"s" : "no_data" }), 404
0 commit comments