162 lines
5.5 KiB
Python
162 lines
5.5 KiB
Python
"""ASV benchmarks for validation edge case processing.
|
|
|
|
Measures the performance of:
|
|
- Fixture loading and parsing
|
|
- Malformed output detection
|
|
- Missing resource validation
|
|
- Timeout simulation logic
|
|
- Schema transform validation
|
|
- Validation ordering and deduplication
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
_SRC = str(Path(__file__).resolve().parents[1] / "src")
|
|
if _SRC not in sys.path:
|
|
sys.path.insert(0, _SRC)
|
|
|
|
_FIXTURE_DIR = (
|
|
Path(__file__).resolve().parents[1] / "features" / "fixtures" / "validation"
|
|
)
|
|
|
|
|
|
def _load_fixture(name: str) -> list[dict[str, Any]]:
|
|
"""Load a named fixture file and return its fixture list."""
|
|
path = _FIXTURE_DIR / f"{name}.json"
|
|
with open(path) as fh:
|
|
data = json.load(fh)
|
|
return data["fixtures"]
|
|
|
|
|
|
def _validate_tool_output(case: dict[str, Any]) -> str | None:
|
|
"""Lightweight tool output validation for benchmarking."""
|
|
inp = case["input"]
|
|
output = inp.get("output")
|
|
if "tool_name" not in inp:
|
|
return "missing required field: tool_name"
|
|
if isinstance(output, str):
|
|
return "tool output must be an object"
|
|
if not isinstance(output, dict):
|
|
return "tool output must be an object"
|
|
if not output:
|
|
return "tool output must contain at least 'status' and 'result'"
|
|
if "status" not in output:
|
|
return "missing required field: status"
|
|
if not isinstance(output.get("status"), str):
|
|
return "invalid type for field 'status'"
|
|
if "result" in output and output["result"] is None:
|
|
return "result payload must not be null"
|
|
return None
|
|
|
|
|
|
def _validate_resource(case: dict[str, Any]) -> str | None:
|
|
"""Lightweight resource reference validation for benchmarking."""
|
|
inp = case["input"]
|
|
if "action_ref" in inp and "nonexistent" in inp["action_ref"]:
|
|
return "action not found"
|
|
if "project_refs" in inp:
|
|
for ref in inp["project_refs"]:
|
|
if ".." in ref:
|
|
return "unresolvable project path"
|
|
if "phase" in inp and "actor_config" in inp:
|
|
phase = inp["phase"]
|
|
actor_key = f"{phase}_actor"
|
|
if actor_key not in inp["actor_config"]:
|
|
return f"missing actor for phase '{phase}'"
|
|
return None
|
|
|
|
|
|
def _sort_validations(case: dict[str, Any]) -> tuple[list[str] | None, str | None]:
|
|
"""Sort validations by level priority."""
|
|
inp = case["input"]
|
|
validations = inp["validations"]
|
|
attachment_map: dict[str, str] = {}
|
|
for v in validations:
|
|
att_id = v.get("attachment_id")
|
|
if att_id is not None:
|
|
if att_id in attachment_map:
|
|
return None, f"duplicate attachment ID: {att_id}"
|
|
attachment_map[att_id] = v["id"]
|
|
required = [v for v in validations if v["level"] == "required"]
|
|
informational = [v for v in validations if v["level"] == "informational"]
|
|
return [v["id"] for v in required + informational], None
|
|
|
|
|
|
class ValidationEdgeFixtureLoadSuite:
|
|
"""Benchmarks for fixture loading and parsing."""
|
|
|
|
timeout = 60
|
|
|
|
def time_load_malformed_tool_output(self) -> None:
|
|
"""Benchmark loading malformed_tool_output fixture."""
|
|
_load_fixture("malformed_tool_output")
|
|
|
|
def time_load_missing_resources(self) -> None:
|
|
"""Benchmark loading missing_resources fixture."""
|
|
_load_fixture("missing_resources")
|
|
|
|
def time_load_validation_timeouts(self) -> None:
|
|
"""Benchmark loading validation_timeouts fixture."""
|
|
_load_fixture("validation_timeouts")
|
|
|
|
def time_load_invalid_schema_transforms(self) -> None:
|
|
"""Benchmark loading invalid_schema_transforms fixture."""
|
|
_load_fixture("invalid_schema_transforms")
|
|
|
|
def time_load_mixed_ordering(self) -> None:
|
|
"""Benchmark loading mixed_ordering fixture."""
|
|
_load_fixture("mixed_ordering")
|
|
|
|
def time_load_all_fixtures(self) -> None:
|
|
"""Benchmark loading all fixture files."""
|
|
names = [
|
|
"malformed_tool_output",
|
|
"missing_resources",
|
|
"validation_timeouts",
|
|
"invalid_schema_transforms",
|
|
"mixed_ordering",
|
|
]
|
|
for name in names:
|
|
_load_fixture(name)
|
|
|
|
|
|
class ValidationEdgeDetectionSuite:
|
|
"""Benchmarks for validation edge case detection."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
"""Load all fixtures for benchmarking."""
|
|
self.malformed_fixtures = _load_fixture("malformed_tool_output")
|
|
self.resource_fixtures = _load_fixture("missing_resources")
|
|
self.ordering_fixtures = _load_fixture("mixed_ordering")
|
|
|
|
def time_malformed_output_detection(self) -> None:
|
|
"""Benchmark malformed output detection across all cases."""
|
|
for case in self.malformed_fixtures:
|
|
_validate_tool_output(case)
|
|
|
|
def time_missing_resource_detection(self) -> None:
|
|
"""Benchmark missing resource detection across all cases."""
|
|
for case in self.resource_fixtures:
|
|
_validate_resource(case)
|
|
|
|
def time_validation_ordering(self) -> None:
|
|
"""Benchmark validation ordering and duplicate detection."""
|
|
for case in self.ordering_fixtures:
|
|
_sort_validations(case)
|
|
|
|
def time_combined_edge_detection(self) -> None:
|
|
"""Benchmark combined edge case detection across all fixture types."""
|
|
for case in self.malformed_fixtures:
|
|
_validate_tool_output(case)
|
|
for case in self.resource_fixtures:
|
|
_validate_resource(case)
|
|
for case in self.ordering_fixtures:
|
|
_sort_validations(case)
|