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()