Files
Gnuboard7/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/PgProviderSupportedMethodsVocabularyTest.php
HeuJung 6bde24bc75 fix(ecommerce): 숫자 설정 문자열 영속으로 인한 주문 생성 실패 해소
관리자 환경설정에서 미입금 자동취소 기한을 저장하면 HTML number 입력의
문자열이 그대로 영속되고, Carbon 3.13 의 strict 타입 경계에서 TypeError 가
발생해 무통장입금·가상계좌 주문이 전면 실패했다(공개 제보). 한 곳만 막으면
다른 저장 경로가 남으므로 저장·조회·소비·모델 네 계층에 각각 방어를 두고,
저장소 전역에서 같은 형태가 더 없음을 audit 룰로 확정했다.

같은 계획서 수행 중 드러난 인접 결함도 함께 해소했다.
- 쿠폰 수정 경로로 (발급일 기준, 일수 NULL) 조합이 저장되어 발급 즉시
 만료된 쿠폰이 조용히 나가던 문제 — 저장·발급 양쪽에서 차단
- 리뷰 작성 기한을 화면과 저장 판정이 서로 다른 기준으로 계산하던 문제
- 결제 플러그인이 PaymentMethodEnum 과 다른 어휘로 지원 결제수단을 선언해
 가상계좌·계좌이체·휴대폰결제에 PG사를 지정할 수 없던 문제

엔진에서는 `_localInit` 적용 여부를 전역 해시로만 판정해 인스턴스별 저장소가
리셋되지 않던 결함(engine-v1.54.5)과, 제거 대상이 없어도 상태 갱신·캐시
무효화가 상시 발생하던 판정 결함(engine-v1.54.6)을 고쳤다.

Playwright MCP 정밀 점검(T1~T11)에서 설정이 빈 문자열로 남은 사이트의
주문서 입금기한 안내가 미치환 플레이스홀더를 노출하는 것을 실측해, 화면
폴백을 서버와 같은 규칙으로 맞췄다.
2026-08-02 18:35:28 +09:00

126 lines
5.0 KiB
PHP

<?php
namespace Modules\Sirsoft\Ecommerce\Tests\Unit\Services;
use Modules\Sirsoft\Ecommerce\Enums\PaymentMethodEnum;
use Modules\Sirsoft\Ecommerce\Tests\ModuleTestCase;
/**
* PG 플러그인이 선언하는 supported_methods 어휘 정합 테스트
*
* 관리자 결제수단 설정의 PG사 드롭다운은 `supported_methods.includes($method.id)` 로
* 후보를 거른다. 즉 PG 플러그인이 선언하는 값은 반드시 **결제수단 id 어휘**여야 한다.
* 다른 어휘(예: 'virtual_account')로 선언하면 교집합이 비어 드롭다운이 영구히 `---` 만
* 노출되고, 그 결제수단은 기본 PG사 상속 외에는 PG를 지정할 수 없게 된다.
* 백엔드는 결제수단별 pg_provider 를 실제로 사용하므로(OrderProcessingService 의
* `$methodConfig['pg_provider'] ?? default_pg_provider`) 이는 기능 손실이다.
*
* 어휘 목록을 손으로 열거하면 플러그인이 늘어날 때 누락되므로, 저장소의 PG 플러그인
* 선언을 전수 스캔해 검증한다.
*
* @effects pg_supported_methods_use_payment_method_vocabulary
*/
class PgProviderSupportedMethodsVocabularyTest extends ModuleTestCase
{
/**
* 저장소의 모든 PG 플러그인에서 supported_methods 선언을 수집합니다.
*
* @return array<string, array<int, string>> 파일 경로 => 선언된 결제수단 값 목록
*/
private function collectDeclarations(): array
{
$roots = [base_path('plugins/_bundled'), base_path('plugins')];
$found = [];
foreach ($roots as $root) {
if (! is_dir($root)) {
continue;
}
$it = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($root, \FilesystemIterator::SKIP_DOTS)
);
foreach ($it as $file) {
if (! $file->isFile() || $file->getExtension() !== 'php') {
continue;
}
$contents = file_get_contents($file->getPathname());
if ($contents === false || ! str_contains($contents, 'supported_methods')) {
continue;
}
if (! preg_match("/'supported_methods'\s*=>\s*\[(.*?)\]/s", $contents, $m)) {
continue;
}
preg_match_all("/'([^']+)'/", $m[1], $values);
if ($values[1] !== []) {
// 활성 디렉토리와 _bundled 중복 수집을 피하기 위해 상대 경로 키를 정규화한다.
$key = str_replace('\\', '/', $file->getPathname());
$found[$key] = $values[1];
}
}
}
return $found;
}
public function test_declared_supported_methods_use_payment_method_ids(): void
{
$declarations = $this->collectDeclarations();
$this->assertNotEmpty(
$declarations,
'PG 플러그인의 supported_methods 선언을 하나도 찾지 못했습니다 (테스트 전제 붕괴 — 스캔 경로/패턴 확인 필요).'
);
$known = array_map(
static fn (PaymentMethodEnum $case): string => $case->value,
PaymentMethodEnum::cases()
);
$violations = [];
foreach ($declarations as $path => $methods) {
foreach ($methods as $method) {
if (! in_array($method, $known, true)) {
$violations[] = sprintf('%s → %s', $path, $method);
}
}
}
$this->assertSame(
[],
$violations,
"PG 플러그인이 결제수단 id 가 아닌 어휘를 선언했습니다.\n".
"이 값은 관리자 PG 드롭다운 필터(`supported_methods.includes(method.id)`)와 직접 대조되므로,\n".
"어휘가 어긋나면 해당 결제수단은 PG를 지정할 수 없습니다.\n".
'허용 어휘: '.implode(', ', $known)."\n위반:\n ".implode("\n ", $violations)
);
}
/**
* PG 가 필요한 결제수단이 최소 한 곳의 PG 플러그인에서 지원 선언되어야,
* 관리자 화면에서 그 결제수단에 PG를 지정할 수 있다.
*/
public function test_pg_requiring_builtin_methods_are_covered_by_some_provider(): void
{
$declared = [];
foreach ($this->collectDeclarations() as $methods) {
foreach ($methods as $m) {
$declared[$m] = true;
}
}
// 무통장입금(dbank)·포인트·예치금·무료는 PG 불필요라 대상이 아니다.
foreach ([PaymentMethodEnum::CARD, PaymentMethodEnum::VBANK, PaymentMethodEnum::BANK, PaymentMethodEnum::PHONE] as $case) {
$this->assertArrayHasKey(
$case->value,
$declared,
"결제수단 '{$case->value}' 를 지원한다고 선언한 PG 플러그인이 없습니다 → 관리자 화면에서 PG 지정 불가."
);
}
}
}