from datetime import datetime, timezone from unittest.mock import MagicMock import pytest from openpyxl import load_workbook from app.native.wndb import s42_sensor_placement from app.services import sensor_placement def _mock_project_cursor(monkeypatch): cursor = MagicMock() connection = MagicMock() connection.cursor.return_value.__enter__.return_value = cursor connection_context = MagicMock() connection_context.__enter__.return_value = connection monkeypatch.setattr( s42_sensor_placement, "project_connection", lambda _network: connection_context, ) return cursor def test_build_workbook_contains_engineering_columns(monkeypatch): monkeypatch.setattr( sensor_placement, "_sensor_points", lambda network, locations: [ { "node_id": "J1", "project_x": 13500000.0, "project_y": 3600000.0, "map_x": 13500000.0, "map_y": 3600000.0, "longitude": 121.0, "latitude": 31.0, "elevation": 4.5, } ], ) scheme = { "id": 7, "scheme_name": "北区测压点", "sensor_number": 2, "min_diameter": 300, "username": "alice", "create_time": datetime(2026, 7, 30, tzinfo=timezone.utc), "sensor_location": ["J1", "J2"], } output = sensor_placement.build_sensor_placement_workbook( network="tjwater", scheme=scheme, sensor_location=["J1"], adjustment_status={"J1": "replaced"}, ) workbook = load_workbook(output) assert workbook.sheetnames == ["方案信息", "监测点清单"] headers = [cell.value for cell in workbook["监测点清单"][1]] assert headers == [ "序号", "节点 ID", "经度", "纬度", "工程 X", "工程 Y", "地图 X", "地图 Y", "高程", "调整状态", ] assert workbook["监测点清单"]["J2"].value == "替换" assert workbook["方案信息"]["B8"].value == "未保存草稿" def test_sensor_points_keep_engineering_coordinates_and_transform_map_coordinates( monkeypatch, ): monkeypatch.setattr( sensor_placement.wndb, "get_sensor_placement_nodes", lambda network, node_ids: [ { "node_id": "J1", "project_x": 3038.94, "project_y": -34446.59, "map_x": 13525191.530279, "map_y": 3622984.760237, "elevation": 4.5, } ], ) point = sensor_placement._sensor_points("tjwater", ["J1"])[0] assert point["project_x"] == 3038.94 assert point["project_y"] == -34446.59 assert point["longitude"] == pytest.approx(121.498863, abs=1e-6) assert point["latitude"] == pytest.approx(30.924784, abs=1e-6) def test_update_validates_nodes_before_write(monkeypatch): monkeypatch.setattr( sensor_placement.wndb, "get_sensor_placement_nodes", lambda network, node_ids: [], ) try: sensor_placement.update_sensor_placement_scheme( "tjwater", 7, expected_sensor_location=["J1"], sensor_location=["missing"], ) except sensor_placement.SensorPlacementValidationError as exc: assert "missing" in str(exc) else: raise AssertionError("expected invalid node to be rejected") def test_sensor_nodes_use_materialized_web_mercator_geometry(monkeypatch): cursor = _mock_project_cursor(monkeypatch) cursor.fetchall.return_value = [] s42_sensor_placement.get_sensor_placement_nodes("tjwater", ["J1"]) query = cursor.execute.call_args.args[0] assert "geo_junctions_mat" in query assert "ST_X(c.coord)" in query assert "ST_Y(c.coord)" in query assert "ST_X(gj.geom)" in query assert "ST_Y(gj.geom)" in query def test_workbook_escapes_formula_in_scheme_metadata(monkeypatch): monkeypatch.setattr( sensor_placement, "_sensor_points", lambda network, locations: [ { "node_id": "J1", "project_x": 3038.94, "project_y": -34446.59, "map_x": 13525191.53, "map_y": 3622984.76, "longitude": 121.49, "latitude": 30.92, "elevation": 4.5, } ], ) output = sensor_placement.build_sensor_placement_workbook( network="tjwater", scheme={ "scheme_name": "=1+1", "sensor_location": ["J1"], "min_diameter": 300, "username": "alice", "create_time": datetime(2026, 7, 30, tzinfo=timezone.utc), }, sensor_location=["J1"], adjustment_status={}, ) workbook = load_workbook(output, data_only=False) assert workbook["方案信息"]["B2"].value == "'=1+1" assert workbook["方案信息"]["B2"].data_type == "s" def test_create_sensor_placement_returns_inserted_record(monkeypatch): cursor = _mock_project_cursor(monkeypatch) cursor.fetchone.return_value = {"id": 7, "sensor_location": ["J1", "J2"]} created = s42_sensor_placement.create_sensor_placement( "tjwater", scheme_name="北区测压点", min_diameter=300, username="alice", sensor_location=["J1", "J2"], ) assert created["id"] == 7 query, parameters = cursor.execute.call_args.args assert "INSERT INTO sensor_placement" in query assert parameters == ("北区测压点", 2, 300, "alice", ["J1", "J2"])