""" 자재 분류기 테스트 """ import pytest from unittest.mock import patch class TestPipeClassifier: """파이프 분류기 테스트""" def test_pipe_classification_basic(self): """기본 파이프 분류 테스트""" from app.services.pipe_classifier import classify_pipe # 명확한 파이프 케이스 result = classify_pipe("", "PIPE, SEAMLESS, A333-6, 6\", SCH40", "6\"", 1000) assert result["category"] == "PIPE" assert result["confidence"] > 0.8 assert result["material_grade"] == "A333-6" assert result["schedule"] == "SCH40" assert result["size"] == "6\"" def test_pipe_classification_welded(self): """용접 파이프 분류 테스트""" from app.services.pipe_classifier import classify_pipe result = classify_pipe("", "PIPE, WELDED, A53-B, 4\", SCH40", "4\"", 500) assert result["category"] == "PIPE" assert result["subcategory"] == "WELDED" assert result["material_grade"] == "A53-B" assert result["confidence"] > 0.8 def test_pipe_classification_low_confidence(self): """낮은 신뢰도 파이프 분류 테스트""" from app.services.pipe_classifier import classify_pipe # 모호한 설명 result = classify_pipe("", "STEEL TUBE, 2 INCH", "2\"", 100) # 파이프로 분류되지만 신뢰도가 낮아야 함 assert result["confidence"] < 0.7 def test_pipe_length_calculation(self): """파이프 길이 계산 테스트""" from app.services.pipe_classifier import classify_pipe result = classify_pipe("", "PIPE, SEAMLESS, A333-6, 6\", SCH40", "6\"", 6000) assert "length_mm" in result assert result["length_mm"] == 6000 assert "purchase_length" in result class TestFittingClassifier: """피팅 분류기 테스트""" def test_elbow_classification(self): """엘보우 분류 테스트""" from app.services.fitting_classifier import classify_fitting result = classify_fitting("", "ELBOW, 90DEG, A234-WPB, 4\", SCH40") assert result["category"] == "FITTING" assert result["subcategory"] == "ELBOW" assert result["angle"] == "90DEG" assert result["material_grade"] == "A234-WPB" assert result["confidence"] > 0.8 def test_tee_classification(self): """티 분류 테스트""" from app.services.fitting_classifier import classify_fitting result = classify_fitting("", "TEE, EQUAL, A234-WPB, 6\", SCH40") assert result["category"] == "FITTING" assert result["subcategory"] == "TEE" assert result["fitting_type"] == "EQUAL" assert result["confidence"] > 0.8 def test_reducer_classification(self): """리듀서 분류 테스트""" from app.services.fitting_classifier import classify_fitting result = classify_fitting("", "REDUCER, CONCENTRIC, A234-WPB, 8\"X6\", SCH40") assert result["category"] == "FITTING" assert result["subcategory"] == "REDUCER" assert result["fitting_type"] == "CONCENTRIC" assert "8\"X6\"" in result["size"] class TestValveClassifier: """밸브 분류기 테스트""" def test_gate_valve_classification(self): """게이트 밸브 분류 테스트""" from app.services.valve_classifier import classify_valve result = classify_valve("", "VALVE, GATE, A216-WCB, 2\", 150LB") assert result["category"] == "VALVE" assert result["subcategory"] == "GATE" assert result["material_grade"] == "A216-WCB" assert result["pressure_rating"] == "150LB" assert result["confidence"] > 0.8 def test_ball_valve_classification(self): """볼 밸브 분류 테스트""" from app.services.valve_classifier import classify_valve result = classify_valve("", "VALVE, BALL, A216-WCB, 4\", 300LB") assert result["category"] == "VALVE" assert result["subcategory"] == "BALL" assert result["pressure_rating"] == "300LB" assert result["confidence"] > 0.8 def test_check_valve_classification(self): """체크 밸브 분류 테스트""" from app.services.valve_classifier import classify_valve result = classify_valve("", "VALVE, CHECK, SWING, A216-WCB, 3\", 150LB") assert result["category"] == "VALVE" assert result["subcategory"] == "CHECK" assert result["valve_type"] == "SWING" class TestFlangeClassifier: """플랜지 분류기 테스트""" def test_weld_neck_flange_classification(self): """용접목 플랜지 분류 테스트""" from app.services.flange_classifier import classify_flange result = classify_flange("", "FLANGE, WELD NECK, A105, 3\", 150LB") assert result["category"] == "FLANGE" assert result["subcategory"] == "WELD NECK" assert result["material_grade"] == "A105" assert result["pressure_rating"] == "150LB" assert result["confidence"] > 0.8 def test_slip_on_flange_classification(self): """슬립온 플랜지 분류 테스트""" from app.services.flange_classifier import classify_flange result = classify_flange("", "FLANGE, SLIP ON, A105, 4\", 300LB") assert result["category"] == "FLANGE" assert result["subcategory"] == "SLIP ON" assert result["pressure_rating"] == "300LB" assert result["confidence"] > 0.8 def test_blind_flange_classification(self): """블라인드 플랜지 분류 테스트""" from app.services.flange_classifier import classify_flange result = classify_flange("", "FLANGE, BLIND, A105, 6\", 150LB") assert result["category"] == "FLANGE" assert result["subcategory"] == "BLIND" assert result["confidence"] > 0.8 class TestBoltClassifier: """볼트 분류기 테스트""" def test_hex_bolt_classification(self): """육각 볼트 분류 테스트""" from app.services.bolt_classifier import classify_bolt result = classify_bolt("", "BOLT, HEX HEAD, A193-B7, M16X50") assert result["category"] == "BOLT" assert result["subcategory"] == "HEX HEAD" assert result["material_grade"] == "A193-B7" assert result["size"] == "M16X50" assert result["confidence"] > 0.8 def test_stud_bolt_classification(self): """스터드 볼트 분류 테스트""" from app.services.bolt_classifier import classify_bolt result = classify_bolt("", "STUD BOLT, A193-B7, M20X80") assert result["category"] == "BOLT" assert result["subcategory"] == "STUD" assert result["material_grade"] == "A193-B7" assert result["confidence"] > 0.8 def test_nut_classification(self): """너트 분류 테스트""" from app.services.bolt_classifier import classify_bolt result = classify_bolt("", "NUT, HEX, A194-2H, M16") assert result["category"] == "BOLT" assert result["subcategory"] == "NUT" assert result["material_grade"] == "A194-2H" assert result["confidence"] > 0.8 class TestGasketClassifier: """가스켓 분류기 테스트""" def test_spiral_wound_gasket_classification(self): """스파이럴 와운드 가스켓 분류 테스트""" from app.services.gasket_classifier import classify_gasket result = classify_gasket("", "GASKET, SPIRAL WOUND, SS316+GRAPHITE, 4\", 150LB") assert result["category"] == "GASKET" assert result["subcategory"] == "SPIRAL WOUND" assert "SS316" in result["material_grade"] assert result["confidence"] > 0.8 def test_rtj_gasket_classification(self): """RTJ 가스켓 분류 테스트""" from app.services.gasket_classifier import classify_gasket result = classify_gasket("", "GASKET, RTJ, SS316, 6\", 300LB") assert result["category"] == "GASKET" assert result["subcategory"] == "RTJ" assert result["material_grade"] == "SS316" assert result["confidence"] > 0.8 def test_flat_gasket_classification(self): """플랫 가스켓 분류 테스트""" from app.services.gasket_classifier import classify_gasket result = classify_gasket("", "GASKET, FLAT, RUBBER, 2\", 150LB") assert result["category"] == "GASKET" assert result["subcategory"] == "FLAT" assert result["material_grade"] == "RUBBER" assert result["confidence"] > 0.8 class TestInstrumentClassifier: """계기 분류기 테스트""" def test_pressure_gauge_classification(self): """압력계 분류 테스트""" from app.services.instrument_classifier import classify_instrument result = classify_instrument("", "PRESSURE GAUGE, 0-10 BAR, 1/2\" NPT") assert result["category"] == "INSTRUMENT" assert result["subcategory"] == "PRESSURE GAUGE" assert "0-10 BAR" in result["range"] assert result["confidence"] > 0.8 def test_temperature_gauge_classification(self): """온도계 분류 테스트""" from app.services.instrument_classifier import classify_instrument result = classify_instrument("", "TEMPERATURE GAUGE, 0-200°C, 1/2\" NPT") assert result["category"] == "INSTRUMENT" assert result["subcategory"] == "TEMPERATURE GAUGE" assert "0-200°C" in result["range"] assert result["confidence"] > 0.8 def test_flow_meter_classification(self): """유량계 분류 테스트""" from app.services.instrument_classifier import classify_instrument result = classify_instrument("", "FLOW METER, ORIFICE, 4\", 150LB") assert result["category"] == "INSTRUMENT" assert result["subcategory"] == "FLOW METER" assert result["instrument_type"] == "ORIFICE" assert result["confidence"] > 0.8 class TestIntegratedClassifier: """통합 분류기 테스트""" def test_integrated_classification_pipe(self): """통합 분류기 파이프 테스트""" from app.services.integrated_classifier import classify_material_integrated result = classify_material_integrated("PIPE, SEAMLESS, A333-6, 6\", SCH40") assert result["category"] == "PIPE" assert result["confidence"] > 0.8 assert "classification_details" in result def test_integrated_classification_valve(self): """통합 분류기 밸브 테스트""" from app.services.integrated_classifier import classify_material_integrated result = classify_material_integrated("VALVE, GATE, A216-WCB, 2\", 150LB") assert result["category"] == "VALVE" assert result["confidence"] > 0.8 def test_exclusion_logic(self): """제외 로직 테스트""" from app.services.integrated_classifier import should_exclude_material # 제외되어야 하는 항목들 assert should_exclude_material("INSULATION, MINERAL WOOL") is True assert should_exclude_material("PAINT, PRIMER") is True assert should_exclude_material("SUPPORT, STRUCTURAL") is True # 제외되지 않아야 하는 항목들 assert should_exclude_material("PIPE, SEAMLESS, A333-6") is False assert should_exclude_material("VALVE, GATE, A216-WCB") is False def test_confidence_threshold(self): """신뢰도 임계값 테스트""" from app.services.integrated_classifier import classify_material_integrated # 모호한 설명으로 낮은 신뢰도 테스트 result = classify_material_integrated("STEEL ITEM, UNKNOWN TYPE") # 신뢰도가 낮아야 함 assert result["confidence"] < 0.5 assert result["category"] in ["UNKNOWN", "EXCLUDE"] class TestClassificationCaching: """분류 결과 캐싱 테스트""" @patch('app.services.integrated_classifier.tkmp_cache') def test_classification_cache_hit(self, mock_cache): """분류 결과 캐시 히트 테스트""" from app.services.integrated_classifier import classify_material_integrated # 캐시에서 결과 반환 설정 cached_result = { "category": "PIPE", "confidence": 0.95, "cached": True } mock_cache.get_classification_result.return_value = cached_result result = classify_material_integrated("PIPE, SEAMLESS, A333-6, 6\", SCH40") assert result == cached_result mock_cache.get_classification_result.assert_called_once() @patch('app.services.integrated_classifier.tkmp_cache') def test_classification_cache_miss(self, mock_cache): """분류 결과 캐시 미스 테스트""" from app.services.integrated_classifier import classify_material_integrated # 캐시에서 None 반환 (캐시 미스) mock_cache.get_classification_result.return_value = None result = classify_material_integrated("PIPE, SEAMLESS, A333-6, 6\", SCH40") assert result["category"] == "PIPE" assert result["confidence"] > 0.8 # 캐시 저장 호출 확인 mock_cache.set_classification_result.assert_called_once() @pytest.mark.performance class TestClassificationPerformance: """분류 성능 테스트""" def test_classification_speed(self): """분류 속도 테스트""" import time from app.services.integrated_classifier import classify_material_integrated descriptions = [ "PIPE, SEAMLESS, A333-6, 6\", SCH40", "VALVE, GATE, A216-WCB, 2\", 150LB", "FLANGE, WELD NECK, A105, 3\", 150LB", "ELBOW, 90DEG, A234-WPB, 4\", SCH40", "BOLT, HEX HEAD, A193-B7, M16X50" ] start_time = time.time() for desc in descriptions: result = classify_material_integrated(desc) assert result["category"] != "UNKNOWN" end_time = time.time() total_time = end_time - start_time # 5개 항목을 1초 이내에 분류해야 함 assert total_time < 1.0 # 평균 분류 시간이 100ms 이하여야 함 avg_time = total_time / len(descriptions) assert avg_time < 0.1 def test_batch_classification(self): """배치 분류 테스트""" from app.services.integrated_classifier import classify_material_integrated descriptions = [ "PIPE, SEAMLESS, A333-6, 6\", SCH40", "VALVE, GATE, A216-WCB, 2\", 150LB", "FLANGE, WELD NECK, A105, 3\", 150LB" ] * 10 # 30개 항목 results = [] for desc in descriptions: result = classify_material_integrated(desc) results.append(result) # 모든 결과가 올바르게 분류되었는지 확인 assert len(results) == 30 # 각 타입별로 올바르게 분류되었는지 확인 pipe_results = [r for r in results if r["category"] == "PIPE"] valve_results = [r for r in results if r["category"] == "VALVE"] flange_results = [r for r in results if r["category"] == "FLANGE"] assert len(pipe_results) == 10 assert len(valve_results) == 10 assert len(flange_results) == 10