portfolio-optimiser/tests/test_reference_domain.py
Kjell Tore Guttormsen b57aa83a30 feat(fase0): synthetic reference domain (D4) + backend profile skeleton (D2)
Completes Fase 0 (skeleton & decision-lock):

- reference_domain.py + data/reference_projects.json: a synthetic
  "anleggskostnad" portfolio (3 fictional construction-cost projects with
  cost line items) as the framework's bundled reference input. Plain typed
  loader (frozen dataclasses); the JSON-Schema data-source *contract* (B5)
  is deliberately deferred to Fase 2.
- backends.py: Profile (azure|local) + ChatBackend Protocol seam +
  AzureFoundryBackend/LocalBackend stubs + get_backend() selector
  (fail-fast on unknown profile). Empty skeleton per D2 — create_chat_client
  raises NotImplementedError until live wiring in Fase 1. Return type is the
  MAF BaseChatClient (the common base of FoundryChatClient/OpenAIChatClient).

Quality gate green: ruff format + check, mypy (src) clean, 12 pytest passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9FyyENxebxVThjrn9et8C
2026-06-23 22:38:41 +02:00

38 lines
1.2 KiB
Python

"""Tests for the synthetic reference domain (D4)."""
import pytest
from portfolio_optimiser.reference_domain import CostItem, Project, load_reference_projects
@pytest.fixture(scope="module")
def projects() -> tuple[Project, ...]:
return load_reference_projects()
def test_loads_non_empty(projects: tuple[Project, ...]) -> None:
assert len(projects) >= 1
def test_project_ids_unique(projects: tuple[Project, ...]) -> None:
ids = [p.id for p in projects]
assert len(ids) == len(set(ids))
def test_every_project_well_formed(projects: tuple[Project, ...]) -> None:
for p in projects:
assert p.id and p.name and p.description
assert p.currency == "NOK"
assert len(p.cost_items) >= 1
def test_cost_item_total_is_quantity_times_unit_cost() -> None:
item = CostItem(code="X", description="d", quantity=4.0, unit="m2", unit_cost=215.0)
assert item.total_cost == pytest.approx(860.0)
def test_project_total_is_sum_of_items(projects: tuple[Project, ...]) -> None:
for p in projects:
expected = sum(item.total_cost for item in p.cost_items)
assert p.total_cost == pytest.approx(expected)
assert p.total_cost > 0