여러 화면에서 조건부(FormRequest) 검증이 우회되거나 과도하게 제한되던 결함을 한 계열로 묶어 수정한다. - 검증 규칙 확장 필터 이름 10개를 나머지 92개와 동일 규칙으로 통일 - 설명 길이를 바이트가 아닌 글자 수로 계산(한글 오검출 해소) - 비활성 언어팩 번역 보존한 채 수정 허용 - 알림 템플릿 제목 nullable 존중, 미지원 발송 수단 저장 차단 - 일정/실행 이력 목록 조회 검증 정합 - 게시판 첨부 미사용 시 빈 확장자 통과, new_display_hours/최소 길이 0 허용, 일괄 적용 override_values 범위 검증(SSoT) - 이커머스 클레임 사유 조회 검증, 용어 정정 - 원문 i18n 키 노출 다수 해소, 저장/삭제 실패 시 사유 화면 노출 - 아이콘 크기/다크모드 대비 회귀 수정 번들 확장(모듈3·플러그인6·템플릿2·언어팩5) 버전 new-bump 및 CHANGELOG 반영, ja 언어팩 동기.
163 lines
6.5 KiB
PHP
163 lines
6.5 KiB
PHP
<?php
|
|
|
|
namespace Modules\Sirsoft\Ecommerce\Tests\Unit\Resources;
|
|
|
|
use App\Models\User;
|
|
use Illuminate\Http\Request;
|
|
use Modules\Sirsoft\Ecommerce\Http\Resources\MileageTransactionCollection;
|
|
use Modules\Sirsoft\Ecommerce\Http\Resources\MileageTransactionResource;
|
|
use Modules\Sirsoft\Ecommerce\Models\MileageTransaction;
|
|
use Modules\Sirsoft\Ecommerce\Tests\ModuleTestCase;
|
|
|
|
/**
|
|
* MileageTransaction Resource / Collection 테스트 (§18.2-G)
|
|
*/
|
|
class MileageTransactionResourceTest extends ModuleTestCase
|
|
{
|
|
private function makeTx(string $type = 'purchase_earn'): MileageTransaction
|
|
{
|
|
$user = User::factory()->create();
|
|
|
|
return MileageTransaction::create([
|
|
'user_id' => $user->id, 'currency' => 'KRW', 'type' => $type,
|
|
'amount' => 1000, 'remaining_amount' => 1000, 'balance_after' => 1000,
|
|
'expires_at' => now()->addDays(30),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Resource: type_label / 배지 그룹 / 표시분류 + *_formatted 동반.
|
|
*/
|
|
public function test_resource_exposes_badge_and_display_category(): void
|
|
{
|
|
$tx = $this->makeTx('refund_restore');
|
|
$array = (new MileageTransactionResource($tx))->toArray(Request::create('/'));
|
|
|
|
$this->assertSame('refund_restore', $array['type']);
|
|
$this->assertSame('teal', $array['admin_badge_group']); // 복원계
|
|
$this->assertSame('adjust', $array['user_display_category']); // 사용자 4분류
|
|
$this->assertNotEmpty($array['type_label']);
|
|
|
|
// *_at 원시 + *_formatted 사용자 타임존 동반
|
|
$this->assertArrayHasKey('expires_at', $array);
|
|
$this->assertArrayHasKey('expires_at_formatted', $array);
|
|
$this->assertArrayHasKey('created_at_formatted', $array);
|
|
}
|
|
|
|
/**
|
|
* Resource: 금액 필드는 통화 소수 자릿수로 정규화된 수치다.
|
|
*
|
|
* `roundToCurrency` 는 자릿수 0인 통화(KRW 등)를 정수로, 자릿수가 있는 통화를 float 로
|
|
* 반환한다. 1000.0 처럼 의미 없는 소수부가 응답에 노출되지 않도록 한 의도된 계약이다.
|
|
*/
|
|
public function test_resource_amounts_are_normalized_to_currency_precision(): void
|
|
{
|
|
$tx = $this->makeTx();
|
|
$array = (new MileageTransactionResource($tx))->toArray(Request::create('/'));
|
|
|
|
// KRW = 소수 0자리 → 정수
|
|
$this->assertIsInt($array['amount']);
|
|
$this->assertIsInt($array['remaining_amount']);
|
|
$this->assertSame(1000, $array['amount']);
|
|
|
|
// 표시용 문자열은 별도 필드로 동반된다.
|
|
$this->assertArrayHasKey('amount_formatted', $array);
|
|
$this->assertNotEmpty($array['amount_formatted']);
|
|
}
|
|
|
|
/**
|
|
* Collection: data + abilities(can_manage) 구조.
|
|
*/
|
|
public function test_collection_structure_with_abilities(): void
|
|
{
|
|
$this->makeTx();
|
|
$this->makeTx('admin_deduct');
|
|
|
|
$collection = MileageTransaction::query()->paginate(20);
|
|
$array = (new MileageTransactionCollection($collection))->toArray(Request::create('/'));
|
|
|
|
$this->assertArrayHasKey('data', $array);
|
|
$this->assertArrayHasKey('abilities', $array);
|
|
$this->assertArrayHasKey('can_manage', $array['abilities']);
|
|
$this->assertCount(2, $array['data']);
|
|
}
|
|
|
|
/**
|
|
* 적립계 + 소멸 집계(expired_amount) 기준 expiry_state 계산: active/partial/fully.
|
|
*/
|
|
public function test_resource_expiry_state_by_expired_amount(): void
|
|
{
|
|
// 미소멸 적립 → active
|
|
$active = $this->makeTx('purchase_earn');
|
|
$activeArr = (new MileageTransactionResource($active))->toArray(Request::create('/'));
|
|
$this->assertSame('active', $activeArr['expiry_state']);
|
|
$this->assertSame(0, $activeArr['expired_amount']);
|
|
|
|
// 부분 소멸: 적립 1000, 소멸 집계 400 → partial_expired (서브쿼리 주입값 모사)
|
|
$partial = $this->makeTx('purchase_earn');
|
|
$partial->expired_amount = 400;
|
|
$partialArr = (new MileageTransactionResource($partial))->toArray(Request::create('/'));
|
|
$this->assertSame('partial_expired', $partialArr['expiry_state']);
|
|
$this->assertSame(400, $partialArr['expired_amount']);
|
|
|
|
// 전체 소멸: 적립 1000, 소멸 집계 1000 → fully_expired
|
|
$full = $this->makeTx('purchase_earn');
|
|
$full->expired_amount = 1000;
|
|
$fullArr = (new MileageTransactionResource($full))->toArray(Request::create('/'));
|
|
$this->assertSame('fully_expired', $fullArr['expiry_state']);
|
|
}
|
|
|
|
/**
|
|
* 비적립계(사용)는 소멸 집계가 있어도 expiry_state=active (적립계만 의미).
|
|
*/
|
|
public function test_resource_non_earning_expiry_state_is_active(): void
|
|
{
|
|
$use = $this->makeTx('order_use');
|
|
$use->expired_amount = 100; // 비정상 값이 들어와도 비적립계는 active 고정
|
|
$arr = (new MileageTransactionResource($use))->toArray(Request::create('/'));
|
|
|
|
$this->assertFalse($arr['is_earning']);
|
|
$this->assertSame('active', $arr['expiry_state']);
|
|
}
|
|
|
|
/**
|
|
* can_edit_expiry: 적립계 + 미소멸 + 잔여>0 일 때만 true.
|
|
*/
|
|
public function test_resource_can_edit_expiry_flag(): void
|
|
{
|
|
// 적립계 + 잔여 + 미소멸 → true
|
|
$editable = $this->makeTx('purchase_earn');
|
|
$this->assertTrue((new MileageTransactionResource($editable))->toArray(Request::create('/'))['can_edit_expiry']);
|
|
|
|
// 소멸됨 → false
|
|
$expired = $this->makeTx('purchase_earn');
|
|
$expired->expired_at = now();
|
|
$expired->remaining_amount = 0;
|
|
$this->assertFalse((new MileageTransactionResource($expired))->toArray(Request::create('/'))['can_edit_expiry']);
|
|
|
|
// 비적립계 → false
|
|
$use = $this->makeTx('order_use');
|
|
$this->assertFalse((new MileageTransactionResource($use))->toArray(Request::create('/'))['can_edit_expiry']);
|
|
}
|
|
|
|
/**
|
|
* adminBadgeGroup 5색 매핑이 Resource 에 정확히 노출.
|
|
*/
|
|
public function test_resource_badge_group_for_each_type(): void
|
|
{
|
|
$map = [
|
|
'purchase_earn' => 'green',
|
|
'order_use' => 'blue',
|
|
'expired' => 'gray',
|
|
'order_cancel_restore' => 'teal',
|
|
'admin_earn' => 'amber',
|
|
];
|
|
|
|
foreach ($map as $type => $expected) {
|
|
$tx = $this->makeTx($type);
|
|
$array = (new MileageTransactionResource($tx))->toArray(Request::create('/'));
|
|
$this->assertSame($expected, $array['admin_badge_group'], "{$type} 배지 그룹");
|
|
}
|
|
}
|
|
}
|