num_rows = count($rows); } public function fetch_assoc(): ?array { return $this->rows[0] ?? null; } } } if (!class_exists('PasswordResetTokenExpiryFakeDb')) { class PasswordResetTokenExpiryFakeDb { public array $queries = []; public function __construct(private readonly array $results) { } public function escape_string(string $string): string { return addslashes($string); } public function query(string $sql): PasswordResetTokenExpiryFakeResult { $this->queries[] = $sql; return $this->results[count($this->queries) - 1] ?? new PasswordResetTokenExpiryFakeResult([]); } } } if (!class_exists('PasswordResetTokenExpiryProbe')) { class PasswordResetTokenExpiryProbe extends customer_password_reset_keys_o { public function getObjectProperties(): void { } public function forceSelectedId(int $id): void { $this->id = $id; } } } beforeEach(function (): void { $this->previousDb = $GLOBALS['db'] ?? null; }); afterEach(function (): void { if ($this->previousDb !== null) { $GLOBALS['db'] = $this->previousDb; return; } unset($GLOBALS['db']); }); it('keeps password reset tokens valid for 72 hours', function (): void { expect(customer_password_reset_keys_o::TOKEN_EXPIRY_SECONDS)->toBe(72 * 60 * 60); }); it('looks up reset tokens using the database 72 hour validity window', function (): void { $GLOBALS['db'] = new PasswordResetTokenExpiryFakeDb([ new PasswordResetTokenExpiryFakeResult([['id' => 42]]), ]); $token = str_repeat('a', customer_password_reset_keys_o::TOKEN_LENGTH); $probe = new PasswordResetTokenExpiryProbe(); $found = $probe->findValidByToken($token); expect($found)->toBe($probe) ->and($probe->id)->toBe(42) ->and($GLOBALS['db']->queries[0])->toContain('created_at >= DATE_SUB(NOW(), INTERVAL 259200 SECOND)') ->and($GLOBALS['db']->queries[0])->not->toContain("DATE_SUB('"); }); it('uses the same database 72 hour window for the selected token guard', function (): void { $GLOBALS['db'] = new PasswordResetTokenExpiryFakeDb([ new PasswordResetTokenExpiryFakeResult([['id' => 42]]), ]); $probe = new PasswordResetTokenExpiryProbe(); $probe->forceSelectedId(42); expect($probe->isValidToken())->toBeTrue() ->and($GLOBALS['db']->queries[0])->toContain('id = 42') ->and($GLOBALS['db']->queries[0])->toContain('created_at >= DATE_SUB(NOW(), INTERVAL 259200 SECOND)'); });