spinor-os / tests /test_real_functionality.py
luguog's picture
Deploy spinor-os as static HF Space receiver
bf3cbf1 verified
Raw
History Blame Contribute Delete
10.3 kB
"""Integration tests for real functionality: persistence, CSV import, and statistical attribution."""
from __future__ import annotations
import os
import tempfile
from io import StringIO
from pathlib import Path
import pytest
from spinor_os import ExperimentationOS, PersistenceManager, import_events_csv
from spinor_os.config import AttributionMethod, EventType
def test_csv_import_allocates_missions_and_records_events():
os = ExperimentationOS()
os.register_employee("import-001", "field-scientist", "northeast")
hypothesis = os.propose_hypothesis(
statement="Imported outreach increases appointments.",
causal_claim="Tailored outreach causes higher appointment rate.",
predicted_effect={
"metric": "appointment_rate",
"direction": "increase",
"magnitude": 0.1,
"unit": "percentage_point",
"timing": "7d",
},
employee_owner="import-001",
falsification_criteria=["No increase observed."],
customer_segment="enterprise",
territory="northeast",
modification="imported_outreach",
)
experiment = os.start_experiment(hypothesis.hypothesis_id)
csv_text = f"""experiment_id,actor_id,event_type,outcome_value,metric,execution_quality,actor_role
{experiment.experiment_id},import-001,OUTCOME_OBSERVED,0.15,appointment_rate,0.88,field-scientist
{experiment.experiment_id},import-001,OUTCOME_OBSERVED,0.18,appointment_rate,0.90,field-scientist
"""
summary = import_events_csv(os, StringIO(csv_text), experiment_id=experiment.experiment_id)
assert summary["rows_read"] == 2
assert summary["rows_ingested"] == 2
assert len(summary["event_ids"]) == 2
assert not summary["errors"]
assert len(os.events) == 2
def test_real_attribution_ols_and_diff_in_diff():
os = ExperimentationOS()
os.register_employee("alice", "field-scientist", "northeast")
os.register_employee("bob", "field-scientist", "southwest")
hypothesis = os.propose_hypothesis(
statement="X lifts Y in enterprise segment",
causal_claim="X causes Y",
predicted_effect={
"metric": "y_rate",
"direction": "increase",
"magnitude": 0.1,
"unit": "pp",
"timing": "7d",
},
employee_owner="alice",
falsification_criteria=["no effect"],
customer_segment="enterprise",
territory="northeast",
modification="treatment_a",
)
treat = os.start_experiment(hypothesis.hypothesis_id)
control = os.start_experiment(hypothesis.hypothesis_id)
m_t = os.allocate_mission(
treat.experiment_id,
"alice",
[
{
"hypothesis_id": hypothesis.hypothesis_id,
"modification": "treatment_a",
"customer_segment": "enterprise",
"territory": "northeast",
"timing": "2026-W31",
"resource_allocation": 1.0,
}
],
)
m_c = os.allocate_mission(
control.experiment_id,
"bob",
[
{
"hypothesis_id": hypothesis.hypothesis_id,
"modification": "control",
"customer_segment": "enterprise",
"territory": "southwest",
"timing": "2026-W31",
"resource_allocation": 1.0,
}
],
)
os.record_event(
treat.experiment_id,
m_t.mission_id,
EventType.OUTCOME_OBSERVED,
"alice",
outcome_value=0.05,
metric="y_rate",
execution_quality=0.9,
)
os.record_event(
control.experiment_id,
m_c.mission_id,
EventType.OUTCOME_OBSERVED,
"bob",
outcome_value=0.07,
metric="y_rate",
execution_quality=0.9,
)
os.record_event(
treat.experiment_id,
m_t.mission_id,
EventType.OUTCOME_OBSERVED,
"alice",
outcome_value=0.35,
metric="y_rate",
execution_quality=0.9,
)
os.record_event(
control.experiment_id,
m_c.mission_id,
EventType.OUTCOME_OBSERVED,
"bob",
outcome_value=0.07,
metric="y_rate",
execution_quality=0.85,
)
claim_ols = os.attribute(
treat.experiment_id,
"y_rate",
0.35,
0.05,
AttributionMethod.OLS,
0.95,
falsification_survived=True,
)
assert claim_ols.real_attribution is not None
assert claim_ols.real_attribution["method"] == "ols_regression"
assert claim_ols.real_attribution["estimated_effect"] > 0
assert claim_ols.real_attribution["n_observations"] >= 4
claim_did = os.attribute(
treat.experiment_id,
"y_rate",
0.35,
0.05,
AttributionMethod.DIFF_IN_DIFF,
0.95,
falsification_survived=True,
)
assert claim_did.real_attribution is not None
assert claim_did.real_attribution["method"] == "difference_in_differences"
assert claim_did.real_attribution["estimated_effect"] > 0
assert claim_did.real_attribution["n_observations"] >= 4
def test_persistence_reloads_imported_data():
with tempfile.TemporaryDirectory() as tmp:
db = Path(tmp) / "spinor_real.sqlite"
os1 = ExperimentationOS(persistence=PersistenceManager(db))
os1.register_employee("persist-001", "field-scientist", "northeast")
hypothesis = os1.propose_hypothesis(
statement="Persistent outreach works.",
causal_claim="Outreach causes lift.",
predicted_effect={
"metric": "lift",
"direction": "increase",
"magnitude": 0.1,
"unit": "pp",
"timing": "7d",
},
employee_owner="persist-001",
falsification_criteria=["no lift"],
customer_segment="enterprise",
territory="northeast",
modification="outreach",
)
experiment = os1.start_experiment(hypothesis.hypothesis_id)
csv_text = f"""experiment_id,actor_id,event_type,outcome_value,metric,execution_quality
{experiment.experiment_id},persist-001,OUTCOME_OBSERVED,0.12,lift,0.88
"""
summary = import_events_csv(os1, StringIO(csv_text), experiment_id=experiment.experiment_id)
assert summary["rows_ingested"] == 1
# Reopen from the same SQLite file.
os2 = ExperimentationOS(persistence=PersistenceManager(db))
assert "persist-001" in os2.employees
assert experiment.experiment_id in os2.experiments
assert len(os2.events) == 1
def test_server_import_and_real_attribution(client):
"""Live-server style verification using the FastAPI TestClient."""
# Register employee and hypothesis.
r = client.post("/employees", json={
"employee_id": "real-api-001",
"role": "field-scientist",
"territory": "northeast",
})
assert r.status_code == 200
r = client.post("/hypotheses", json={
"statement": "Real outreach increases conversions",
"causal_claim": "Outreach causes conversions",
"predicted_effect": {
"metric": "real_conversion_rate",
"direction": "increase",
"magnitude": 0.1,
"unit": "pp",
"timing": "7d",
},
"employee_owner": "real-api-001",
"falsification_criteria": ["No lift"],
"customer_segment": "enterprise",
"territory": "northeast",
"modification": "real_outreach",
})
assert r.status_code == 200
hypothesis_id = r.json()["hypothesis_id"]
# Create treatment and control experiments.
treat = client.post("/experiments", json={"hypothesis_id": hypothesis_id}).json()
control = client.post("/experiments", json={"hypothesis_id": hypothesis_id}).json()
treat_id = treat["experiment_id"]
control_id = control["experiment_id"]
# Allocate missions.
m_t = client.post(f"/experiments/{treat_id}/missions", json={
"employee_id": "real-api-001",
"candidates": [{
"hypothesis_id": hypothesis_id,
"modification": "real_outreach",
"customer_segment": "enterprise",
"territory": "northeast",
"timing": "2026-W31",
"resource_allocation": 1.0,
}],
}).json()
m_c = client.post(f"/experiments/{control_id}/missions", json={
"employee_id": "real-api-001",
"candidates": [{
"hypothesis_id": hypothesis_id,
"modification": "control",
"customer_segment": "enterprise",
"territory": "northeast",
"timing": "2026-W31",
"resource_allocation": 1.0,
}],
}).json()
# Record pre and post outcomes.
for (exp, mid, val, eq) in [
(treat_id, m_t["mission_id"], 0.05, 0.9),
(control_id, m_c["mission_id"], 0.06, 0.9),
(treat_id, m_t["mission_id"], 0.35, 0.9),
(control_id, m_c["mission_id"], 0.07, 0.85),
]:
r = client.post(f"/experiments/{exp}/events", json={
"mission_id": mid,
"event_type": "outcome_observed",
"actor_id": "real-api-001",
"outcome_value": val,
"metric": "real_conversion_rate",
"execution_quality": eq,
})
assert r.status_code == 200
# Compute real OLS attribution.
r = client.post(f"/experiments/{treat_id}/attributes", json={
"outcome_metric": "real_conversion_rate",
"outcome_value": 0.35,
"counterfactual_estimate": 0.05,
"method": "ols",
"confidence": 0.95,
"falsification_survived": True,
})
assert r.status_code == 200
data = r.json()
assert data["real_attribution"] is not None
assert data["real_attribution"]["method"] == "ols_regression"
assert data["real_attribution"]["estimated_effect"] > 0
# Force a save and check persistence status.
r = client.post("/persistence/save")
assert r.status_code == 200
r = client.get("/persistence/status")
assert r.status_code == 200
status = r.json()
assert status["persistence_enabled"] is True
assert status["experiments"] >= 2