Polish web demo tests and refresh notebooks

- add README live-demo badge
- tighten engine validation for jump grids and detector indices
- replace flaky FastAPI TestClient web tests with direct handler tests
- remove unused httpx dev dependency
- clean stale frontend comments
- re-execute notebooks with clean sequential outputs
This commit is contained in:
2026-08-03 08:15:22 -04:00
parent 8e6c98945b
commit e5d785f511
13 changed files with 277 additions and 238 deletions
+17
View File
@@ -52,6 +52,16 @@ def test_detection_deadline_is_enforced():
assert detect_barrier_event(samples, values, 1, 90.0, "down", 3) == (True, 3, 4)
def test_detection_rejects_invalid_indices():
values = np.array([100.0, 89.0, 88.0])
with pytest.raises(ValueError, match="within the path"):
detect_barrier_event(np.array([0, 3]), values, 1, 90.0, "down", 2)
with pytest.raises(ValueError, match="ascending"):
detect_barrier_event(np.array([2, 1]), values, 1, 90.0, "down", 2)
with pytest.raises(ValueError, match="integer grid indices"):
detect_barrier_event(np.array([0.5, 1.0]), values, 1, 90.0, "down", 2)
def test_fixed_indices_have_exact_requested_count():
for n in (2, 10, 101):
for k in range(1, n + 1):
@@ -74,6 +84,13 @@ def test_zero_jump_intensity_matches_gbm_for_same_seed():
assert np.array_equal(gbm, jump)
def test_merton_jump_diffusion_validates_time_grid_before_sampling():
with pytest.raises(ValueError, match="T"):
merton_jump_diffusion(100.0, 0.0, 100)
with pytest.raises(ValueError, match="n_steps"):
merton_jump_diffusion(100.0, 5 / TRADING_MINUTES_PER_YEAR, 0)
def test_simulation_baseline_has_exact_per_path_budget():
result = run_monte_carlo_simulation(
n_paths=12,
+35 -39
View File
@@ -1,36 +1,30 @@
from fastapi.testclient import TestClient
import pytest
from pydantic import ValidationError
from webapp.app import app
client = TestClient(app)
from webapp.app import SimulateRequest, app, health, simulate
def test_health_endpoint():
response = client.get("/api/health")
assert response.status_code == 200
payload = response.json()
payload = health()
assert payload["ok"] is True
assert payload["version"] == "0.4.7"
def test_simulation_endpoint_preserves_equal_budget():
response = client.post(
"/api/simulate",
json={
"rng_seed": 3,
"n_paths": 5,
"n_steps": 100,
"drop_fraction": 0.05,
"rise_fraction": 0.08,
"max_detection_lag_steps": 3,
},
payload = simulate(
SimulateRequest(
rng_seed=3,
n_paths=5,
n_steps=100,
drop_fraction=0.05,
rise_fraction=0.08,
max_detection_lag_steps=3,
)
)
assert response.status_code == 200
payload = response.json()
assert payload["adaptive_total_samples"] == payload["fixed_total_samples"]
assert payload["max_detection_lag_steps"] == 3
def test_webapp_imports_from_source_checkout_without_installed_package(tmp_path):
"""The documented direct uvicorn command must resolve the src-layout package."""
import os
@@ -53,30 +47,32 @@ def test_webapp_imports_from_source_checkout_without_installed_package(tmp_path)
assert "Adaptive Barrier Monitor" in completed.stdout
def test_simulation_endpoint_supports_fixed_cadence():
response = client.post(
"/api/simulate",
json={
"rng_seed": 3,
"n_paths": 5,
"n_steps": 200,
"window_minutes": 200.0,
"dt_cap_minutes": 2.0,
"comparison_mode": "fixed_cadence",
"fixed_cadence_minutes": 20.0,
},
payload = simulate(
SimulateRequest(
rng_seed=3,
n_paths=5,
n_steps=200,
window_minutes=200.0,
dt_cap_minutes=2.0,
comparison_mode="fixed_cadence",
fixed_cadence_minutes=20.0,
)
)
assert response.status_code == 200
payload = response.json()
assert payload["comparison_mode"] == "fixed_cadence"
assert payload["fixed_cadence_minutes"] == 20.0
assert payload["adaptive_total_samples"] != payload["fixed_total_samples"]
def test_simulation_endpoint_rejects_unknown_comparison_mode():
response = client.post(
"/api/simulate",
json={"comparison_mode": "unknown"},
)
assert response.status_code == 422
with pytest.raises(ValidationError):
SimulateRequest.model_validate({"comparison_mode": "unknown"})
def test_app_registers_expected_routes():
route_methods = {
route.path: getattr(route, "methods", set())
for route in app.routes
}
assert "GET" in route_methods["/api/health"]
assert "POST" in route_methods["/api/simulate"]