Files
Gnuboard7/modules/_bundled/sirsoft-ecommerce/tests/Unit/Models/OrderPaymentTest.php
T
HeuJung 2ba4a760fb test(ecommerce,pay_kginicis,pay_nhnkcp,pay_nicepayments): 브랜드 배지 회귀 테스트 전 플러그인 확대 + 시나리오 마커 정합
브랜치 통합 검증에서 정적 룰이 잡지 못한 결함 4건을 해소한다.

replaceState 후킹은 4개 결제 플러그인에 동일하게 적용됐는데 단위 회귀 테스트는
tosspayments 에만 있었다. 나머지 3개는 기존 injector 테스트가 있는데도 변경이
반영되지 않아, 앞으로 한 플러그인만 고쳐도 회귀를 잡을 수단이 없었다.

브랜드 마크 E2E 의 준비 게이트가 pg-locked-badge 만 세고 있었다. 그 배지는
pg_locked 인 수단에만 렌더되므로 PG 고정 수단이 없는 구성에서는 화면이 정상
렌더돼도 0 이 되어 타임아웃으로 오판 실패한다. 스펙이 선언한 "설치 구성을
하드코딩하지 않는다" 규율과 어긋나는 숨은 결합이라, PG 열의 상호배타 4분기
합계로 바꿔 구성과 무관하게 비고아 행 수와 일치시킨다. 읽히지 않던 badges
헬퍼도 함께 걷어낸다.

brand_mark 축과 두 효과가 매니페스트에 없어 마커가 가리킬 대상이 없었다.
축·효과·제외 규칙을 추가하며, 마커 파서가 쉼표로만 축을 분리한다는 사실을
확인해(× 는 첫 축만 잡힌다) 해당 spec 을 쉼표 형식으로 정정했다. 매니페스트를
건드리자 test_files 에 선언됐으나 마킹이 0건이던 파일 2건도 함께 드러나 마킹했다.
2026-08-10 17:17:53 +09:00

