적립 포인트 절사가 코드에 1점 버림으로 고정돼 있어 운영자가 조정할 수 없었다. 언어/통화 설정의 환율 절사를 따라가는 방안을 먼저 검토했으나, 그쪽은 외화 환산 분기에서만 적용되어 기본 통화 상점에는 따라갈 값 자체가 없다. 적립은 기본 통화 원장에 정수로 확정 기록되는 값이라 성격이 다르므로 마일리지 통화별 규칙에 신설했다. - earn_rounding_unit(1/10/100) + earn_rounding_method(floor/round/ceil) - 기본값은 종전 하드코딩과 정확히 같은 금액을 산출 — 기존 쇼핑몰 적립액 무변화 - 주문 계산·옵션 분할·부분취소 안분 전 경로에 동일 기준 적용 - 주문 시점 값을 스냅샷으로 보관 — 이후 기준을 바꿔도 과거 주문이 소급 변경되지 않는다 - 설정 UI·검증·다국어(ko/en/ja)·API 문서 동반, 기존 사이트는 업그레이드 스텝이 명시 기입 함께 처리: 마일리지 내역 목록 테스트가 첫 컬럼 field 를 `no` 로 단언해 실패하던 것을 현재 레이아웃 값(`number`)에 맞춰 정정했다. 이 브랜치와 무관한 stale 케이스다.
168 lines
5.4 KiB
PHP
168 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace Modules\Sirsoft\Ecommerce\Tests\Unit\Upgrades;
|
|
|
|
use App\Extension\UpgradeContext;
|
|
use Illuminate\Support\Facades\File;
|
|
use Modules\Sirsoft\Ecommerce\Support\MileageRounding;
|
|
use Modules\Sirsoft\Ecommerce\Tests\ModuleTestCase;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
|
|
/**
|
|
* 마일리지 적립 절사 기준 백필 마이그레이션 테스트
|
|
*
|
|
* 폴백이 있어 백필 없이도 금액은 같다. 이 백필의 목적은 "운영자가 버림을 선택했다" 와
|
|
* "키가 없어서 폴백됐다" 를 설정 파일에서 구분 가능하게 만드는 것이다 — 그래야 이후 폴백
|
|
* 기본값을 바꿔도 기존 사이트의 적립 절사가 조용히 따라 바뀌지 않는다.
|
|
*/
|
|
class BackfillMileageEarnRoundingTest extends ModuleTestCase
|
|
{
|
|
private const MIGRATION = 'App\\Upgrades\\Data\\Ext\\Modules\\SirsoftEcommerce\\V1_0_5\\Migrations\\BackfillMileageEarnRounding';
|
|
|
|
private string $settingsFile;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
// DataMigration 클래스는 오토로드 밖 — 직접 로드
|
|
require_once dirname(__DIR__, 3).'/upgrades/data/1.0.5/migrations/07_BackfillMileageEarnRounding.php';
|
|
|
|
$dir = storage_path('framework/testing/modules/sirsoft-ecommerce/settings');
|
|
if (! File::isDirectory($dir)) {
|
|
File::makeDirectory($dir, 0755, true);
|
|
}
|
|
|
|
$this->settingsFile = $dir.'/mileage.json';
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
if (File::exists($this->settingsFile)) {
|
|
File::delete($this->settingsFile);
|
|
}
|
|
|
|
parent::tearDown();
|
|
}
|
|
|
|
private function migration(): object
|
|
{
|
|
$class = self::MIGRATION;
|
|
|
|
return new $class;
|
|
}
|
|
|
|
private function context(): UpgradeContext
|
|
{
|
|
return new UpgradeContext(
|
|
fromVersion: '1.0.2',
|
|
toVersion: '1.0.5',
|
|
currentStep: '1.0.5',
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 마일리지 설정 파일을 기록합니다.
|
|
*
|
|
* @param array<int, array> $rules 통화별 규칙
|
|
*/
|
|
private function writeSettings(array $rules): void
|
|
{
|
|
File::put($this->settingsFile, json_encode([
|
|
'enabled' => true,
|
|
'default_earn_rate' => 1,
|
|
'currency_rules' => $rules,
|
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
}
|
|
|
|
/**
|
|
* @return array<int, array> 저장된 통화별 규칙
|
|
*/
|
|
private function readRules(): array
|
|
{
|
|
return json_decode((string) File::get($this->settingsFile), true)['currency_rules'];
|
|
}
|
|
|
|
#[Test]
|
|
public function 절사_키가_없는_행에_종전_동작값을_기입한다(): void
|
|
{
|
|
$this->writeSettings([
|
|
['currency_code' => 'KRW', 'point_value' => 1, 'use_unit' => 10],
|
|
['currency_code' => 'USD', 'point_value' => 0.001, 'use_unit' => 1],
|
|
]);
|
|
|
|
$this->migration()->run($this->context());
|
|
|
|
foreach ($this->readRules() as $rule) {
|
|
$this->assertSame('1', $rule[MileageRounding::UNIT_KEY]);
|
|
$this->assertSame('floor', $rule[MileageRounding::METHOD_KEY]);
|
|
}
|
|
}
|
|
|
|
#[Test]
|
|
public function 이미_기입된_값은_덮어쓰지_않는다(): void
|
|
{
|
|
$this->writeSettings([
|
|
[
|
|
'currency_code' => 'KRW',
|
|
MileageRounding::UNIT_KEY => '100',
|
|
MileageRounding::METHOD_KEY => 'ceil',
|
|
],
|
|
]);
|
|
|
|
$this->migration()->run($this->context());
|
|
// 멱등 확인 — 두 번 돌려도 운영자 선택이 보존되어야 한다
|
|
$this->migration()->run($this->context());
|
|
|
|
$rules = $this->readRules();
|
|
$this->assertSame('100', $rules[0][MileageRounding::UNIT_KEY]);
|
|
$this->assertSame('ceil', $rules[0][MileageRounding::METHOD_KEY]);
|
|
}
|
|
|
|
#[Test]
|
|
public function 한쪽_키만_있는_행은_빠진_키만_채운다(): void
|
|
{
|
|
$this->writeSettings([
|
|
['currency_code' => 'KRW', MileageRounding::METHOD_KEY => 'round'],
|
|
]);
|
|
|
|
$this->migration()->run($this->context());
|
|
|
|
$rules = $this->readRules();
|
|
$this->assertSame('1', $rules[0][MileageRounding::UNIT_KEY], '빠진 단위는 채워야 한다');
|
|
$this->assertSame('round', $rules[0][MileageRounding::METHOD_KEY], '있던 방식은 보존해야 한다');
|
|
}
|
|
|
|
#[Test]
|
|
public function 설정_파일이_없으면_아무것도_만들지_않는다(): void
|
|
{
|
|
if (File::exists($this->settingsFile)) {
|
|
File::delete($this->settingsFile);
|
|
}
|
|
|
|
$this->migration()->run($this->context());
|
|
|
|
// 신규 설치는 config/settings/defaults.json 이 값을 갖는다 — 여기서 파일을 만들면
|
|
// 운영자가 저장한 적 없는 설정 파일이 생겨 defaults 병합 경로가 달라진다.
|
|
$this->assertFalse(File::exists($this->settingsFile));
|
|
}
|
|
|
|
#[Test]
|
|
public function 백필_후_적립_산출이_종전과_같다(): void
|
|
{
|
|
$this->writeSettings([['currency_code' => 'KRW', 'point_value' => 1]]);
|
|
|
|
$this->migration()->run($this->context());
|
|
|
|
$rounding = MileageRounding::normalize($this->readRules()[0]);
|
|
|
|
foreach ([781.6, 2735.6, 1516.5] as $raw) {
|
|
$this->assertSame(
|
|
(int) floor($raw),
|
|
MileageRounding::apply($raw, $rounding),
|
|
'백필된 값이 종전 (int) floor 동작과 어긋난다'
|
|
);
|
|
}
|
|
}
|
|
}
|