import asyncio from datetime import date import pytest from fastapi import HTTPException from app.api.v1.endpoints import project_data def test_element_gis_properties_endpoint_returns_typed_groups(monkeypatch): async def list_element_groups(conn, element_type, element_id): assert conn is connection assert element_type == "pipes" assert element_id == "DEMO-P001" return [ { "dataset_key": "fx_pipe_inventory_20200811", "dataset_title": "奉贤 DN100 及以上管网台账", "source_date": date(2020, 8, 11), "layer_key": "fx_20200811_dn100_pipes", "layer_name": "DN100 及以上管线台账", "source_fid": 117, "match_method": "node_pair", "match_distance_m": 0, "attributes": {"PIPE_D": 300}, } ] connection = object() monkeypatch.setattr( project_data.GisAttributeRepository, "list_element_groups", list_element_groups, ) result = asyncio.run( project_data.get_element_gis_properties( element_type="pipes", element_id=" DEMO-P001 ", conn=connection, ) ) assert result.element_id == "DEMO-P001" assert result.groups[0].attributes == {"PIPE_D": 300} def test_element_gis_properties_endpoint_rejects_blank_id(): with pytest.raises(HTTPException) as failure: asyncio.run( project_data.get_element_gis_properties( element_type="pipes", element_id=" ", conn=object(), ) ) assert failure.value.status_code == 422 def test_source_gis_feature_endpoint_returns_direct_source_group(monkeypatch): async def get_source_feature_group(conn, layer_key, source_fid): assert conn is connection assert layer_key == "fx_20200423_flow_meters" assert source_fid == 17 return { "dataset_key": "fx_water_assets_20200423", "dataset_title": "奉贤全网除头桥 GIS 数据导出", "source_date": date(2020, 4, 23), "layer_key": layer_key, "layer_name": "流量仪", "source_fid": source_fid, "match_method": "source", "match_distance_m": None, "attributes": {"流量仪ID编码": "FM-17"}, } connection = object() monkeypatch.setattr( project_data.GisAttributeRepository, "get_source_feature_group", get_source_feature_group, ) result = asyncio.run( project_data.get_source_gis_feature_properties( layer_key=" fx_20200423_flow_meters ", source_fid=17, conn=connection, ) ) assert result.layer_key == "fx_20200423_flow_meters" assert result.group.match_method == "source" assert result.group.attributes == {"流量仪ID编码": "FM-17"} def test_analysis_run_endpoint_forwards_page_bounds(monkeypatch): captured = {} async def list_runs(conn, *, limit, offset): captured.update(conn=conn, limit=limit, offset=offset) return [{"run_id": "run-1"}] connection = object() monkeypatch.setattr(project_data.AnalysisRepository, "list_runs", list_runs) result = asyncio.run( project_data.get_analysis_runs(limit=25, offset=50, conn=connection) ) assert captured == {"conn": connection, "limit": 25, "offset": 50} assert result == { "success": True, "data": [{"run_id": "run-1"}], "count": 1, "limit": 25, "offset": 50, } def test_analysis_endpoints_do_not_return_database_exception_text(monkeypatch): async def unavailable(*_args, **_kwargs): raise RuntimeError("postgresql://user:secret@database/internal") monkeypatch.setattr(project_data.AnalysisRepository, "list_runs", unavailable) with pytest.raises(HTTPException) as failure: asyncio.run( project_data.get_analysis_runs(limit=100, offset=0, conn=object()) ) assert failure.value.status_code == 500 assert failure.value.detail == "查询分析运行失败" assert "secret" not in failure.value.detail