171 lines
7.0 KiB
PHP
Raw 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 Modules\Sirsoft\Ecommerce\Tests\Unit\Models;
use Modules\Sirsoft\Ecommerce\Database\Factories\OrderFactory;
use Modules\Sirsoft\Ecommerce\Database\Factories\OrderPaymentFactory;
use Modules\Sirsoft\Ecommerce\Enums\PaymentMethodEnum;
use Modules\Sirsoft\Ecommerce\Enums\PaymentStatusEnum;
use Modules\Sirsoft\Ecommerce\Models\Order;
use Modules\Sirsoft\Ecommerce\Models\OrderPayment;
use Modules\Sirsoft\Ecommerce\Tests\ModuleTestCase;
/**
* OrderPayment 모델 테스트
*
* @scenario extension_payment_method method_kind=extension × capability_declared=declared × capability=refund_method
* @effects extension_id_persisted_as_is, builtin_capability_unchanged, refund_method_is_pg
*/
class OrderPaymentTest extends ModuleTestCase
{
public function test_order_payment_can_be_created(): void
{
// paid_amount 컬럼은 paid_amount_local / paid_amount_base 로 분리됨 (다중 통화 지원)
$order = OrderFactory::new()->create();
$payment = OrderPaymentFactory::new()->forOrder($order)->create([
'paid_amount_local' => 50000,
'paid_amount_base' => 50000,
]);
$this->assertDatabaseHas('ecommerce_order_payments', [
'id' => $payment->id,
'order_id' => $order->id,
]);
}
public function test_order_payment_belongs_to_order(): void
{
$order = OrderFactory::new()->create();
$payment = OrderPaymentFactory::new()->forOrder($order)->create();
$this->assertInstanceOf(Order::class, $payment->order);
$this->assertEquals($order->id, $payment->order->id);
}
public function test_order_payment_method_is_stored_as_plain_string(): void
{
// payment_method 는 enum 캐스트를 두지 않는다 (#475) — PG 플러그인이 등록하는
// 확장 결제수단(예: nhnkcp_naverpay)은 enum case 가 없어 캐스트가 ValueError 를 던지기 때문.
// 능력 질의는 모델의 능력 메서드가 PaymentMethodResolver 에 위임해 답한다.
$order = OrderFactory::new()->create();
$payment = OrderPaymentFactory::new()->forOrder($order)->card()->create();
$this->assertSame(PaymentMethodEnum::CARD->value, $payment->fresh()->payment_method);
$this->assertTrue($payment->isCardPayment());
}
public function test_order_payment_accepts_extension_payment_method_id(): void
{
// 확장 결제수단 ID 를 1급 시민으로 저장할 수 있어야 한다 (캐스트가 있으면 ValueError).
$order = OrderFactory::new()->create();
$payment = OrderPaymentFactory::new()->forOrder($order)->create([
'payment_method' => 'nhnkcp_naverpay',
'pg_provider' => 'nhnkcp',
]);
$this->assertSame('nhnkcp_naverpay', $payment->fresh()->payment_method);
}
public function test_order_payment_casts_status_to_enum(): void
{
$order = OrderFactory::new()->create();
$payment = OrderPaymentFactory::new()->forOrder($order)->completed()->create();
$this->assertInstanceOf(PaymentStatusEnum::class, $payment->payment_status);
$this->assertEquals(PaymentStatusEnum::PAID, $payment->payment_status);
}
public function test_card_payment_has_card_info(): void
{
$order = OrderFactory::new()->create();
$payment = OrderPaymentFactory::new()->forOrder($order)->card()->create([
'card_name' => '신한카드',
]);
$this->assertTrue($payment->isCardPayment());
$this->assertEquals('신한카드', $payment->card_name);
}
public function test_virtual_account_payment_has_vbank_info(): void
{
$order = OrderFactory::new()->create();
$payment = OrderPaymentFactory::new()->forOrder($order)->virtualAccount()->create();
$this->assertTrue($payment->isVirtualAccount());
$this->assertNotNull($payment->vbank_name);
$this->assertNotNull($payment->vbank_number);
}
public function test_pending_payment_has_no_paid_at(): void
{
$order = OrderFactory::new()->create();
$payment = OrderPaymentFactory::new()->forOrder($order)->pending()->create();
$this->assertEquals(PaymentStatusEnum::READY, $payment->payment_status);
$this->assertNull($payment->paid_at);
}
public function test_completed_payment_has_paid_at(): void
{
$order = OrderFactory::new()->create();
$payment = OrderPaymentFactory::new()->forOrder($order)->completed()->create();
$this->assertEquals(PaymentStatusEnum::PAID, $payment->payment_status);
$this->assertNotNull($payment->paid_at);
}
public function test_cancelled_payment_has_cancel_info(): void
{
$order = OrderFactory::new()->create();
$payment = OrderPaymentFactory::new()->forOrder($order)->cancelled()->create();
$this->assertEquals(PaymentStatusEnum::CANCELLED, $payment->payment_status);
$this->assertNotNull($payment->cancelled_at);
$this->assertNotNull($payment->cancel_reason);
}
public function test_failed_payment_has_correct_status(): void
{
$order = OrderFactory::new()->create();
$payment = OrderPaymentFactory::new()->forOrder($order)->failed()->create();
$this->assertEquals(PaymentStatusEnum::FAILED, $payment->payment_status);
$this->assertNull($payment->paid_at);
}
public function test_cancellable_amount_uses_payment_currency_cumulative_when_cross_currency(): void
{
// base JPY 주문을 KRW 로 결제: paid_amount_local 은 결제 통화(KRW 4750), 누적 취소도
// 결제 통화로 비교해야 한다. mc_cancelled_amount[KRW]=1000 이면 잔여 = 4750-1000 = 3750.
$order = OrderFactory::new()->create();
$payment = OrderPaymentFactory::new()->forOrder($order)->create([
'currency' => 'KRW',
'paid_amount_local' => 4750,
'paid_amount_base' => 500,
// base 누적(cancelled_amount)은 JPY 100 이지만 결제 통화 비교에는 쓰지 않는다.
'cancelled_amount' => 100,
'mc_cancelled_amount' => ['JPY' => 100, 'KRW' => 1000],
]);
$this->assertSame(1000.0, $payment->cancelledLocalAmount());
// 3750 = 4750(KRW) - 1000(KRW). base cancelled(100)으로 빼면 4650 이 되는 회귀를 차단.
$this->assertSame(3750.0, $payment->getCancellableAmount());
}
public function test_cancellable_amount_falls_back_to_base_cancelled_for_legacy_payment(): void
{
// mc_cancelled_amount 가 없는(레거시) 결제는 base 누적 cancelled_amount 로 폴백한다.
$order = OrderFactory::new()->create();
$payment = OrderPaymentFactory::new()->forOrder($order)->create([
'currency' => 'KRW',
'paid_amount_local' => 50000,
'paid_amount_base' => 50000,
'cancelled_amount' => 20000,
'mc_cancelled_amount' => null,
]);
$this->assertSame(20000.0, $payment->cancelledLocalAmount());
$this->assertSame(30000.0, $payment->getCancellableAmount());
}
}