diff --git a/tests/test_foundry_profile_live.py b/tests/test_foundry_profile_live.py new file mode 100644 index 0000000..a119ebb --- /dev/null +++ b/tests/test_foundry_profile_live.py @@ -0,0 +1,27 @@ +"""Step 14 — GATED live Foundry-profile check (trivial response on the cheapest model, D6). + +NOT default CI: skips cleanly without a configured Foundry endpoint + deployment. When set, a +trivial agent responds on a real FoundryChatClient (cheapest model, hard-capped per D6). +""" + +import os + +import pytest +from agent_framework import Agent + +from portfolio_optimiser.backends import AzureFoundryBackend + +_ENDPOINT = os.environ.get("PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT") +_DEPLOYMENT = os.environ.get("PORTFOLIO_FOUNDRY_DEPLOYMENT") +_NO_FOUNDRY = not (_ENDPOINT and _DEPLOYMENT) + + +@pytest.mark.skipif( + _NO_FOUNDRY, + reason="Foundry not configured (set PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT + PORTFOLIO_FOUNDRY_DEPLOYMENT)", +) +async def test_foundry_profile_trivial_response() -> None: + client = AzureFoundryBackend().create_chat_client(model=_DEPLOYMENT) # type: ignore[arg-type] + agent = Agent(client, "Answer in exactly one word.", name="probe") + result = await agent.run("Reply with the single word OK.") + assert getattr(result, "text", "") # a trivial response came back diff --git a/tests/test_local_profile_live.py b/tests/test_local_profile_live.py new file mode 100644 index 0000000..4534d9d --- /dev/null +++ b/tests/test_local_profile_live.py @@ -0,0 +1,39 @@ +"""Step 14 — GATED live local-profile check (real UsageDetails + loopback-only no-egress). + +NOT default CI: it skips cleanly without a configured LOCAL endpoint. When the env IS set +(and an OpenAI-compatible server such as Ollama is running on loopback), it proves what +FakeChatClient cannot — a REAL OpenAIChatCompletionClient non-streaming call returns a +response with a load-bearing ``total_token_count > 0`` — and asserts the endpoint is loopback +(no-egress: the client only ever talks to the configured ``base_url``, never api.openai.com). +""" + +import os + +import pytest +from agent_framework import Message + +from portfolio_optimiser.backends import LocalBackend + +_BASE_URL = os.environ.get("PORTFOLIO_LOCAL_BASE_URL") +_MODEL = os.environ.get("PORTFOLIO_LOCAL_MODEL") +_NO_LOCAL = not (_BASE_URL and _MODEL) + + +@pytest.mark.skipif( + _NO_LOCAL, + reason="LOCAL endpoint not configured (set PORTFOLIO_LOCAL_BASE_URL + PORTFOLIO_LOCAL_MODEL)", +) +async def test_local_profile_real_usage_and_loopback_only() -> None: + # No-egress: the configured live endpoint MUST be loopback (never a remote host). + assert _BASE_URL is not None + assert "127.0.0.1" in _BASE_URL or "localhost" in _BASE_URL, ( + "live local endpoint must be loopback (no egress)" + ) + client = LocalBackend().create_chat_client(model=_MODEL) # type: ignore[arg-type] + reply = await client.get_response( + [Message(role="user", contents=["Reply with the single word OK."])] + ) + assert reply.text # a real response came back + usage = reply.usage_details + # The criterion FakeChatClient cannot prove: real, positive, load-bearing usage. + assert usage is not None and (usage.get("total_token_count") or 0) > 0