Files
Gnuboard7/tests/Unit/Helpers/TimezoneHelperTest.php
T
2026-04-01 10:30:52 +09:00

368 lines
12 KiB
PHP

<?php
namespace Tests\Unit\Helpers;
use App\Helpers\TimezoneHelper;
use Carbon\Carbon;
use Illuminate\Support\Facades\App;
use Tests\TestCase;
/**
* TimezoneHelper 단위 테스트
*/
class TimezoneHelperTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
// 테스트 시간 고정 (UTC 기준 2025-01-15 00:00:00)
Carbon::setTestNow(Carbon::parse('2025-01-15 00:00:00', 'UTC'));
}
protected function tearDown(): void
{
Carbon::setTestNow();
parent::tearDown();
}
/**
* user_timezone 바인딩이 없을 때 config 기본값을 반환하는지 테스트합니다.
*/
public function test_get_user_timezone_returns_config_default(): void
{
App::forgetInstance('user_timezone');
$this->assertEquals(
config('app.default_user_timezone', 'Asia/Seoul'),
TimezoneHelper::getUserTimezone()
);
}
/**
* user_timezone 바인딩이 있을 때 해당 값을 반환하는지 테스트합니다.
*/
public function test_get_user_timezone_returns_bound_value(): void
{
App::instance('user_timezone', 'America/New_York');
$this->assertEquals('America/New_York', TimezoneHelper::getUserTimezone());
}
/**
* null datetime에 대해 null을 반환하는지 테스트합니다.
*/
public function test_to_user_timezone_returns_null_for_null(): void
{
$this->assertNull(TimezoneHelper::toUserTimezone(null));
}
/**
* ISO8601 형식으로 사용자 타임존 변환이 올바른지 테스트합니다.
*/
public function test_to_user_timezone_converts_to_iso8601(): void
{
App::instance('user_timezone', 'Asia/Seoul');
$utcDatetime = Carbon::parse('2025-01-15 00:00:00', 'UTC');
$result = TimezoneHelper::toUserTimezone($utcDatetime);
$this->assertNotNull($result);
// ISO8601 파싱 후 +09:00 확인
$parsed = Carbon::parse($result);
$this->assertEquals('+09:00', $parsed->timezone->getName());
$this->assertEquals(9, $parsed->hour);
}
/**
* Y-m-d H:i:s 형식 변환이 올바른지 테스트합니다.
*/
public function test_to_user_date_time_string_converts_correctly(): void
{
App::instance('user_timezone', 'Asia/Seoul');
$utcDatetime = Carbon::parse('2025-01-15 00:00:00', 'UTC');
$result = TimezoneHelper::toUserDateTimeString($utcDatetime);
$this->assertEquals('2025-01-15 09:00:00', $result);
}
/**
* null datetime에 대해 toUserDateTimeString이 null을 반환하는지 테스트합니다.
*/
public function test_to_user_date_time_string_returns_null_for_null(): void
{
$this->assertNull(TimezoneHelper::toUserDateTimeString(null));
}
/**
* Y-m-d 형식 변환이 올바른지 테스트합니다.
*/
public function test_to_user_date_string_converts_correctly(): void
{
App::instance('user_timezone', 'Asia/Seoul');
// UTC 23:00 → Asia/Seoul 다음날 08:00
$utcDatetime = Carbon::parse('2025-01-15 23:00:00', 'UTC');
$result = TimezoneHelper::toUserDateString($utcDatetime);
// UTC 23시 + 9시간 = 다음날 08시
$this->assertEquals('2025-01-16', $result);
}
/**
* null datetime에 대해 toUserDateString이 null을 반환하는지 테스트합니다.
*/
public function test_to_user_date_string_returns_null_for_null(): void
{
$this->assertNull(TimezoneHelper::toUserDateString(null));
}
/**
* toUserCarbon이 사용자 타임존의 Carbon 인스턴스를 반환하는지 테스트합니다.
*/
public function test_to_user_carbon_returns_carbon_with_user_timezone(): void
{
App::instance('user_timezone', 'America/New_York');
$utcDatetime = Carbon::parse('2025-01-15 12:00:00', 'UTC');
$result = TimezoneHelper::toUserCarbon($utcDatetime);
$this->assertInstanceOf(Carbon::class, $result);
$this->assertEquals('America/New_York', $result->timezone->getName());
$this->assertEquals(7, $result->hour); // UTC 12:00 → EST 07:00
}
/**
* null datetime에 대해 toUserCarbon이 null을 반환하는지 테스트합니다.
*/
public function test_to_user_carbon_returns_null_for_null(): void
{
$this->assertNull(TimezoneHelper::toUserCarbon(null));
}
/**
* toUserCarbon이 원본 datetime을 변경하지 않는지 테스트합니다 (immutability).
*/
public function test_to_user_carbon_does_not_mutate_original(): void
{
App::instance('user_timezone', 'Asia/Seoul');
$original = Carbon::parse('2025-01-15 00:00:00', 'UTC');
TimezoneHelper::toUserCarbon($original);
$this->assertEquals('UTC', $original->timezone->getName());
$this->assertEquals(0, $original->hour);
}
/**
* toUserTimezone도 원본 datetime을 변경하지 않는지 테스트합니다 (immutability).
*/
public function test_to_user_timezone_does_not_mutate_original(): void
{
App::instance('user_timezone', 'Asia/Seoul');
$original = Carbon::parse('2025-01-15 00:00:00', 'UTC');
TimezoneHelper::toUserTimezone($original);
$this->assertEquals('UTC', $original->timezone->getName());
$this->assertEquals(0, $original->hour);
}
/**
* toUserCarbon에서 diffForHumans를 사용할 수 있는지 테스트합니다.
*/
public function test_to_user_carbon_supports_diff_for_humans(): void
{
App::instance('user_timezone', 'Asia/Seoul');
$utcDatetime = Carbon::now('UTC')->subHours(2);
$result = TimezoneHelper::toUserCarbon($utcDatetime);
$this->assertNotNull($result);
$this->assertIsString($result->diffForHumans());
}
// ==================== getSiteTimezone ====================
/**
* getSiteTimezone이 config 값을 반환하는지 테스트합니다.
*/
public function test_get_site_timezone_returns_config_value(): void
{
$expected = config('app.default_user_timezone');
$this->assertEquals($expected, TimezoneHelper::getSiteTimezone());
}
// ==================== toSiteDateString ====================
/**
* toSiteDateString이 사이트 타임존 기준 Y-m-d를 반환하는지 테스트합니다.
*/
public function test_to_site_date_string_converts_correctly(): void
{
config(['app.default_user_timezone' => 'Asia/Seoul']);
// UTC 15:00 → KST 다음날 00:00
$utcDatetime = Carbon::parse('2025-01-15 15:00:00', 'UTC');
$result = TimezoneHelper::toSiteDateString($utcDatetime);
$this->assertEquals('2025-01-16', $result);
}
/**
* toSiteDateString이 null에 대해 null을 반환하는지 테스트합니다.
*/
public function test_to_site_date_string_returns_null_for_null(): void
{
$this->assertNull(TimezoneHelper::toSiteDateString(null));
}
// ==================== fromSiteDateStartOfDay ====================
/**
* fromSiteDateStartOfDay가 사이트 타임존 00:00:00을 UTC로 변환하는지 테스트합니다.
*/
public function test_from_site_date_start_of_day_converts_to_utc(): void
{
config(['app.default_user_timezone' => 'Asia/Seoul']);
$result = TimezoneHelper::fromSiteDateStartOfDay('2025-03-15');
// 2025-03-15 00:00:00 KST = 2025-03-14 15:00:00 UTC
$this->assertInstanceOf(Carbon::class, $result);
$this->assertEquals('2025-03-14 15:00:00', $result->format('Y-m-d H:i:s'));
$this->assertEquals('UTC', $result->timezone->getName());
}
/**
* fromSiteDateStartOfDay가 null에 대해 null을 반환하는지 테스트합니다.
*/
public function test_from_site_date_start_of_day_returns_null_for_null(): void
{
$this->assertNull(TimezoneHelper::fromSiteDateStartOfDay(null));
}
// ==================== fromSiteDateEndOfDay ====================
/**
* fromSiteDateEndOfDay가 사이트 타임존 23:59:59를 UTC로 변환하는지 테스트합니다.
*/
public function test_from_site_date_end_of_day_converts_to_utc(): void
{
config(['app.default_user_timezone' => 'Asia/Seoul']);
$result = TimezoneHelper::fromSiteDateEndOfDay('2025-03-20');
// 2025-03-20 23:59:59 KST = 2025-03-20 14:59:59 UTC
$this->assertInstanceOf(Carbon::class, $result);
$this->assertEquals('2025-03-20 14:59:59', $result->format('Y-m-d H:i:s'));
$this->assertEquals('UTC', $result->timezone->getName());
}
/**
* fromSiteDateEndOfDay가 null에 대해 null을 반환하는지 테스트합니다.
*/
public function test_from_site_date_end_of_day_returns_null_for_null(): void
{
$this->assertNull(TimezoneHelper::fromSiteDateEndOfDay(null));
}
// ==================== 왕복 변환 테스트 ====================
/**
* fromSiteDateStartOfDay → toSiteDateString 왕복 변환이 원래 날짜를 유지하는지 테스트합니다.
*/
public function test_round_trip_start_of_day_preserves_date(): void
{
config(['app.default_user_timezone' => 'Asia/Seoul']);
$originalDate = '2025-06-15';
$utcCarbon = TimezoneHelper::fromSiteDateStartOfDay($originalDate);
$result = TimezoneHelper::toSiteDateString($utcCarbon);
$this->assertEquals($originalDate, $result);
}
/**
* fromSiteDateEndOfDay → toSiteDateString 왕복 변환이 원래 날짜를 유지하는지 테스트합니다.
*/
public function test_round_trip_end_of_day_preserves_date(): void
{
config(['app.default_user_timezone' => 'Asia/Seoul']);
$originalDate = '2025-06-15';
$utcCarbon = TimezoneHelper::fromSiteDateEndOfDay($originalDate);
$result = TimezoneHelper::toSiteDateString($utcCarbon);
$this->assertEquals($originalDate, $result);
}
// ==================== toSiteDateTimeLocalString ====================
/**
* toSiteDateTimeLocalString이 datetime-local 호환 형식을 반환하는지 테스트합니다.
*/
public function test_to_site_date_time_local_string_converts_correctly(): void
{
config(['app.default_user_timezone' => 'Asia/Seoul']);
// UTC 15:00 → KST 다음날 00:00
$utcDatetime = Carbon::parse('2025-01-15 15:00:00', 'UTC');
$result = TimezoneHelper::toSiteDateTimeLocalString($utcDatetime);
$this->assertEquals('2025-01-16T00:00', $result);
}
/**
* toSiteDateTimeLocalString이 null에 대해 null을 반환하는지 테스트합니다.
*/
public function test_to_site_date_time_local_string_returns_null_for_null(): void
{
$this->assertNull(TimezoneHelper::toSiteDateTimeLocalString(null));
}
// ==================== fromSiteDateTime ====================
/**
* fromSiteDateTime이 datetime-local 입력을 사이트 타임존 기준 UTC로 변환하는지 테스트합니다.
*/
public function test_from_site_date_time_converts_to_utc(): void
{
config(['app.default_user_timezone' => 'Asia/Seoul']);
// 2025-03-15 09:30 KST = 2025-03-15 00:30:00 UTC
$result = TimezoneHelper::fromSiteDateTime('2025-03-15T09:30');
$this->assertInstanceOf(Carbon::class, $result);
$this->assertEquals('2025-03-15 00:30:00', $result->format('Y-m-d H:i:s'));
$this->assertEquals('UTC', $result->timezone->getName());
}
/**
* fromSiteDateTime이 null에 대해 null을 반환하는지 테스트합니다.
*/
public function test_from_site_date_time_returns_null_for_null(): void
{
$this->assertNull(TimezoneHelper::fromSiteDateTime(null));
}
// ==================== datetime-local 왕복 변환 ====================
/**
* fromSiteDateTime → toSiteDateTimeLocalString 왕복 변환이 원래 값을 유지하는지 테스트합니다.
*/
public function test_round_trip_date_time_local_preserves_value(): void
{
config(['app.default_user_timezone' => 'Asia/Seoul']);
$original = '2025-06-15T14:30';
$utcCarbon = TimezoneHelper::fromSiteDateTime($original);
$result = TimezoneHelper::toSiteDateTimeLocalString($utcCarbon);
$this->assertEquals($original, $result);
}
}