The origin check is an exact match including the context path, so users had to type "https://collab.lge.com/main" precisely. GET /api/v1/config now returns the approved origins in canonical form (non-secret: they are the only destinations the backend will talk to), and the UI swaps the URL text field for a select listing them, keeping the element id, focus handling and the Test connection flow unchanged. The text field remains the fallback when the fetch fails. Backend validation of the submitted URL is untouched. Mock server serves the endpoint; contract, API and e2e tests cover it.
57 lines
2.4 KiB
Python
57 lines
2.4 KiB
Python
"""API tests for GET /api/v1/config: approved origins offered to the UI as a fixed choice."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import httpx
|
|
import pytest
|
|
from httpx import ASGITransport
|
|
|
|
from backend.app import create_app
|
|
from backend.artifacts import ArtifactStore
|
|
from backend.containers import FakeContainerManager
|
|
from backend.dev.fake_peer import ScriptedContainerPeer
|
|
from backend.model import FakeModelAdapter
|
|
from backend.settings import Settings
|
|
|
|
from tests.backend.conftest import make_test_confluence_client_factory
|
|
|
|
|
|
def build_app(tmp_path: Path, origins: list[str]):
|
|
settings = Settings(approved_confluence_origins=origins)
|
|
return create_app(
|
|
settings=settings,
|
|
container_manager=FakeContainerManager(lambda: ScriptedContainerPeer(scenario="standard")),
|
|
artifact_store=ArtifactStore(tmp_path / "artifacts"),
|
|
model_adapter=FakeModelAdapter(),
|
|
confluence_client_factory=make_test_confluence_client_factory(),
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_config_lists_canonical_approved_origins(tmp_path: Path):
|
|
app = build_app(tmp_path, ["https://Collab.Example.com/main/", "https://approved.example.com", "https://approved.example.com/"])
|
|
async with httpx.AsyncClient(transport=ASGITransport(app=app), base_url="http://testserver") as client:
|
|
resp = await client.get("/api/v1/config")
|
|
assert resp.status_code == 200
|
|
assert resp.headers.get("Cache-Control") == "no-store"
|
|
assert resp.json() == {"approved_origins": ["https://collab.example.com/main", "https://approved.example.com"]}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_config_needs_no_session_or_origin_header(tmp_path: Path):
|
|
app = build_app(tmp_path, ["https://approved.example.com"])
|
|
async with httpx.AsyncClient(transport=ASGITransport(app=app), base_url="http://testserver") as client:
|
|
resp = await client.get("/api/v1/config")
|
|
assert resp.status_code == 200
|
|
assert "set-cookie" not in resp.headers
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_config_rejects_other_methods(tmp_path: Path):
|
|
app = build_app(tmp_path, ["https://approved.example.com"])
|
|
async with httpx.AsyncClient(transport=ASGITransport(app=app), base_url="http://testserver") as client:
|
|
resp = await client.post("/api/v1/config", json={}, headers={"Origin": "http://testserver"})
|
|
assert resp.status_code == 405
|