Normalize ISO8601 datetimes in mock server request body matching.

Treat Z and +00:00 as equivalent when comparing serialized SDK request
bodies to OAS examples, fixing alert suppression window integration tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Kevin 2026-07-28 12:22:48 +01:00
parent b1faff026e
commit f1f280d48c
2 changed files with 40 additions and 1 deletions

View File

@ -19,6 +19,7 @@ from __future__ import annotations
import json import json
import re import re
import threading import threading
from datetime import datetime
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from typing import Any, Dict, Mapping, Optional from typing import Any, Dict, Mapping, Optional
from urllib.parse import urlparse from urllib.parse import urlparse
@ -30,12 +31,21 @@ ERROR_STATUS_HEADER = "X-TE-Error-Status"
AUTHORIZATION_HEADER = "Authorization" AUTHORIZATION_HEADER = "Authorization"
def _normalize_scalar(value: Any) -> Any:
if isinstance(value, str) and "T" in value:
try:
return datetime.fromisoformat(value.replace("Z", "+00:00")).isoformat().replace("+00:00", "Z")
except ValueError:
return value
return value
def _normalize_json(value: Any) -> Any: def _normalize_json(value: Any) -> Any:
if isinstance(value, dict): if isinstance(value, dict):
return {key: _normalize_json(value[key]) for key in sorted(value.keys())} return {key: _normalize_json(value[key]) for key in sorted(value.keys())}
if isinstance(value, list): if isinstance(value, list):
return [_normalize_json(item) for item in value] return [_normalize_json(item) for item in value]
return value return _normalize_scalar(value)
def _json_body_matches(expected: Any, actual: Any) -> bool: def _json_body_matches(expected: Any, actual: Any) -> bool:

View File

@ -185,3 +185,32 @@ def test_mock_server_rejects_path_missing_path_variable(manifest):
) )
assert status == 400 assert status == 400
assert json.loads(body.decode("utf-8"))["detail"] == "Path does not match operation expectation" assert json.loads(body.decode("utf-8"))["detail"] == "Path does not match operation expectation"
def test_mock_server_accepts_equivalent_iso8601_datetime_formats(manifest):
datetime_manifest = {
**manifest,
"createAlertRule": OperationExpectation(
operation_id="createAlertRule",
method="POST",
path="/alerts/rules",
request_body_example={
"ruleName": "Example",
"startDate": "2017-07-01T05:00:00Z",
},
success_status=201,
success_body={"ruleId": "1"},
),
}
with MockApiServer(datetime_manifest) as server:
status, body = _request(
server,
method="POST",
path="/alerts/rules",
body={
"ruleName": "Example",
"startDate": "2017-07-01T05:00:00+00:00",
},
)
assert status == 201
assert json.loads(body.decode("utf-8")) == {"ruleId": "1"}