Files
cleveragents-core/robot/helper_scale_test.py
T
brent.edwards b88bc0ec1b
CI / build (push) Successful in 19s
CI / lint (push) Successful in 3m19s
CI / quality (push) Successful in 3m43s
CI / typecheck (push) Successful in 3m55s
CI / security (push) Successful in 4m2s
CI / unit_tests (push) Successful in 6m39s
CI / integration_tests (push) Successful in 6m48s
CI / docker (push) Successful in 1m7s
CI / e2e_tests (push) Successful in 9m7s
CI / benchmark-regression (push) Has been skipped
CI / coverage (push) Failing after 16m36s
CI / benchmark-publish (push) Successful in 25m51s
CI / status-check (push) Failing after 4s
feat(perf): large project scaling tests (#984)
## Summary

Add large project scaling benchmarks and tests at production scale (10K–100K files).

### New ASV Benchmarks

**IndexingScalingSuite** (`large_project_scaling_bench.py`):
- `time_walk_and_index` at 1K/10K/50K/100K files
- `time_incremental_refresh` (1% modified files)
- `track_indexed_file_count`, `track_tokens_per_second`

**ContextAssemblyScalingSuite** (`context_assembly_scaling_bench.py`):
- `time_full_pipeline` at 100/1K/5K/10K fragments
- `time_tiered_strategy`, `time_recency_strategy`
- `track_assembled_tokens`, `track_fragments_per_second`

**ExecutionThroughputSuite** (`execution_throughput_bench.py`):
- `time_sequential_plans` at 10/50/100 plans
- `time_executor_construction`, `time_decision_tree_scaling`

### Scale Fixture Updates

- Added `xlarge` (50K files) and `xxlarge` (100K files) profiles to `scale_metadata.json`
- Added 50K/100K thresholds to `baseline_thresholds.json`
- Added `context_assembly` and `execution_throughput` threshold sections

### Tests & Documentation

- 15 Behave scenarios validating profiles, thresholds, monotonicity, memory budgets
- 6 Robot integration tests including live 1K-file indexing throughput check
- `docs/reference/scaling_baselines.md` documenting all baseline metrics

### Quality Gates

| Session | Result |
|---|---|
| `nox -s lint` | PASS |
| `nox -s typecheck` | PASS (0 errors) |
| `nox -s unit_tests` | PASS (10,910 scenarios) |
| `nox -s integration_tests` | PASS (1,526 tests) |
| `nox -s coverage_report` | 97% (>= 97%) |

Closes #859

Reviewed-on: #984
Co-authored-by: Brent E. Edwards <brent.edwards@cleverthis.com>
Co-committed-by: Brent E. Edwards <brent.edwards@cleverthis.com>
2026-03-21 04:46:45 +00:00

200 lines
7.1 KiB
Python

"""Robot Framework helper for scale test fixture validation.
Provides a CLI-style interface for Robot to invoke fixture loading,
metadata validation, threshold checks, and profile simulation.
Exit code 0 = success, 1 = failure.
Usage:
python robot/helper_scale_test.py load-metadata
python robot/helper_scale_test.py validate-profiles
python robot/helper_scale_test.py validate-thresholds
python robot/helper_scale_test.py check-monotonicity
python robot/helper_scale_test.py simulate-distribution
python robot/helper_scale_test.py check-generator-docs
"""
from __future__ import annotations
import json
import math
import sys
from pathlib import Path
# Ensure the src directory is on the import path.
_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
sys.path.insert(0, _SRC)
_FIXTURES_DIR = Path(__file__).resolve().parents[1] / "features" / "fixtures" / "scale"
def _load_json(filename: str) -> dict:
"""Load a JSON fixture file."""
path = _FIXTURES_DIR / filename
if not path.exists():
print(f"ERROR: fixture not found: {path}")
sys.exit(1)
with open(path) as fh:
return json.load(fh)
def _cmd_load_metadata() -> int:
"""Load scale_metadata.json and verify basic structure."""
data = _load_json("scale_metadata.json")
assert "schema_version" in data, "Missing schema_version"
assert "scale_profiles" in data, "Missing scale_profiles"
assert len(data["scale_profiles"]) > 0, "No profiles defined"
print(f"scale-metadata-ok: {len(data['scale_profiles'])} profiles loaded")
return 0
def _cmd_validate_profiles() -> int:
"""Validate all scale profiles have required fields."""
data = _load_json("scale_metadata.json")
required_fields = [
"name",
"file_count",
"total_size_mb",
"language_mix",
"expected_index_time_s",
"expected_decomposition_time_s",
]
expected_names = {"small", "medium", "large", "xlarge", "xxlarge"}
actual_names = {p["name"] for p in data["scale_profiles"]}
assert actual_names == expected_names, (
f"Expected profiles {expected_names}, got {actual_names}"
)
for profile in data["scale_profiles"]:
for field in required_fields:
assert field in profile, f"Missing {field} in profile {profile.get('name')}"
# Verify language mix sums to 1.0
mix_total = sum(profile["language_mix"].values())
assert math.isclose(mix_total, 1.0, rel_tol=1e-6), (
f"Language mix for {profile['name']} sums to {mix_total}"
)
print(f"scale-profiles-ok: {len(data['scale_profiles'])} profiles validated")
return 0
def _cmd_validate_thresholds() -> int:
"""Load and validate baseline thresholds structure."""
data = _load_json("baseline_thresholds.json")
assert "schema_version" in data, "Missing schema_version"
for section in ("indexing", "decomposition"):
assert section in data, f"Missing section: {section}"
for file_count in (
"1000_files",
"5000_files",
"10000_files",
"50000_files",
"100000_files",
):
assert file_count in data[section], f"Missing {file_count} in {section}"
for pct in ("p50_ms", "p95_ms", "p99_ms"):
assert pct in data[section][file_count], (
f"Missing {pct} in {section}[{file_count}]"
)
print("scale-thresholds-ok: all threshold sections validated")
return 0
def _cmd_check_monotonicity() -> int:
"""Verify p50 < p95 < p99 for all threshold entries."""
data = _load_json("baseline_thresholds.json")
for section in ("indexing", "decomposition"):
for file_count, thresholds in data[section].items():
p50 = thresholds["p50_ms"]
p95 = thresholds["p95_ms"]
p99 = thresholds["p99_ms"]
assert p50 < p95 < p99, (
f"Non-monotonic in {section}[{file_count}]: "
f"p50={p50}, p95={p95}, p99={p99}"
)
print("scale-monotonicity-ok: all thresholds are monotonically increasing")
return 0
def _cmd_simulate_distribution() -> int:
"""Generate file distributions for all profiles and verify totals."""
data = _load_json("scale_metadata.json")
for profile in data["scale_profiles"]:
total = profile["file_count"]
mix = profile["language_mix"]
distribution: dict[str, int] = {}
allocated = 0
items = sorted(mix.items(), key=lambda x: x[1], reverse=True)
for i, (lang, ratio) in enumerate(items):
if i == len(items) - 1:
distribution[lang] = total - allocated
else:
count = round(total * ratio)
distribution[lang] = count
allocated += count
actual_total = sum(distribution.values())
assert actual_total == total, (
f"Profile {profile['name']}: expected {total} files, got {actual_total}"
)
# Verify distribution roughly matches mix
for lang, expected_ratio in mix.items():
actual_ratio = distribution[lang] / total
assert abs(actual_ratio - expected_ratio) < 0.05, (
f"Profile {profile['name']}, lang {lang}: "
f"expected ~{expected_ratio}, got {actual_ratio}"
)
print(f" {profile['name']}: {distribution}")
print("scale-distribution-ok: all profile distributions validated")
return 0
def _cmd_check_generator_docs() -> int:
"""Verify the generator instructions document exists and has content."""
path = _FIXTURES_DIR / "generator_instructions.md"
assert path.exists(), f"Generator instructions not found: {path}"
content = path.read_text()
assert len(content) > 100, "Generator instructions file is too short"
assert "## Overview" in content, "Missing Overview section"
assert "## Generating" in content or "## Prerequisites" in content, (
"Missing generation instructions"
)
print("scale-generator-docs-ok: generator instructions validated")
return 0
def main() -> int:
"""Entry point called by Robot Framework ``Run Process``."""
if len(sys.argv) < 2:
print(
"Usage: helper_scale_test.py "
"<load-metadata|validate-profiles|validate-thresholds|"
"check-monotonicity|simulate-distribution|check-generator-docs>"
)
return 1
command = sys.argv[1]
commands = {
"load-metadata": _cmd_load_metadata,
"validate-profiles": _cmd_validate_profiles,
"validate-thresholds": _cmd_validate_thresholds,
"check-monotonicity": _cmd_check_monotonicity,
"simulate-distribution": _cmd_simulate_distribution,
"check-generator-docs": _cmd_check_generator_docs,
}
handler = commands.get(command)
if handler is None:
print(f"Unknown command: {command}")
return 1
try:
return handler()
except (AssertionError, KeyError, json.JSONDecodeError) as exc:
print(f"FAIL: {exc}")
return 1
if __name__ == "__main__":
sys.exit(main())