| Author | Dave Jarvis <email> |
|---|---|
| Date | 2026-01-25 13:31:01 GMT-0800 |
| Commit | 3fca5d7e796162ab81c18be212b4401456ef3e34 |
| Parent | 2104272 |
| private const LIFETIME = 3600; | ||
| - public function __construct() { | ||
| + private int $lifetime; | ||
| + | ||
| + public function __construct( ?int $lifetime = null ) { | ||
| + $this->lifetime = $lifetime ?? self::LIFETIME; | ||
| + | ||
| if( session_status() === PHP_SESSION_NONE ) { | ||
| session_start(); | ||
| - $this->validateSession(); | ||
| + $this->validate(); | ||
| } | ||
| } | ||
| - private function validateSession() { | ||
| + public function write( $key, $value ) { | ||
| + $_SESSION[ $key ] = $value; | ||
| + } | ||
| + | ||
| + public function read( $key ) { | ||
| + return $_SESSION[ $key ] ?? null; | ||
| + } | ||
| + | ||
| + public function clear() { | ||
| + if( session_status() === PHP_SESSION_ACTIVE ) { | ||
| + $_SESSION = []; | ||
| + session_destroy(); | ||
| + } | ||
| + } | ||
| + | ||
| + private function validate() { | ||
| if( isset( $_SESSION['last_activity'] ) ) { | ||
| $elapsed = time() - $_SESSION['last_activity']; | ||
| - if( $elapsed > self::LIFETIME ) { | ||
| + if( $elapsed > $this->lifetime ) { | ||
| session_unset(); | ||
| session_destroy(); | ||
| session_start(); | ||
| } | ||
| } | ||
| $_SESSION['last_activity'] = time(); | ||
| - } | ||
| - | ||
| - public function write( $key, $value ) { | ||
| - $_SESSION[ $key ] = $value; | ||
| - } | ||
| - | ||
| - public function read( $key ) { | ||
| - return $_SESSION[ $key ] ?? null; | ||
| } | ||
| } |
| require_once 'TestExchangeRate.php'; | ||
| require_once 'TestMoney.php'; | ||
| +require_once 'TestSession.php'; | ||
| /** | ||
| class Main { | ||
| public function run() { | ||
| + // Test sessions first to ensure headers are sent before any output occurs. | ||
| + $tests = [ | ||
| + new SessionTest(), | ||
| + new AddressTest(), | ||
| + new ExchangeRateTest(), | ||
| + new MoneyTest() | ||
| + ]; | ||
| $suite = new TestSuite(); | ||
| - $tests = [ new AddressTest(), new ExchangeRateTest(), new MoneyTest() ]; | ||
| $failed = false; | ||
| $main = new Main(); | ||
| exit( $main->run() ); | ||
| - | ||
| public function run( $callback ) { | ||
| $callback( [ | ||
| - 'test_Process_MissingFields_Incomplete', | ||
| - 'test_Process_AllFields_Normalized' | ||
| + 'test_Address_MissingFields_Incomplete', | ||
| + 'test_Address_AllFields_Normalized' | ||
| ] ); | ||
| } | ||
| - public function test_Process_MissingFields_Incomplete() { | ||
| + public function test_Address_MissingFields_Incomplete() { | ||
| $addr = new Address( [ 'name' => 'John' ] ); | ||
| $addr->process( [ 'name', 'street1', 'city' ] ); | ||
| return !$addr->isComplete(); | ||
| } | ||
| - public function test_Process_AllFields_Normalized() { | ||
| + public function test_Address_AllFields_Normalized() { | ||
| $addr = new Address( [ | ||
| 'name' => 'John Doe', |
| $callback( [ | ||
| - 'test_Convert_SameCurrency_ReturnsSameAmount', | ||
| - 'test_Convert_RoundTrip_ReturnsOriginalAmount' | ||
| + 'test_ExchangeRate_ConvertSameCurrency_ReturnsSameAmount', | ||
| + 'test_ExchangeRate_ConvertRoundTrip_ReturnsOriginalAmount' | ||
| ] ); | ||
| } | ||
| /** | ||
| * Tests conversion when source and target currencies are identical. | ||
| * | ||
| * @return bool True if conversion returns same amount. | ||
| */ | ||
| - public function test_Convert_SameCurrency_ReturnsSameAmount() { | ||
| + public function test_ExchangeRate_ConvertSameCurrency_ReturnsSameAmount() { | ||
| $forex = $this->createExchangeRate(); | ||
| $amount = $this->createAmount(); | ||
| * @return bool True if forward and reverse conversions are consistent. | ||
| */ | ||
| - public function test_Convert_RoundTrip_ReturnsOriginalAmount() { | ||
| + public function test_ExchangeRate_ConvertRoundTrip_ReturnsOriginalAmount() { | ||
| $forex = $this->createExchangeRate(); | ||
| $amount = $this->createAmount(); | ||
| public function run( $callback ) { | ||
| $callback( [ | ||
| - 'test_Constructor_NegativeAmount_CreatesInstance', | ||
| - 'test_Constructor_ZeroAmount_CreatesInstance', | ||
| - 'test_Constructor_LargeAmount_CreatesInstance', | ||
| - 'test_Constructor_ManyDecimals_CreatesInstance', | ||
| - 'test_IsCurrency_MatchingCurrency_ReturnsTrue', | ||
| - 'test_IsCurrency_DifferentCurrency_ReturnsFalse', | ||
| - 'test_IsAmount_WithinTolerance_ReturnsTrue', | ||
| - 'test_IsAmount_OutsideTolerance_ReturnsFalse' | ||
| + 'test_Money_ConstructNegativeAmount_Succeeds', | ||
| + 'test_Money_ConstructZeroAmount_Succeeds', | ||
| + 'test_Money_ConstructLargeAmount_Succeeds', | ||
| + 'test_Money_ConstructManyDecimals_Succeeds', | ||
| + 'test_Money_IsCurrencyMatching_ReturnsTrue', | ||
| + 'test_Money_IsCurrencyDifferent_ReturnsFalse', | ||
| + 'test_Money_IsAmountWithinTolerance_ReturnsTrue', | ||
| + 'test_Money_IsAmountOutsideTolerance_ReturnsFalse' | ||
| ] ); | ||
| } | ||
| - public function test_Constructor_NegativeAmount_CreatesInstance() { | ||
| + public function test_Money_ConstructNegativeAmount_Succeeds() { | ||
| $money = new Money( -250.75, Currency::EUR ); | ||
| return $money->isAmount( -250.75 ) && $money->isCurrency( Currency::EUR ); | ||
| } | ||
| - public function test_Constructor_ZeroAmount_CreatesInstance() { | ||
| + public function test_Money_ConstructZeroAmount_Succeeds() { | ||
| $money = new Money( 0.0, Currency::GBP ); | ||
| return $money->isAmount( 0.0 ) && $money->isCurrency( Currency::GBP ); | ||
| } | ||
| - public function test_Constructor_LargeAmount_CreatesInstance() { | ||
| + public function test_Money_ConstructLargeAmount_Succeeds() { | ||
| $money = new Money( 999999999.99, Currency::CAD ); | ||
| return $money->isAmount( 999999999.99 ) && $money->isCurrency( Currency::CAD ); | ||
| } | ||
| - public function test_Constructor_ManyDecimals_CreatesInstance() { | ||
| + public function test_Money_ConstructManyDecimals_Succeeds() { | ||
| $money = new Money( 123.456789, Currency::AUD ); | ||
| return $money->isAmount( 123.456789 ) && $money->isCurrency( Currency::AUD ); | ||
| } | ||
| - public function test_IsCurrency_MatchingCurrency_ReturnsTrue() { | ||
| + public function test_Money_IsCurrencyMatching_ReturnsTrue() { | ||
| $money = new Money( 50.0, Currency::USD ); | ||
| return $money->isCurrency( Currency::USD ); | ||
| } | ||
| - public function test_IsCurrency_DifferentCurrency_ReturnsFalse() { | ||
| + public function test_Money_IsCurrencyDifferent_ReturnsFalse() { | ||
| $money = new Money( 50.0, Currency::USD ); | ||
| return !$money->isCurrency( Currency::EUR ); | ||
| } | ||
| - public function test_IsAmount_WithinTolerance_ReturnsTrue() { | ||
| + public function test_Money_IsAmountWithinTolerance_ReturnsTrue() { | ||
| $money = new Money( 100.005, Currency::USD ); | ||
| return $money->isAmount( 100.00 ); | ||
| } | ||
| - public function test_IsAmount_OutsideTolerance_ReturnsFalse() { | ||
| + public function test_Money_IsAmountOutsideTolerance_ReturnsFalse() { | ||
| $money = new Money( 100.00, Currency::USD ); | ||
| return !$money->isAmount( 100.02 ); |
| class TestSuite { | ||
| public function execute( $tests, $test ) { | ||
| - $failed = true; | ||
| + $results = []; | ||
| + $failed = false; | ||
| foreach( $tests as $method ) { | ||
| - $result = $test->$method(); | ||
| - $failed = $failed && !$result; | ||
| - echo ($result ? "PASS" : "FAIL") . ": {$method}\n"; | ||
| + $passed = $test->$method(); | ||
| + $results[] = ($passed ? "PASS" : "FAIL") . ": {$method}"; | ||
| + | ||
| + $failed = $failed || !$passed; | ||
| } | ||
| + | ||
| + echo implode( "\n", $results ) . "\n"; | ||
| return $failed ? 1 : 0; |
| Delta | 63 lines added, 42 lines removed, 21-line increase |
|---|