Files
Gnuboard7/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/CurrencyConversionServiceTest.php
T
2026-04-01 10:30:52 +09:00

402 lines
13 KiB
PHP

<?php
namespace Modules\Sirsoft\Ecommerce\Tests\Unit\Services;
use InvalidArgumentException;
use Modules\Sirsoft\Ecommerce\Services\CurrencyConversionService;
use Modules\Sirsoft\Ecommerce\Tests\ModuleTestCase;
/**
* 통화 변환 서비스 Unit 테스트
*/
class CurrencyConversionServiceTest extends ModuleTestCase
{
protected CurrencyConversionService $service;
protected function setUp(): void
{
parent::setUp();
$this->service = new CurrencyConversionService;
// 테스트용 통화 설정 주입
$this->setupTestCurrencySettings();
}
/**
* 테스트용 통화 설정을 저장합니다.
*/
protected function setupTestCurrencySettings(): void
{
$settingsPath = storage_path('app/modules/sirsoft-ecommerce/settings');
if (! is_dir($settingsPath)) {
mkdir($settingsPath, 0755, true);
}
$settings = [
'default_language' => 'ko',
'default_currency' => 'KRW',
'currencies' => [
[
'code' => 'KRW',
'name' => ['ko' => 'KRW (원)', 'en' => 'KRW (Won)'],
'exchange_rate' => null,
'rounding_unit' => '1',
'rounding_method' => 'floor',
'decimal_places' => 0,
'is_default' => true,
],
[
'code' => 'USD',
'name' => ['ko' => 'USD (달러)', 'en' => 'USD (Dollar)'],
'exchange_rate' => 0.85,
'rounding_unit' => '0.01',
'rounding_method' => 'round',
'decimal_places' => 2,
'is_default' => false,
],
[
'code' => 'JPY',
'name' => ['ko' => 'JPY (엔)', 'en' => 'JPY (Yen)'],
'exchange_rate' => 115,
'rounding_unit' => '1',
'rounding_method' => 'floor',
'decimal_places' => 0,
'is_default' => false,
],
[
'code' => 'EUR',
'name' => ['ko' => 'EUR (유로)', 'en' => 'EUR (Euro)'],
'exchange_rate' => 0.78,
'rounding_unit' => '0.01',
'rounding_method' => 'ceil',
'decimal_places' => 2,
'is_default' => false,
],
],
];
file_put_contents(
$settingsPath.'/language_currency.json',
json_encode($settings, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
);
// 캐시 초기화
$this->service->clearCache();
}
protected function tearDown(): void
{
// 테스트 설정 파일 정리
$settingsFile = storage_path('app/modules/sirsoft-ecommerce/settings/language_currency.json');
if (file_exists($settingsFile)) {
unlink($settingsFile);
}
parent::tearDown();
}
// ========================================
// 1. 기본 통화 조회 테스트
// ========================================
public function test_it_returns_default_currency(): void
{
// When
$defaultCurrency = $this->service->getDefaultCurrency();
// Then
$this->assertEquals('KRW', $defaultCurrency);
}
// ========================================
// 2. 지원 통화 목록 조회 테스트
// ========================================
public function test_it_returns_supported_currencies(): void
{
// When
$currencies = $this->service->getSupportedCurrencies();
// Then
$this->assertContains('KRW', $currencies);
$this->assertContains('USD', $currencies);
$this->assertContains('JPY', $currencies);
$this->assertContains('EUR', $currencies);
}
// ========================================
// 3. KRW → USD 기본 변환 테스트
// ========================================
public function test_it_converts_krw_to_usd(): void
{
// Given
$basePrice = 100000; // 100,000 KRW
// When
$result = $this->service->convertToCurrency($basePrice, 'USD');
// Then
// 100,000 / 1000 * 0.85 = 85.00 (round)
$this->assertEquals(85.00, $result['price']);
$this->assertEquals('USD', $result['currency']);
$this->assertEquals(0.85, $result['exchange_rate']);
}
// ========================================
// 4. KRW → JPY 변환 테스트
// ========================================
public function test_it_converts_krw_to_jpy(): void
{
// Given
$basePrice = 100000; // 100,000 KRW
// When
$result = $this->service->convertToCurrency($basePrice, 'JPY');
// Then
// 100,000 / 1000 * 115 = 11,500 (floor)
$this->assertEquals(11500, $result['price']);
$this->assertEquals('JPY', $result['currency']);
$this->assertEquals(115, $result['exchange_rate']);
}
// ========================================
// 5. round 반올림 적용 테스트
// ========================================
public function test_it_applies_round_rounding(): void
{
// Given
$basePrice = 33333; // 33,333 KRW
// When
$result = $this->service->convertToCurrency($basePrice, 'USD');
// Then
// 33,333 / 1000 * 0.85 = 28.33305
// round to 0.01 → 28.33
$this->assertEqualsWithDelta(28.33, $result['price'], 0.001);
}
// ========================================
// 6. floor 버림 적용 테스트
// ========================================
public function test_it_applies_floor_rounding(): void
{
// Given
$basePrice = 33333; // 33,333 KRW
// When
$result = $this->service->convertToCurrency($basePrice, 'JPY');
// Then
// 33,333 / 1000 * 115 = 3833.295
// floor to 1 → 3833
$this->assertEquals(3833, $result['price']);
}
// ========================================
// 7. ceil 올림 적용 테스트
// ========================================
public function test_it_applies_ceil_rounding(): void
{
// Given
$basePrice = 33333; // 33,333 KRW
// When
$result = $this->service->convertToCurrency($basePrice, 'EUR');
// Then
// 33,333 / 1000 * 0.78 = 25.99974
// ceil to 0.01 → 26.00
$this->assertEquals(26.00, $result['price']);
}
// ========================================
// 8. 기본통화는 변환 없이 반환 테스트
// ========================================
public function test_it_returns_base_price_for_default_currency(): void
{
// Given
$basePrice = 100000; // 100,000 KRW
// When
$result = $this->service->convertToCurrency($basePrice, 'KRW');
// Then
$this->assertEquals(100000, $result['price']);
$this->assertEquals('KRW', $result['currency']);
$this->assertArrayNotHasKey('exchange_rate', $result);
}
// ========================================
// 9. 여러 필드 그룹 변환 테스트
// ========================================
public function test_it_converts_multiple_amounts_grouped_by_currency(): void
{
// Given
$amounts = [
'subtotal' => 100000,
'discount' => 10000,
'final_amount' => 90000,
];
// When
$result = $this->service->convertMultipleAmounts($amounts);
// Then
// KRW
$this->assertArrayHasKey('KRW', $result);
$this->assertEquals(100000, $result['KRW']['subtotal']);
$this->assertEquals(10000, $result['KRW']['discount']);
$this->assertEquals(90000, $result['KRW']['final_amount']);
$this->assertTrue($result['KRW']['_meta']['is_default']);
// USD
$this->assertArrayHasKey('USD', $result);
$this->assertEquals(85.00, $result['USD']['subtotal']);
$this->assertEquals(8.50, $result['USD']['discount']);
$this->assertEquals(76.50, $result['USD']['final_amount']);
$this->assertFalse($result['USD']['_meta']['is_default']);
$this->assertEquals(0.85, $result['USD']['_meta']['exchange_rate']);
// JPY
$this->assertArrayHasKey('JPY', $result);
$this->assertEquals(11500, $result['JPY']['subtotal']);
$this->assertEquals(1150, $result['JPY']['discount']);
$this->assertEquals(10350, $result['JPY']['final_amount']);
// EUR
$this->assertArrayHasKey('EUR', $result);
$this->assertEquals(78.00, $result['EUR']['subtotal']);
$this->assertEquals(7.80, $result['EUR']['discount']);
$this->assertEquals(70.20, $result['EUR']['final_amount']);
}
// ========================================
// 10. 미지원 통화 예외 테스트
// ========================================
public function test_it_throws_exception_for_unknown_currency(): void
{
// Given
$basePrice = 100000;
// Then
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Unknown currency: GBP');
// When
$this->service->convertToCurrency($basePrice, 'GBP');
}
// ========================================
// 추가 테스트: 가격 포맷팅
// ========================================
public function test_it_formats_price_for_each_currency(): void
{
// KRW
$this->assertEquals('100,000원', $this->service->formatPrice(100000, 'KRW'));
// USD
$this->assertEquals('$85.00', $this->service->formatPrice(85.00, 'USD'));
// JPY
$this->assertEquals('¥11,500', $this->service->formatPrice(11500, 'JPY'));
// EUR
$this->assertEquals('€78.00', $this->service->formatPrice(78.00, 'EUR'));
}
// ========================================
// 추가 테스트: 통화 지원 여부 확인
// ========================================
public function test_it_checks_if_currency_is_supported(): void
{
$this->assertTrue($this->service->isSupportedCurrency('KRW'));
$this->assertTrue($this->service->isSupportedCurrency('USD'));
$this->assertTrue($this->service->isSupportedCurrency('JPY'));
$this->assertTrue($this->service->isSupportedCurrency('EUR'));
// GBP is not in our test settings
$this->assertFalse($this->service->isSupportedCurrency('GBP'));
$this->assertFalse($this->service->isSupportedCurrency('XYZ'));
}
// ========================================
// 추가 테스트: 다통화 변환 (convertToMultiCurrency)
// ========================================
public function test_it_converts_single_price_to_all_currencies(): void
{
// Given
$basePrice = 50000;
// When
$result = $this->service->convertToMultiCurrency($basePrice);
// Then
// KRW
$this->assertArrayHasKey('KRW', $result);
$this->assertEquals(50000, $result['KRW']['price']);
$this->assertTrue($result['KRW']['is_default']);
// USD: 50000 / 1000 * 0.85 = 42.50
$this->assertArrayHasKey('USD', $result);
$this->assertEquals(42.50, $result['USD']['price']);
// JPY: 50000 / 1000 * 115 = 5750
$this->assertArrayHasKey('JPY', $result);
$this->assertEquals(5750, $result['JPY']['price']);
// EUR: 50000 / 1000 * 0.78 = 39.00
$this->assertArrayHasKey('EUR', $result);
$this->assertEquals(39.00, $result['EUR']['price']);
}
// ========================================
// 추가 테스트: 소수 자릿수 조회 (getDecimalPlaces)
// ========================================
public function test_it_returns_decimal_places_for_each_currency(): void
{
// KRW: 소수 자릿수 0
$this->assertEquals(0, $this->service->getDecimalPlaces('KRW'));
// USD: 소수 자릿수 2
$this->assertEquals(2, $this->service->getDecimalPlaces('USD'));
// JPY: 소수 자릿수 0
$this->assertEquals(0, $this->service->getDecimalPlaces('JPY'));
// EUR: 소수 자릿수 2
$this->assertEquals(2, $this->service->getDecimalPlaces('EUR'));
}
public function test_it_returns_default_decimal_places_for_unknown_currency(): void
{
// 설정에 없는 통화는 기본값 2 반환
$this->assertEquals(2, $this->service->getDecimalPlaces('GBP'));
$this->assertEquals(2, $this->service->getDecimalPlaces('XYZ'));
}
public function test_format_price_uses_decimal_places_from_settings(): void
{
// KRW: 소수 자릿수 0이므로 정수만 표시
$formatted = $this->service->formatPrice(10000.50, 'KRW');
$this->assertStringNotContainsString('.', $formatted);
// USD: 소수 자릿수 2이므로 소수점 표시
$formatted = $this->service->formatPrice(100.5, 'USD');
$this->assertStringContainsString('.50', $formatted);
}
}