Initial CivilPlan MCP implementation
This commit is contained in:
159
tests/test_v11_tools.py
Normal file
159
tests/test_v11_tools.py
Normal file
@@ -0,0 +1,159 @@
|
||||
from pathlib import Path
|
||||
|
||||
import ezdxf
|
||||
|
||||
from civilplan_mcp.config import Settings
|
||||
from civilplan_mcp.tools.benchmark_validator import validate_against_benchmark
|
||||
from civilplan_mcp.tools.budget_report_generator import generate_budget_report
|
||||
from civilplan_mcp.tools.dxf_generator import generate_dxf_drawing
|
||||
from civilplan_mcp.tools.feasibility_analyzer import analyze_feasibility
|
||||
from civilplan_mcp.tools.land_info_query import (
|
||||
build_land_use_bbox_params,
|
||||
extract_address_result,
|
||||
extract_feature_properties,
|
||||
query_land_info,
|
||||
)
|
||||
from civilplan_mcp.tools.project_parser import parse_project
|
||||
from civilplan_mcp.tools.quantity_estimator import estimate_quantities
|
||||
|
||||
|
||||
def test_query_land_info_returns_graceful_message_without_api_keys() -> None:
|
||||
result = query_land_info(address="경기도 양주시 덕계동 123", pnu=None)
|
||||
|
||||
assert result["status"] == "disabled"
|
||||
|
||||
|
||||
def test_settings_keep_public_data_key_for_nara_usage() -> None:
|
||||
settings = Settings(data_go_kr_api_key="abc")
|
||||
|
||||
assert settings.data_go_kr_api_key == "abc"
|
||||
|
||||
|
||||
def test_extract_address_result_reads_vworld_shape() -> None:
|
||||
payload = {
|
||||
"response": {
|
||||
"status": "OK",
|
||||
"result": {
|
||||
"point": {"x": "127.061", "y": "37.821"},
|
||||
"items": [
|
||||
{
|
||||
"id": "4163010100101230000",
|
||||
"address": {"parcel": "경기도 양주시 덕계동 123"}
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
parsed = extract_address_result(payload)
|
||||
assert parsed["pnu"] == "4163010100101230000"
|
||||
assert parsed["x"] == 127.061
|
||||
|
||||
|
||||
def test_extract_feature_properties_returns_first_feature() -> None:
|
||||
payload = {
|
||||
"response": {
|
||||
"status": "OK",
|
||||
"result": {
|
||||
"featureCollection": {
|
||||
"features": [
|
||||
{"properties": {"pnu": "4163010100101230000", "jimok": "대"}}
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
parsed = extract_feature_properties(payload)
|
||||
assert parsed["jimok"] == "대"
|
||||
|
||||
|
||||
def test_build_land_use_bbox_params_uses_wfs_bbox_pattern() -> None:
|
||||
params = build_land_use_bbox_params(127.0, 37.0, "test-key")
|
||||
|
||||
assert params["SERVICE"] == "WFS"
|
||||
assert params["REQUEST"] == "GetFeature"
|
||||
assert params["TYPENAME"] == "lt_c_lhblpn"
|
||||
assert params["SRSNAME"] == "EPSG:4326"
|
||||
assert params["KEY"] == "test-key"
|
||||
assert params["OUTPUTFORMAT"] == "application/json"
|
||||
assert params["BBOX"] == "126.9995,36.9995,127.0005,37.0005,EPSG:4326"
|
||||
|
||||
|
||||
def test_analyze_feasibility_returns_positive_cost_structure() -> None:
|
||||
result = analyze_feasibility(
|
||||
land_area_m2=600,
|
||||
land_price_per_m2=850000,
|
||||
land_price_multiplier=1.0,
|
||||
construction_cost_total=890000000,
|
||||
other_costs_million=50,
|
||||
revenue_type="임대",
|
||||
building_floor_area_m2=720,
|
||||
sale_price_per_m2=None,
|
||||
monthly_rent_per_m2=16000,
|
||||
vacancy_rate_pct=10.0,
|
||||
operating_expense_pct=20.0,
|
||||
equity_ratio_pct=30.0,
|
||||
loan_rate_pct=5.5,
|
||||
loan_term_years=10,
|
||||
construction_months=24,
|
||||
sale_months=12,
|
||||
)
|
||||
|
||||
assert result["cost_structure"]["total_investment"] > 0
|
||||
assert "irr_pct" in result["returns"]
|
||||
|
||||
|
||||
def test_validate_against_benchmark_includes_bid_warning() -> None:
|
||||
result = validate_against_benchmark(
|
||||
project_type="소로_도로",
|
||||
road_length_m=890,
|
||||
floor_area_m2=None,
|
||||
region="경기도",
|
||||
our_estimate_won=1067000000,
|
||||
)
|
||||
|
||||
assert "낙찰가 ≠ 사업비" in result["bid_rate_reference"]["경고"]
|
||||
|
||||
|
||||
def test_generate_budget_report_creates_docx(tmp_path: Path) -> None:
|
||||
project_data = parse_project(description="복지관 신축 2026~2028 경기도")
|
||||
project_data["output_dir"] = str(tmp_path)
|
||||
result = generate_budget_report(
|
||||
report_type="예산편성요구서",
|
||||
project_data=project_data,
|
||||
boq_summary={"total_cost": 1067000000, "direct_cost": 900422000, "indirect_cost": 166578000},
|
||||
department="도로과",
|
||||
requester="22B Labs",
|
||||
output_filename="budget.docx",
|
||||
)
|
||||
|
||||
assert Path(result["file_path"]).exists()
|
||||
|
||||
|
||||
def test_generate_dxf_drawing_creates_dxf(tmp_path: Path) -> None:
|
||||
project_spec = parse_project(description="소로 신설 L=890m B=6m 경기도")
|
||||
project_spec["output_dir"] = str(tmp_path)
|
||||
quantities = estimate_quantities(
|
||||
road_class="소로",
|
||||
length_m=890,
|
||||
width_m=6.0,
|
||||
terrain="평지",
|
||||
pavement_type="아스콘",
|
||||
include_water_supply=False,
|
||||
include_sewage=False,
|
||||
include_retaining_wall=False,
|
||||
include_bridge=False,
|
||||
bridge_length_m=0.0,
|
||||
)
|
||||
|
||||
result = generate_dxf_drawing(
|
||||
drawing_type="횡단면도",
|
||||
project_spec=project_spec,
|
||||
quantities=quantities,
|
||||
scale="1:200",
|
||||
output_filename="road.dxf",
|
||||
)
|
||||
|
||||
doc = ezdxf.readfile(result["file_path"])
|
||||
assert doc is not None
|
||||
Reference in New Issue
Block a user