[], ]; } protected function tearDown(): void { $_COOKIE = []; unset($GLOBALS['sync_basalam_oauth_test_user_id']); } public function testOauthProofDoesNotDependOnTransientStorage(): void { self::assertNotFalse(OAuthManager::issueOauthState()); self::assertArrayHasKey(OAuthManager::OAUTH_STATE_COOKIE, $_COOKIE); self::assertNotContains('set_transient', $GLOBALS['sync_basalam_jobs_runner_test_state']['events']); self::assertTrue($this->verifyOauthState()); self::assertArrayNotHasKey(OAuthManager::OAUTH_STATE_COOKIE, $_COOKIE); } public function testOauthProofIsSingleUse(): void { self::assertNotFalse(OAuthManager::issueOauthState()); self::assertTrue($this->verifyOauthState()); self::assertFalse($this->verifyOauthState()); } public function testTamperedOauthProofIsRejectedAndConsumed(): void { self::assertNotFalse(OAuthManager::issueOauthState()); $_COOKIE[OAuthManager::OAUTH_STATE_COOKIE] .= 'tampered'; self::assertFalse($this->verifyOauthState()); self::assertArrayNotHasKey(OAuthManager::OAUTH_STATE_COOKIE, $_COOKIE); } public function testOauthProofIsBoundToTheStartingAdministrator(): void { self::assertNotFalse(OAuthManager::issueOauthState()); $GLOBALS['sync_basalam_oauth_test_user_id'] = 99; self::assertFalse($this->verifyOauthState()); } public function testExpiredOauthProofIsRejected(): void { $_COOKIE[OAuthManager::OAUTH_STATE_COOKIE] = $this->buildOauthStateCookieValue( 'expired-state', 42, time() - 1 ); self::assertFalse($this->verifyOauthState()); } private function buildOauthStateCookieValue(string $state, int $userId, int $expiresAt): string { $method = new ReflectionMethod(OAuthManager::class, 'buildOauthStateCookieValue'); $method->setAccessible(true); return (string) $method->invoke(null, $state, $userId, $expiresAt); } private function verifyOauthState(): bool { $method = new ReflectionMethod(OAuthManager::class, 'verifyOauthState'); $method->setAccessible(true); return (bool) $method->invoke(null); } } }