Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
- H/F/I/O SS304/GRAPHITE/CS/CS 패턴에서 4개 구성요소 모두 표시 - 기존 SS304 + GRAPHITE → SS304/GRAPHITE/CS/CS로 완전한 구성 표시 - 외부링/필러/내부링/추가구성 모든 정보 포함 - 구매수량 계산 모달에서 정확한 재질 정보 확인 가능
424 lines
16 KiB
Python
424 lines
16 KiB
Python
"""
|
|
자재 분류기 테스트
|
|
"""
|
|
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
|