Files
sam-api/tests/Unit/Production/PrefixResolverTest.php
권혁성 5a3d6c2243 feat(WEB): 절곡 자재투입 LOT 매핑 파이프라인 구현
- PrefixResolver: 제품코드×마감재질→LOT prefix 결정 + BD-XX-NN 코드 생성
- DynamicBomEntry DTO: dynamic_bom JSON 항목 타입 안전 관리
- BendingInfoBuilder 확장: build() 리턴 변경 + buildDynamicBomForItem() 추가
- OrderService: 작업지시 생성 시 per-item dynamic_bom 자동 저장
- WorkOrderService.getMaterials(): dynamic_bom 우선 체크 + N+1 배치 최적화
- WorkOrderService.registerMaterialInput(): work_order_item_id 분기 라우팅 통일
- 단위 테스트 58개 + 통합 테스트 6개 (64 tests / 293 assertions)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 04:19:47 +09:00

264 lines
12 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Tests\Unit\Production;
use App\Services\Production\PrefixResolver;
use PHPUnit\Framework\TestCase;
class PrefixResolverTest extends TestCase
{
private PrefixResolver $resolver;
protected function setUp(): void
{
parent::setUp();
$this->resolver = new PrefixResolver;
}
// ─────────────────────────────────────────────────
// 가이드레일 벽면형(Wall) Prefix
// ─────────────────────────────────────────────────
/**
* @dataProvider wallFinishProvider
*/
public function test_wall_finish_prefix(string $productCode, string $expected): void
{
$this->assertEquals(
$expected,
$this->resolver->resolveGuideRailPrefix('finish', 'wall', $productCode)
);
}
public static function wallFinishProvider(): array
{
return [
'KSS01 → RS' => ['KSS01', 'RS'],
'KQTS01 → RS' => ['KQTS01', 'RS'],
'KSE01 → RE' => ['KSE01', 'RE'],
'KWE01 → RE' => ['KWE01', 'RE'],
'KTE01 → RS' => ['KTE01', 'RS'],
];
}
public function test_wall_body_prefix(): void
{
$this->assertEquals('RM', $this->resolver->resolveGuideRailPrefix('body', 'wall', 'KSS01'));
$this->assertEquals('RM', $this->resolver->resolveGuideRailPrefix('body', 'wall', 'KSE01'));
$this->assertEquals('RM', $this->resolver->resolveGuideRailPrefix('body', 'wall', 'KWE01'));
}
public function test_wall_body_steel_override(): void
{
$this->assertEquals('RT', $this->resolver->resolveGuideRailPrefix('body', 'wall', 'KTE01'));
}
public function test_wall_fixed_prefixes(): void
{
foreach (['KSS01', 'KSE01', 'KWE01', 'KTE01', 'KQTS01'] as $code) {
$this->assertEquals('RC', $this->resolver->resolveGuideRailPrefix('c_type', 'wall', $code));
$this->assertEquals('RD', $this->resolver->resolveGuideRailPrefix('d_type', 'wall', $code));
$this->assertEquals('YY', $this->resolver->resolveGuideRailPrefix('extra_finish', 'wall', $code));
$this->assertEquals('XX', $this->resolver->resolveGuideRailPrefix('base', 'wall', $code));
}
}
// ─────────────────────────────────────────────────
// 가이드레일 측면형(Side) Prefix
// ─────────────────────────────────────────────────
/**
* @dataProvider sideFinishProvider
*/
public function test_side_finish_prefix(string $productCode, string $expected): void
{
$this->assertEquals(
$expected,
$this->resolver->resolveGuideRailPrefix('finish', 'side', $productCode)
);
}
public static function sideFinishProvider(): array
{
return [
'KSS01 → SS' => ['KSS01', 'SS'],
'KQTS01 → SS' => ['KQTS01', 'SS'],
'KSE01 → SE' => ['KSE01', 'SE'],
'KWE01 → SE' => ['KWE01', 'SE'],
'KTE01 → SS' => ['KTE01', 'SS'],
];
}
public function test_side_body_prefix(): void
{
$this->assertEquals('SM', $this->resolver->resolveGuideRailPrefix('body', 'side', 'KSS01'));
$this->assertEquals('SM', $this->resolver->resolveGuideRailPrefix('body', 'side', 'KSE01'));
}
public function test_side_body_steel_override(): void
{
$this->assertEquals('ST', $this->resolver->resolveGuideRailPrefix('body', 'side', 'KTE01'));
}
public function test_side_fixed_prefixes(): void
{
foreach (['KSS01', 'KSE01', 'KWE01', 'KTE01', 'KQTS01'] as $code) {
$this->assertEquals('SC', $this->resolver->resolveGuideRailPrefix('c_type', 'side', $code));
$this->assertEquals('SD', $this->resolver->resolveGuideRailPrefix('d_type', 'side', $code));
$this->assertEquals('YY', $this->resolver->resolveGuideRailPrefix('extra_finish', 'side', $code));
$this->assertEquals('XX', $this->resolver->resolveGuideRailPrefix('base', 'side', $code));
}
}
// ─────────────────────────────────────────────────
// 하단마감재 Prefix
// ─────────────────────────────────────────────────
public function test_bottom_bar_main_prefix(): void
{
// EGI 제품
$this->assertEquals('BE', $this->resolver->resolveBottomBarPrefix('main', 'KSE01', 'EGI마감'));
$this->assertEquals('BE', $this->resolver->resolveBottomBarPrefix('main', 'KWE01', 'EGI마감'));
// SUS 제품
$this->assertEquals('BS', $this->resolver->resolveBottomBarPrefix('main', 'KSS01', 'SUS마감'));
$this->assertEquals('BS', $this->resolver->resolveBottomBarPrefix('main', 'KQTS01', 'SUS마감'));
// 철재
$this->assertEquals('TS', $this->resolver->resolveBottomBarPrefix('main', 'KTE01', 'EGI마감'));
}
public function test_bottom_bar_fixed_prefixes(): void
{
foreach (['KSS01', 'KSE01', 'KWE01', 'KTE01'] as $code) {
$this->assertEquals('LA', $this->resolver->resolveBottomBarPrefix('lbar', $code, 'EGI마감'));
$this->assertEquals('HH', $this->resolver->resolveBottomBarPrefix('reinforce', $code, 'EGI마감'));
$this->assertEquals('YY', $this->resolver->resolveBottomBarPrefix('extra', $code, 'SUS마감'));
}
}
// ─────────────────────────────────────────────────
// 셔터박스 Prefix
// ─────────────────────────────────────────────────
public function test_shutter_box_standard_prefixes(): void
{
$this->assertEquals('CF', $this->resolver->resolveShutterBoxPrefix('front', true));
$this->assertEquals('CL', $this->resolver->resolveShutterBoxPrefix('lintel', true));
$this->assertEquals('CP', $this->resolver->resolveShutterBoxPrefix('inspection', true));
$this->assertEquals('CB', $this->resolver->resolveShutterBoxPrefix('rear_corner', true));
$this->assertEquals('XX', $this->resolver->resolveShutterBoxPrefix('top_cover', true));
$this->assertEquals('XX', $this->resolver->resolveShutterBoxPrefix('fin_cover', true));
}
public function test_shutter_box_nonstandard_all_xx(): void
{
foreach (['front', 'lintel', 'inspection', 'rear_corner', 'top_cover', 'fin_cover'] as $part) {
$this->assertEquals('XX', $this->resolver->resolveShutterBoxPrefix($part, false));
}
}
// ─────────────────────────────────────────────────
// 연기차단재 Prefix
// ─────────────────────────────────────────────────
public function test_smoke_barrier_always_gi(): void
{
$this->assertEquals('GI', $this->resolver->resolveSmokeBarrierPrefix());
}
// ─────────────────────────────────────────────────
// lengthToCode 변환
// ─────────────────────────────────────────────────
/**
* @dataProvider lengthCodeProvider
*/
public function test_length_to_code(int $lengthMm, ?string $smokeCategory, ?string $expected): void
{
$this->assertSame($expected, PrefixResolver::lengthToCode($lengthMm, $smokeCategory));
}
public static function lengthCodeProvider(): array
{
return [
'1219 → 12' => [1219, null, '12'],
'2438 → 24' => [2438, null, '24'],
'3000 → 30' => [3000, null, '30'],
'3500 → 35' => [3500, null, '35'],
'4000 → 40' => [4000, null, '40'],
'4150 → 41' => [4150, null, '41'],
'4200 → 42' => [4200, null, '42'],
'4300 → 43' => [4300, null, '43'],
'smoke w50 3000 → 53' => [3000, 'w50', '53'],
'smoke w50 4000 → 54' => [4000, 'w50', '54'],
'smoke w80 3000 → 83' => [3000, 'w80', '83'],
'smoke w80 4000 → 84' => [4000, 'w80', '84'],
'unknown length → null' => [9999, null, null],
];
}
// ─────────────────────────────────────────────────
// buildItemCode
// ─────────────────────────────────────────────────
public function test_build_item_code(): void
{
$this->assertEquals('BD-RS-43', $this->resolver->buildItemCode('RS', 4300));
$this->assertEquals('BD-RM-30', $this->resolver->buildItemCode('RM', 3000));
$this->assertEquals('BD-GI-53', $this->resolver->buildItemCode('GI', 3000, 'w50'));
$this->assertEquals('BD-GI-84', $this->resolver->buildItemCode('GI', 4000, 'w80'));
}
public function test_build_item_code_invalid_length_returns_null(): void
{
$this->assertNull($this->resolver->buildItemCode('RS', 9999));
}
// ─────────────────────────────────────────────────
// partTypeName
// ─────────────────────────────────────────────────
public function test_part_type_name(): void
{
$this->assertEquals('마감재', PrefixResolver::partTypeName('finish'));
$this->assertEquals('본체', PrefixResolver::partTypeName('body'));
$this->assertEquals('C형', PrefixResolver::partTypeName('c_type'));
$this->assertEquals('D형', PrefixResolver::partTypeName('d_type'));
$this->assertEquals('별도마감', PrefixResolver::partTypeName('extra_finish'));
$this->assertEquals('하부BASE', PrefixResolver::partTypeName('base'));
$this->assertEquals('L-Bar', PrefixResolver::partTypeName('lbar'));
$this->assertEquals('보강평철', PrefixResolver::partTypeName('reinforce'));
$this->assertEquals('전면부', PrefixResolver::partTypeName('front'));
$this->assertEquals('unknown_type', PrefixResolver::partTypeName('unknown_type'));
}
// ─────────────────────────────────────────────────
// 전체 조합 커버리지 (productCode × guideType × partType)
// ─────────────────────────────────────────────────
public function test_all_product_code_guide_type_combinations_produce_non_empty_prefix(): void
{
$productCodes = ['KSS01', 'KSE01', 'KWE01', 'KTE01', 'KQTS01'];
$guideTypes = ['wall', 'side'];
$partTypes = ['finish', 'body', 'c_type', 'd_type', 'base'];
foreach ($productCodes as $code) {
foreach ($guideTypes as $guide) {
foreach ($partTypes as $part) {
$prefix = $this->resolver->resolveGuideRailPrefix($part, $guide, $code);
$this->assertNotEmpty(
$prefix,
"Empty prefix for {$code}/{$guide}/{$part}"
);
$this->assertMatchesRegularExpression(
'/^[A-Z]{2}$/',
$prefix,
"Invalid prefix '{$prefix}' for {$code}/{$guide}/{$part}"
);
}
}
}
}
}