test_rule_execution_capacity.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. from __future__ import annotations
  2. import json
  3. from pathlib import Path
  4. from scripts.validation.rule_execution_capacity import (
  5. CapacityLimits,
  6. CapacityObservation,
  7. assert_capacity_within_limits,
  8. write_capacity_evidence,
  9. )
  10. def test_capacity_evidence_is_recalculable_and_fails_closed(tmp_path):
  11. limits = CapacityLimits(
  12. runner_memory_bytes=512 * 1024 * 1024,
  13. datasource_pool_budget=4,
  14. artifact_bytes=128 * 1024 * 1024,
  15. )
  16. observations = [
  17. CapacityObservation(
  18. backend="sql_pushdown",
  19. rows=100_000,
  20. elapsed_seconds=2.0,
  21. peak_memory_bytes=64 * 1024 * 1024,
  22. pool_peak=2,
  23. artifact_bytes=0,
  24. node_durations_seconds=(1.8, 2.0, 2.2),
  25. ),
  26. CapacityObservation(
  27. backend="polars_batch",
  28. rows=100_000,
  29. elapsed_seconds=4.0,
  30. peak_memory_bytes=128 * 1024 * 1024,
  31. pool_peak=1,
  32. artifact_bytes=8 * 1024 * 1024,
  33. node_durations_seconds=(3.8, 4.0, 4.2),
  34. ),
  35. ]
  36. for observation in observations:
  37. assert_capacity_within_limits(observation, limits)
  38. output = tmp_path / "capacity.json"
  39. write_capacity_evidence(
  40. output,
  41. observations=observations,
  42. limits=limits,
  43. machine={"source": "pytest", "logical_cpu_count": 8},
  44. )
  45. evidence = json.loads(output.read_text(encoding="utf-8"))
  46. assert evidence["schema_version"] == "1.0"
  47. assert evidence["limits"]["datasource_pool_budget"] == 4
  48. assert evidence["observations"][0]["throughput_rows_per_second"] == 50_000
  49. assert evidence["observations"][0]["p95_node_seconds"] == 2.2
  50. assert evidence["evidence_sha256"] == (
  51. write_capacity_evidence.digest_payload(evidence)
  52. )
  53. def test_capacity_gate_rejects_runner_pool_and_artifact_overruns():
  54. limits = CapacityLimits(
  55. runner_memory_bytes=100,
  56. datasource_pool_budget=2,
  57. artifact_bytes=100,
  58. )
  59. cases = [
  60. CapacityObservation(
  61. "polars_batch", 100, 1.0, 101, 1, 10, (1.0,)
  62. ),
  63. CapacityObservation(
  64. "sql_pushdown", 100, 1.0, 10, 3, 0, (1.0,)
  65. ),
  66. CapacityObservation(
  67. "polars_batch", 100, 1.0, 10, 1, 101, (1.0,)
  68. ),
  69. ]
  70. for observation in cases:
  71. try:
  72. assert_capacity_within_limits(observation, limits)
  73. except ValueError as exc:
  74. assert "capacity limit exceeded" in str(exc)
  75. else:
  76. raise AssertionError("capacity overrun was accepted")
  77. def test_capacity_targets_declare_100k_1m_and_10m_or_hardware_limit():
  78. manifest = Path("docs/validation/data-rule-m5-capacity-targets.json")
  79. assert manifest.is_file()
  80. payload = json.loads(manifest.read_text(encoding="utf-8"))
  81. assert payload["requested_rows"] == [100_000, 1_000_000, 10_000_000]
  82. assert payload["hardware_limit"]["maximum_measured_rows"] >= 100_000
  83. if payload["hardware_limit"]["maximum_measured_rows"] < 10_000_000:
  84. assert payload["hardware_limit"]["reason"]