"""마이그레이션 테스트 — 서버 저장 형식(구조물 문자열만)과 클라이언트 형식 모두 커버. 크로스체크 지적 1(2026-08-16): 확정 저장분은 `{chainage_m, structure}` 문자열뿐이다 (`B05_Profile_UI_Page.ts` confirmRoute 전송부). structureType이 없어도 문자열에서 종류를 알아내야 한다. 2026-09-01 사용자 확정: 정본이 갈린 타입(기슭막이 = `managed_by: pipe_points`)은 구조물 목록이 아니라 관 지점 정본으로 간다. 결과도 두 갈래(`MigrationPlan`)다. """ from B05_Profile.B05_Profile_Structures_Migration import migrate_irregular_stations # ── 서버 저장 형식 (structure 문자열만) ────────────────────────────────────── def test_server_format_gisungmagi_maps_to_revetment(): (item,) = migrate_irregular_stations( [{"chainage_m": 120.0, "structure": "기성막이"}] ).pipe_facilities assert item.facility == "revetment" def test_server_format_escape_route_maps_to_refuge_with_width(): (item,) = migrate_irregular_stations( [{"chainage_m": 300.0, "structure": "대피로 2.5m"}] ).structures assert item.type_id == "refuge" assert item.options["width_m"] == 2.5 def test_server_format_pipe_label_is_skipped(): """배관 라벨(관종 D직경)은 관 정본 소관 — 옮기지 않는다.""" for label in ("파형강관 D800", "흄관 D1000", "배관"): plan = migrate_irregular_stations([{"chainage_m": 50.0, "structure": label}]) assert plan.structures == [] and plan.pipe_facilities == [] def test_server_format_unknown_text_becomes_etc(): (item,) = migrate_irregular_stations( [{"chainage_m": 15.0, "structure": "돌망태 보강"}] ).structures assert item.type_id == "etc" assert item.options["name"] == "돌망태 보강" # ── 클라이언트 형식 (structureType 포함) ──────────────────────────────────── def test_client_format_pipe_entries_are_skipped(): plan = migrate_irregular_stations( [ {"chainage_m": 50.0, "structure": "파형강관 D800", "structureType": "배관"}, {"chainage_m": 60.0, "structure": "배관", "origin": "pipe"}, ] ) assert plan.structures == [] and plan.pipe_facilities == [] def test_client_format_gisungmagi_maps_to_revetment(): """기슭막이는 관 시설 — 구간·제원은 레지스트리 기본값을 승계한다(2026-09-01).""" plan = migrate_irregular_stations( [{"chainage_m": 120.0, "structure": "기성막이", "structureType": "기성막이"}] ) assert plan.structures == [] (item,) = plan.pipe_facilities assert item.facility == "revetment" assert item.chainage_m == 120.0 # 기준측점 전/후 5m — 레지스트리 기본값. assert (item.start_m, item.end_m) == (115.0, 125.0) assert item.options["form"] == "돌쌓기(메)" assert item.options["height_m"] == 2.5 and item.options["length_m"] == 10 def test_client_format_escape_route_width_field_wins(): (item,) = migrate_irregular_stations( [ { "chainage_m": 300.0, "structure": "대피로 2.5m", "structureType": "대피로", "escapeWidthM": 3.0, } ] ).structures assert item.type_id == "refuge" assert item.options["width_m"] == 3.0 # 명시 필드가 라벨 파싱보다 우선 def test_etc_keeps_custom_name(): (item,) = migrate_irregular_stations( [ { "chainage_m": 80.0, "structure": "임시 표지", "structureType": "기타", "customName": "임시 표지", } ] ).structures assert item.type_id == "etc" assert item.options["name"] == "임시 표지" def test_duplicate_positions_are_deduplicated(): entries = [ {"chainage_m": 100.0, "structure": "기성막이"}, {"chainage_m": 100.0, "structure": "기성막이"}, ] assert len(migrate_irregular_stations(entries).pipe_facilities) == 1 def test_migration_is_idempotent(): entries = [ {"chainage_m": 10.0, "structure": "기성막이"}, {"chainage_m": 20.0, "structure": "대피로 2.0m"}, ] first = migrate_irregular_stations(entries) second = migrate_irregular_stations(entries) assert [(i.type_id, i.start_m) for i in first.structures] == [ (i.type_id, i.start_m) for i in second.structures ] assert [(p.facility, p.start_m) for p in first.pipe_facilities] == [ (p.facility, p.start_m) for p in second.pipe_facilities ] def test_empty_input_returns_empty(): plan = migrate_irregular_stations([]) assert plan.structures == [] and plan.pipe_facilities == [] # ── 다른 정본이 관리하는 A군 라벨 (2026-08-28) ────────────────────────────── def test_group_a_labels_from_server_are_not_re_migrated(): """서버가 종단 정본에 심는 A군 라벨은 구조물로 옮기지 않는다. 옮기면 정본이 이중화되어 "기타" 고스트가 쌓인다 — 실증: 초기화 전 프로젝트의 structures.json에 `etc@149.73`(세월교)·`etc@200.92`(BOX암거)가 남아 있었다. """ entries = [ {"chainage_m": 149.73, "structure": "세월교"}, {"chainage_m": 200.92, "structure": "BOX암거"}, {"chainage_m": 210.0, "structure": "물넘이포장"}, {"chainage_m": 30.0, "structure": "배수관"}, {"chainage_m": 60.0, "structure": "노출형 횡단수로"}, {"chainage_m": 70.0, "structure": "개거(겉도랑)"}, ] plan = migrate_irregular_stations(entries) assert plan.structures == [] and plan.pipe_facilities == [] def test_pipe_owned_label_is_not_re_migrated(): """관 시설에서 투영된 "기슭막이" 라벨은 되옮기지 않는다 — 되옮기면 "기타"로 굳는다. 구 이름("기성막이")만 이관 대상이다(2026-08-28 이관 뒤 라벨이 갈렸다). """ plan = migrate_irregular_stations([{"chainage_m": 120.0, "structure": "기슭막이"}]) assert plan.structures == [] and plan.pipe_facilities == [] def test_non_group_a_labels_still_migrate(): """대조군 — 필터가 과하게 넓어지면 이 세 건이 함께 사라진다.""" (revetment,) = migrate_irregular_stations( [{"chainage_m": 120.0, "structure": "기성막이"}] ).pipe_facilities assert revetment.facility == "revetment" (refuge,) = migrate_irregular_stations( [{"chainage_m": 300.0, "structure": "대피로 2.0m"}] ).structures assert refuge.type_id == "refuge" (etc,) = migrate_irregular_stations( [{"chainage_m": 15.0, "structure": "돌망태 보강"}] ).structures assert etc.type_id == "etc"