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
39 lines
1.2 KiB
Python
39 lines
1.2 KiB
Python
"""Tests for the backend profile skeleton (D2)."""
|
|
|
|
import pytest
|
|
|
|
from portfolio_optimiser.backends import (
|
|
AzureFoundryBackend,
|
|
ChatBackend,
|
|
LocalBackend,
|
|
Profile,
|
|
get_backend,
|
|
)
|
|
|
|
|
|
def test_get_backend_by_string() -> None:
|
|
assert isinstance(get_backend("azure"), AzureFoundryBackend)
|
|
assert isinstance(get_backend("local"), LocalBackend)
|
|
|
|
|
|
def test_get_backend_by_enum() -> None:
|
|
assert get_backend(Profile.AZURE).profile is Profile.AZURE
|
|
assert get_backend(Profile.LOCAL).profile is Profile.LOCAL
|
|
|
|
|
|
def test_backends_satisfy_seam() -> None:
|
|
# Structural conformance to the ChatBackend Protocol (the D2 seam).
|
|
assert isinstance(get_backend("azure"), ChatBackend)
|
|
assert isinstance(get_backend("local"), ChatBackend)
|
|
|
|
|
|
def test_unknown_profile_fails_fast() -> None:
|
|
with pytest.raises(ValueError):
|
|
get_backend("on-prem")
|
|
|
|
|
|
@pytest.mark.parametrize("profile", ["azure", "local"])
|
|
def test_create_chat_client_is_skeleton(profile: str) -> None:
|
|
# Fase 0: the seam is defined but not wired — must fail loudly, not silently.
|
|
with pytest.raises(NotImplementedError):
|
|
get_backend(profile).create_chat_client(model="dummy-model")
|