Add project-bound OAuth MCP queries and controlled analysis with scoped evidence, limits, timeouts and a nearby-model spatial query using EPSG:4326 inputs. Default nearby searches to 200 meters and five results. Integrate investigation messaging/presentation, health/readiness and typed history; remove duplicate web search and fix online simulation and compressed baseline writes. Include project migrations 023-025; keep local credentials and demo seeding excluded. Validation: 319 focused pytest checks; real PostGIS temporary-table regression; read-only Jinqian bridge radius comparison; cross-language MCP discovery/calls.
35 lines
1.7 KiB
Python
35 lines
1.7 KiB
Python
from unittest.mock import AsyncMock
|
|
from uuid import uuid4
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.api.v1.rest_router import api_router
|
|
from app.api.v1.endpoints.timeseries import composite
|
|
from app.auth.project_dependencies import ProjectContext, get_project_context
|
|
from app.domain.schemas.timeseries_history import ElementHistoryResponse
|
|
|
|
|
|
def test_viewer_can_query_history_without_scada_clean_permission(monkeypatch):
|
|
project_id = uuid4()
|
|
context = ProjectContext(project_id=project_id, project_code="test", user_id=uuid4(), project_role="viewer")
|
|
app = FastAPI()
|
|
app.include_router(api_router, prefix="/api/v1")
|
|
app.dependency_overrides[get_project_context] = lambda: context
|
|
app.dependency_overrides[composite.get_timescale_connection] = lambda: object()
|
|
app.dependency_overrides[composite.get_postgres_connection] = lambda: object()
|
|
query = AsyncMock(return_value=ElementHistoryResponse(series=[]))
|
|
monkeypatch.setattr(composite.TimeseriesHistoryService, "query", query)
|
|
with TestClient(app) as client:
|
|
headers = {"X-Project-Id": str(project_id)}
|
|
response = client.post("/api/v1/timeseries/views/element-history/query", headers=headers, json={
|
|
"start_time": "2026-09-01T00:00:00Z", "end_time": "2026-09-01T01:00:00Z",
|
|
"elements": [{"element_type": "junction", "element_id": "J1"}],
|
|
})
|
|
assert response.status_code == 200
|
|
assert response.json() == {"series": []}
|
|
assert query.await_count == 1
|
|
write_response = client.post("/api/v1/timeseries/scada-cleaning-runs", headers=headers, json={})
|
|
assert write_response.status_code == 403
|
|
assert write_response.json()["detail"]["permission"] == "scada.clean"
|