idea39-vizforge-interactive.../tests/test_vizforge.py

106 lines
3.0 KiB
Python

from __future__ import annotations
from pathlib import Path
from idea39_vizforge_interactive_economic.artifacts import export_artifact, generate_investor_artifact
from idea39_vizforge_interactive_economic.dsl import dump_business_model, load_business_model, parse_business_model
from idea39_vizforge_interactive_economic.models import BusinessModel
from idea39_vizforge_interactive_economic.simulation import compare_scenarios, sensitivity_analysis, simulate_business_model
def sample_yaml() -> str:
return """
name: Acme AI
horizon_months: 12
base_currency: USD
starting_cash: 250000
macro:
gdp_growth: 0.02
inflation: 0.03
interest_rate: 0.05
consumer_confidence: 102
funding_environment: 0.68
regime_switch_probability: 0.05
fx_rates:
USD: 1.0
revenue_streams:
- name: core
starting_customers: 100
price_per_customer: 120
monthly_growth_rate: 0.06
monthly_churn: 0.04
gross_margin: 0.82
acquisition_rate: 15
macro_sensitivity: 0.4
costs:
- name: cloud
monthly_amount: 10000
inflation_sensitivity: 0.5
macro_sensitivity: 0.1
hires:
- month: 2
role: engineer
annual_salary: 180000
count: 1
capex:
- month: 3
amount: 25000
description: hardware
financing:
- month: 0
instrument: equity
amount: 150000
valuation: 3000000
- month: 6
instrument: option_pool
option_pool_percent: 0.08
scenarios:
- name: recession
probability: 0.2
demand_multiplier: 0.82
cac_multiplier: 1.25
churn_delta: 0.03
cost_inflation_delta: 0.02
funding_environment_delta: -0.2
interest_rate_delta: 0.02
duration_months: 4
regime_bias: -0.2
""".strip()
def test_parse_roundtrip_and_digest() -> None:
model, digest = parse_business_model(sample_yaml())
assert model.name == "Acme AI"
assert len(digest) == 64
dumped = dump_business_model(model)
loaded = load_business_model(dumped)
assert loaded.name == model.name
def test_simulation_and_scenario_comparison() -> None:
model = load_business_model(sample_yaml())
base = simulate_business_model(model, n_sims=64, seed=11)
assert base.revenue_paths.shape == (64, 12)
assert base.cash_paths.shape == (64, 13)
assert base.summary()["runway_p50"] >= 0
recession = model.scenarios[0]
results = compare_scenarios(model, [recession], n_sims=32, seed=12)
assert results[0].scenario_name == "recession"
assert results[0].runway_months.shape == (32,)
def test_sensitivity_and_artifacts(tmp_path: Path) -> None:
model = load_business_model(sample_yaml())
rows = sensitivity_analysis(model, "macro.inflation", [0.02, 0.05], n_sims=24, seed=3)
assert len(rows) == 2
assert rows[0]["value"] == 0.02
bundle = generate_investor_artifact(model, n_sims=32, seed=4)
assert "VizForge Investor Scenario Brief" in bundle.markdown
assert "cash_runway.svg" in bundle.charts
export_artifact(bundle, tmp_path)
assert (tmp_path / "report.md").exists()
assert (tmp_path / "cash_runway.svg").exists()