Files
agent-desktop/tests/integration/test_providers.py
T

128 lines
4.0 KiB
Python

# -*- coding: utf-8 -*-
"""Integration tests for LLM provider/model APIs."""
from __future__ import annotations
import pytest
from helpers import default_http_timeout
_PROVIDERS_HTTP_TIMEOUT = default_http_timeout(15.0)
@pytest.mark.integration
@pytest.mark.p0
def test_providers_list_returns_baseline_providers(app_server) -> None:
"""Test purpose:
- Verify GET /api/models returns a non-empty list of providers so the
console can render the model picker and agents can attach a model.
A failure here means users cannot pick any LLM, which makes the
product unusable.
Test flow:
1. GET /api/models.
2. Assert 200 status and that the response is a non-empty list whose
items expose ``id`` and ``name`` fields.
API endpoints:
- GET /api/models
"""
resp = app_server.api_request(
"GET",
"/api/models",
timeout=_PROVIDERS_HTTP_TIMEOUT,
)
assert resp.status_code == 200, app_server.logs_tail()
payload = resp.json()
assert isinstance(payload, list)
assert len(payload) > 0
for provider in payload:
assert isinstance(provider.get("id"), str) and provider["id"]
assert isinstance(provider.get("name"), str) and provider["name"]
@pytest.mark.integration
@pytest.mark.p2
def test_active_model_get_global_scope_contract(app_server) -> None:
"""Test purpose:
- Verify GET /api/models/active?scope=global returns the
ActiveModelsInfo contract (object with ``active_llm`` field) even
when no model has been activated yet.
Test flow:
1. GET /api/models/active?scope=global.
2. Assert 200 and the response is a JSON object containing the
``active_llm`` key (value may be null on a fresh workspace).
API endpoints:
- GET /api/models/active
"""
resp = app_server.api_request(
"GET",
"/api/models/active",
params={"scope": "global"},
timeout=_PROVIDERS_HTTP_TIMEOUT,
)
assert resp.status_code == 200, app_server.logs_tail()
payload = resp.json()
assert isinstance(payload, dict)
assert "active_llm" in payload
@pytest.mark.integration
@pytest.mark.p2
def test_set_active_model_rejects_unknown_provider_404(app_server) -> None:
"""Test purpose:
- Verify PUT /api/models/active rejects activation requests pointing
to a non-existent provider with 404 instead of corrupting state.
Test flow:
1. PUT /api/models/active with scope=global and an unknown provider_id.
2. Assert 404 response and a ``detail`` mentioning the missing provider.
API endpoints:
- PUT /api/models/active
"""
resp = app_server.api_request(
"PUT",
"/api/models/active",
json={
"provider_id": "integ_unknown_provider_xyz",
"model": "integ_model_zzz",
"scope": "global",
},
timeout=_PROVIDERS_HTTP_TIMEOUT,
)
assert resp.status_code == 404, app_server.logs_tail()
detail = resp.json().get("detail", "")
assert (
"integ_unknown_provider_xyz" in detail or "not found" in detail.lower()
)
@pytest.mark.integration
@pytest.mark.p0
def test_active_model_get_effective_scope_contract(app_server) -> None:
"""Test purpose:
- Verify GET /api/models/active (default scope=effective) returns the
ActiveModelsInfo contract. Console calls this on every page load
to render the current-model selector; a regression makes the whole
Console appear broken from a user's perspective.
Test flow:
1. GET /api/models/active without scope query param (router default
is ``effective``).
2. Assert 200 and the response is a JSON object with the
``active_llm`` key (value may be null on a fresh workspace).
API endpoints:
- GET /api/models/active
"""
resp = app_server.api_request(
"GET",
"/api/models/active",
timeout=_PROVIDERS_HTTP_TIMEOUT,
)
assert resp.status_code == 200, app_server.logs_tail()
payload = resp.json()
assert isinstance(payload, dict)
assert "active_llm" in payload