81 / 89 · 04 API & Contract Testing with AI · Schema Drift Detection← prev⊞ allnext →☰ Read as one page
12.5Comprehensive Drift Scanner
class FullDriftScanner:
"""Scan all endpoints for all types of drift."""
def __init__(self, spec: dict, base_url: str, auth_token: str = None):
self.spec = spec
self.base_url = base_url
self.headers = {}
if auth_token:
self.headers["Authorization"] = f"Bearer {auth_token}"
def scan_all(self) -> dict:
"""Scan every endpoint in the spec."""
report = {
"timestamp": datetime.now().isoformat(),
"base_url": self.base_url,
"endpoints_scanned": 0,
"drifts": [],
}
for path, methods in self.spec["paths"].items():
for method, details in methods.items():
if method not in ("get", "post", "put", "patch", "delete"):
continue
report["endpoints_scanned"] += 1
try:
drifts = self.check_endpoint(path, method, details)
report["drifts"].extend(drifts)
except Exception as e:
report["drifts"].append(Drift(
type="SCAN_ERROR",
field=f"{method.upper()} {path}",
severity="MEDIUM",
message=f"Could not scan: {e}"
))
return report
def check_endpoint(self, path, method, details) -> list[Drift]:
"""Check a single endpoint for drift."""
drifts = []
# Check response schema drift
if "200" in details.get("responses", {}):
response_drifts = self.check_response_drift(path, method, details)
drifts.extend(response_drifts)
# Check if documented error codes are actually returned
for status_code in details.get("responses", {}):
if status_code.startswith("4") or status_code.startswith("5"):
# Attempt to trigger this error code
pass # Requires endpoint-specific logic
return drifts