pdo = TestDatabase::getPDO(); $this->ot = new OneTimeToken($this->pdo); } public function testIssueReturns256BitHex(): void { $token = $this->ot->issue('test', 3600); $this->assertMatchesRegularExpression('/^[0-9a-f]{64}$/', $token); } public function testIssueStoresOnlyHashAndContext(): void { $token = $this->ot->issue('test', 3600, ['a' => 1]); $row = $this->pdo->query("SELECT * FROM one_time_tokens WHERE purpose = 'test'")->fetch(); $this->assertNotFalse($row); $this->assertSame(hash('sha256', $token), $row['token_hash']); $this->assertNotSame($token, $row['token_hash']); $this->assertSame(['a' => 1], json_decode($row['context'], true)); } public function testPurposeIsolation(): void { $token = $this->ot->issue('alpha', 3600); $this->assertFalse($this->ot->isValid('beta', $token)); } public function testRedeemReturnsContextAndConsumesToken(): void { $token = $this->ot->issue('test', 3600, ['k' => 'v']); $this->assertSame(['k' => 'v'], $this->ot->redeem('test', $token)); $this->assertNull($this->ot->redeem('test', $token)); } public function testExpiredTokenIsInvalidAndLookupStillFindsIt(): void { $token = $this->ot->issue('test', -10, ['k' => 'v']); $this->assertFalse($this->ot->isValid('test', $token)); $this->assertNull($this->ot->redeem('test', $token)); $row = $this->ot->lookup('test', $token); $this->assertNotNull($row); $this->assertSame(['k' => 'v'], $row['context']); } public function testLookupUnknownTokenReturnsNull(): void { $this->assertNull($this->ot->lookup('test', str_repeat('ab', 32))); } }