db = TestDatabase::getInstance(); $this->pdo = TestDatabase::getPDO(); } /** * Seed a thesis + an approved file-access request, returning * [thesisId, requestId]. */ private function seedApprovedRequest(): array { [$authorId, $thesisId] = TestDatabase::seedBasicThesis(); $requestId = $this->db->createFileAccessRequest($thesisId, 'xamxam@erg.be', null); // Approve it (status must be 'approved' for redemption to succeed). $this->pdo->prepare( "UPDATE file_access_requests SET status = 'approved', approved_at = CURRENT_TIMESTAMP WHERE id = ?" )->execute([$requestId]); return [$thesisId, $requestId]; } // ── generateAccessToken ────────────────────────────────────────────────── public function testGenerateAccessTokenReturns256BitHex(): void { [$thesisId, $requestId] = $this->seedApprovedRequest(); $token = $this->db->generateAccessToken($requestId, 24); $this->assertMatchesRegularExpression('/^[0-9a-f]{64}$/', $token); } public function testGenerateAccessTokenPersistsRowWithExpectedState(): void { [$thesisId, $requestId] = $this->seedApprovedRequest(); $token = $this->db->generateAccessToken($requestId, 24); $stmt = $this->pdo->prepare('SELECT * FROM file_access_tokens WHERE request_id = ?'); $stmt->execute([$requestId]); $row = $stmt->fetch(); $this->assertNotFalse($row); $this->assertSame($token, $row['token'], 'Current impl stores the plaintext token'); $this->assertSame(1, (int) $row['is_valid']); $this->assertNull($row['used_at']); $this->assertNotNull($row['expires_at']); } // ── redeemAccessToken ──────────────────────────────────────────────────── public function testRedeemValidTokenReturnsThesisAndRequestIds(): void { [$thesisId, $requestId] = $this->seedApprovedRequest(); $token = $this->db->generateAccessToken($requestId, 24); $result = $this->db->redeemAccessToken($token, '1.2.3.4', 'ua'); $this->assertNotNull($result); $this->assertSame($thesisId, $result['thesis_id']); $this->assertSame($requestId, $result['request_id']); } public function testRedeemMarksTokenUsed(): void { [$thesisId, $requestId] = $this->seedApprovedRequest(); $token = $this->db->generateAccessToken($requestId, 24); $this->db->redeemAccessToken($token, '1.2.3.4', 'ua'); $stmt = $this->pdo->prepare('SELECT used_at, is_valid FROM file_access_tokens WHERE request_id = ?'); $stmt->execute([$requestId]); $row = $stmt->fetch(); $this->assertNotNull($row['used_at']); $this->assertSame(0, (int) $row['is_valid']); } public function testRedeemIsOneTimeUse(): void { [$thesisId, $requestId] = $this->seedApprovedRequest(); $token = $this->db->generateAccessToken($requestId, 24); $first = $this->db->redeemAccessToken($token, '1.2.3.4', 'ua'); $second = $this->db->redeemAccessToken($token, '1.2.3.4', 'ua'); $this->assertNotNull($first); $this->assertNull($second); } public function testRedeemUnknownTokenReturnsNull(): void { $result = $this->db->redeemAccessToken(str_repeat('ab', 32), '1.2.3.4', 'ua'); $this->assertNull($result); } public function testRedeemExpiredTokenReturnsNull(): void { [$thesisId, $requestId] = $this->seedApprovedRequest(); // Negative hours → already expired $token = $this->db->generateAccessToken($requestId, -1); $result = $this->db->redeemAccessToken($token, '1.2.3.4', 'ua'); $this->assertNull($result); } public function testRedeemLogsAuditOnSuccess(): void { [$thesisId, $requestId] = $this->seedApprovedRequest(); $token = $this->db->generateAccessToken($requestId, 24); $this->db->redeemAccessToken($token, '1.2.3.4', 'ua'); $stmt = $this->pdo->prepare( "SELECT COUNT(*) FROM file_access_audit WHERE request_id = ? AND event = 'redeemed'" ); $stmt->execute([$requestId]); $this->assertSame(1, (int) $stmt->fetchColumn()); } public function testRedeemDoesNotRequireThesisIdArgument(): void { // The thesis is resolved from the request JOIN, not passed in. [$thesisId, $requestId] = $this->seedApprovedRequest(); $token = $this->db->generateAccessToken($requestId, 24); // Approving a different request must not affect this one's scoping — // the returned thesis_id is derived from file_access_requests. $result = $this->db->redeemAccessToken($token, '1.2.3.4', 'ua'); $this->assertSame($thesisId, $result['thesis_id']); } }