litellm/tests/rust-python-harness/strategies/unit_tests_mapping/contracts.py
yujonglee ee08c36fc0
refactor(tests): restructure rust python harness around strategy definitions (#39628)
* wip

* refactor(tests): move sdk function tracing into rust python harness

* dead code

* fix: handle harness keyboard interrupts

* refactor(tests): deduplicate rust python harness helpers

* fix(harness): expose validated strategy choices

* wip

* refactor(harness): let strategies own parity reports

* docs(harness): update strategy structure

* refactor(harness): localize strategy report views

* wip

* fix(harness): satisfy mapping runner type checks

* fix(harness): clarify trace parity output

* wip

* fix(harness): clarify unit mapping report

* fix(harness): finalize trace parity contracts

* refactor(harness): structure parity contracts

* feat: derive unit test mapping from traces

* feat(harness): map rstest test families

* feat(ocr): port Azure document intelligence tests

* feat(harness): enforce complete unit mappings

* feat(ocr): add reducto core transforms

* feat(harness): classify host-only unit tests

* fix(ocr): complete Rust provider plumbing

* fix(harness): reuse OCR parity workers
2026-09-03 21:15:01 -07:00

220 lines
8.2 KiB
Python

from __future__ import annotations
from collections import Counter
from typing import Final, Literal
from pydantic import BaseModel, ConfigDict, field_validator, model_validator
from typing_extensions import Self
from ...shared.tracing.pytest_usage import PythonFunctionReference
from ...shared.unit_runners.rust_runner import RustTarget, RustTestIdentity, RustTestScope
class _ContractModel(BaseModel):
model_config = ConfigDict(frozen=True, extra="forbid")
def _clean_unique(values: tuple[str, ...], field: str) -> tuple[str, ...]:
cleaned: Final = tuple(value.strip().rstrip("/") for value in values)
if not cleaned or any(not value for value in cleaned):
raise ValueError(f"{field} must contain non-empty paths")
duplicates: Final = tuple(value for value, count in Counter(cleaned).items() if count > 1)
if duplicates:
raise ValueError(f"{field} contains duplicates: {sorted(duplicates)}")
return cleaned
def _selector_contains(parent: str, child: str) -> bool:
return child == parent or child.startswith(f"{parent}/")
class RustTestFamily(_ContractModel):
kind: Literal["family"] = "family"
target: RustTarget
name: str
@field_validator("name")
@classmethod
def validate_name(cls, value: str) -> str:
stripped: Final = value.strip()
if not stripped or stripped.endswith("::"):
raise ValueError("must be a non-empty Rust test base name")
return stripped
@property
def key(self) -> str:
return f"{self.target.key}::{self.name}::case_*"
def contains(self, identity: RustTestIdentity) -> bool:
return identity.target == self.target and identity.name.startswith(f"{self.name}::case_")
class TestMapping(_ContractModel):
python: str
rust: RustTestIdentity | RustTestFamily
@field_validator("python")
@classmethod
def validate_python_nodeid(cls, value: str) -> str:
stripped: Final = value.strip()
if "::" not in stripped:
raise ValueError("must be a source path and test name separated by '::'")
return stripped
class PythonFunctionDiscoverySpec(_ContractModel):
functions: tuple[PythonFunctionReference, ...] = ()
trace_module: str | None = None
trace_spans: tuple[str, ...] = ()
search_roots: tuple[str, ...]
exclude_roots: tuple[str, ...] = ()
includes: tuple[str, ...] = ()
exclusions: tuple[str, ...] = ()
@field_validator("search_roots")
@classmethod
def validate_search_roots(cls, value: tuple[str, ...]) -> tuple[str, ...]:
return _clean_unique(value, "python function search_roots")
@field_validator("exclude_roots")
@classmethod
def validate_exclude_roots(cls, value: tuple[str, ...]) -> tuple[str, ...]:
if not value:
return ()
return _clean_unique(value, "python function exclude_roots")
@model_validator(mode="after")
def validate_functions(self) -> Self:
if bool(self.functions) == bool(self.trace_module):
raise ValueError("python function discovery needs exactly one function list or trace module")
if self.trace_module is not None and not self.trace_spans:
raise ValueError("trace-derived Python function discovery needs trace_spans")
if not self.functions:
return self
keys: Final = tuple(f"{function.module}:{function.qualname}" for function in self.functions)
duplicates: Final = tuple(key for key, count in Counter(keys).items() if count > 1)
if duplicates:
raise ValueError(f"python function discovery contains duplicates: {sorted(duplicates)}")
return self
class UnitParityExclusionSpec(_ContractModel):
nodeid: str
reason: str
@field_validator("nodeid", "reason")
@classmethod
def validate_fields(cls, value: str) -> str:
stripped: Final = value.strip()
if not stripped:
raise ValueError("must be a non-empty string")
return stripped
class MappingExclusionSpec(_ContractModel):
nodeid: str
reason: str
@field_validator("nodeid", "reason")
@classmethod
def validate_fields(cls, value: str) -> str:
stripped: Final = value.strip()
if not stripped:
raise ValueError("must be a non-empty string")
return stripped
class MappingSpec(_ContractModel):
python_selectors: tuple[str, ...] = ()
python_functions: PythonFunctionDiscoverySpec | None = None
rust_scope: tuple[RustTestScope, ...] = ()
rust_targets: tuple[RustTarget, ...] = ()
mappings: tuple[TestMapping, ...]
exclusions: tuple[MappingExclusionSpec, ...] = ()
require_complete: bool = False
@field_validator("python_selectors")
@classmethod
def validate_python_selectors(cls, value: tuple[str, ...]) -> tuple[str, ...]:
if not value:
return ()
return _clean_unique(value, "python_selectors")
@model_validator(mode="after")
def validate_rust_scope(self) -> Self:
if bool(self.python_selectors) == bool(self.python_functions):
raise ValueError("mapping needs exactly one Python selector or function-discovery scope")
targets: Final = tuple(scope.target.key for scope in self.rust_scope)
duplicates: Final = tuple(target for target, count in Counter(targets).items() if count > 1)
if duplicates:
raise ValueError(f"rust_scope contains duplicate targets: {sorted(duplicates)}")
target_names: Final = tuple(target.name for target in self.rust_targets)
duplicate_names: Final = tuple(name for name, count in Counter(target_names).items() if count > 1)
if duplicate_names:
raise ValueError(f"rust_targets contains duplicate names: {sorted(duplicate_names)}")
exclusion_nodeids: Final = tuple(exclusion.nodeid for exclusion in self.exclusions)
duplicate_exclusions: Final = tuple(nodeid for nodeid, count in Counter(exclusion_nodeids).items() if count > 1)
if duplicate_exclusions:
raise ValueError(f"mapping exclusions contain duplicate nodeids: {sorted(duplicate_exclusions)}")
return self
class UnitParitySpec(_ContractModel):
python_selectors: tuple[str, ...]
exclusions: tuple[UnitParityExclusionSpec, ...] = ()
@field_validator("python_selectors")
@classmethod
def validate_python_selectors(cls, value: tuple[str, ...]) -> tuple[str, ...]:
return _clean_unique(value, "unit parity python_selectors")
@model_validator(mode="after")
def validate_exclusions(self) -> Self:
nodeids: Final = tuple(exclusion.nodeid for exclusion in self.exclusions)
duplicates: Final = tuple(nodeid for nodeid, count in Counter(nodeids).items() if count > 1)
if duplicates:
raise ValueError(f"unit parity exclusions contain duplicate nodeids: {sorted(duplicates)}")
return self
class RustUnitSpec(_ContractModel):
cargo_manifest: str
cargo_filter: str
cargo_package: str | None = None
@field_validator("cargo_manifest", "cargo_filter")
@classmethod
def validate_required_fields(cls, value: str) -> str:
stripped: Final = value.strip()
if not stripped:
raise ValueError("must be a non-empty string")
return stripped
@field_validator("cargo_package")
@classmethod
def validate_package(cls, value: str | None) -> str | None:
if value is None:
return None
stripped: Final = value.strip()
if not stripped:
raise ValueError("must be a non-empty string when provided")
return stripped
class UnitTestContract(_ContractModel):
mapping: MappingSpec
unit_parity: UnitParitySpec
rust: RustUnitSpec
@model_validator(mode="after")
def validate_unit_parity_scope(self) -> Self:
if not self.mapping.python_selectors:
return self
unknown: Final = tuple(
selector
for selector in self.unit_parity.python_selectors
if not any(_selector_contains(parent, selector) for parent in self.mapping.python_selectors)
)
if unknown:
raise ValueError(f"unit parity selectors must be contained in mapping selectors: {sorted(unknown)}")
return self