"""Phase 2 PoC #3 — transshipment_5stage params 카탈로그 동치성 테스트. 런타임 override 는 후속 PR 에서 활성화되므로, 이 테스트는 **DEFAULT_PARAMS ↔ 모듈 상수 ↔ seed SQL JSONB** 3 자 일치만 검증한다. """ from __future__ import annotations import importlib import json import os import sys import types import unittest # pandas/pydantic_settings stub (다른 phase 2 테스트와 동일 관용) if 'pandas' not in sys.modules: pd_stub = types.ModuleType('pandas') pd_stub.DataFrame = type('DataFrame', (), {}) pd_stub.Timestamp = type('Timestamp', (), {}) sys.modules['pandas'] = pd_stub if 'pydantic_settings' not in sys.modules: stub = types.ModuleType('pydantic_settings') class _S: def __init__(self, **kw): for name, value in self.__class__.__dict__.items(): if name.isupper(): setattr(self, name, kw.get(name, value)) stub.BaseSettings = _S sys.modules['pydantic_settings'] = stub if 'algorithms' not in sys.modules: pkg = types.ModuleType('algorithms') pkg.__path__ = [os.path.join(os.path.dirname(__file__), '..', 'algorithms')] sys.modules['algorithms'] = pkg # fleet_tracker 의 GEAR_PATTERN 을 transshipment.py 상단에서 import 하므로 stub if 'fleet_tracker' not in sys.modules: ft_stub = types.ModuleType('fleet_tracker') import re as _re ft_stub.GEAR_PATTERN = _re.compile(r'^xxx$') sys.modules['fleet_tracker'] = ft_stub ts = importlib.import_module('algorithms.transshipment') class TransshipmentParamsTest(unittest.TestCase): def test_default_values_match_module_constants(self): p = ts.TRANSSHIPMENT_DEFAULT_PARAMS self.assertEqual(p['sog_threshold_kn'], ts.SOG_THRESHOLD_KN) self.assertEqual(p['proximity_deg'], ts.PROXIMITY_DEG) self.assertEqual(p['approach_deg'], ts.APPROACH_DEG) self.assertEqual(p['rendezvous_min'], ts.RENDEZVOUS_MIN) self.assertEqual(p['pair_expiry_min'], ts.PAIR_EXPIRY_MIN) self.assertEqual(p['gap_tolerance_cycles'], ts.GAP_TOLERANCE_CYCLES) self.assertEqual(set(p['fishing_kinds']), set(ts._FISHING_KINDS)) self.assertEqual(set(p['carrier_kinds']), set(ts._CARRIER_KINDS)) self.assertEqual(set(p['excluded_ship_ty']), set(ts._EXCLUDED_SHIP_TY)) self.assertEqual(list(p['carrier_hints']), list(ts._CARRIER_HINTS)) def test_seed_sql_values_match_python_default(self): seed_path = os.path.join( os.path.dirname(__file__), '..', 'models_core', 'seeds', 'v1_transshipment.sql', ) with open(seed_path, 'r', encoding='utf-8') as f: sql = f.read() start = sql.index('$json$') + len('$json$') end = sql.index('$json$', start) raw = sql[start:end].strip() params = json.loads(raw) expected = ts.TRANSSHIPMENT_DEFAULT_PARAMS for scalar_key in ['sog_threshold_kn', 'proximity_deg', 'approach_deg', 'rendezvous_min', 'pair_expiry_min', 'gap_tolerance_cycles', 'min_score']: self.assertEqual(params[scalar_key], expected[scalar_key], scalar_key) for list_key in ['fishing_kinds', 'carrier_kinds', 'excluded_ship_ty', 'carrier_hints']: self.assertEqual(set(params[list_key]), set(expected[list_key]), list_key) if __name__ == '__main__': unittest.main()