Files
michaelschiemer/tests/Unit/Framework/OAuth/Storage/StoredOAuthTokenTest.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

188 lines
8.7 KiB
PHP

<?php
declare(strict_types=1);
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\OAuth\Storage\StoredOAuthToken;
use App\Framework\OAuth\ValueObjects\AccessToken;
use App\Framework\OAuth\ValueObjects\OAuthToken;
use App\Framework\OAuth\ValueObjects\RefreshToken;
use App\Framework\OAuth\ValueObjects\TokenType;
describe('StoredOAuthToken Entity', function () {
it('creates new stored token', function () {
$accessToken = AccessToken::create('access_1234567890', Timestamp::now()->add(Duration::fromSeconds(3600)));
$oauthToken = new OAuthToken($accessToken, null, TokenType::BEARER, null);
$stored = StoredOAuthToken::create('user-123', 'spotify', $oauthToken);
expect($stored->id)->toBeNull(); // Not persisted yet
expect($stored->userId)->toBe('user-123');
expect($stored->provider)->toBe('spotify');
expect($stored->token)->toBe($oauthToken);
});
it('sets timestamps on creation', function () {
$accessToken = AccessToken::create('access_1234567890', Timestamp::now()->add(Duration::fromSeconds(3600)));
$oauthToken = new OAuthToken($accessToken, null, TokenType::BEARER, null);
$beforeCreate = Timestamp::now();
$stored = StoredOAuthToken::create('user-123', 'spotify', $oauthToken);
$afterCreate = Timestamp::now();
expect($stored->createdAt->toTimestamp())->toBeGreaterThanOrEqual($beforeCreate->toTimestamp());
expect($stored->createdAt->toTimestamp())->toBeLessThanOrEqual($afterCreate->toTimestamp());
expect($stored->updatedAt->toTimestamp())->toBe($stored->createdAt->toTimestamp());
});
it('creates from database row', function () {
$now = Timestamp::now();
$row = [
'id' => 1,
'user_id' => 'user-456',
'provider' => 'github',
'access_token' => 'access_1234567890',
'refresh_token' => 'refresh_1234567890',
'token_type' => 'Bearer',
'expires_at' => $now->add(Duration::fromSeconds(3600))->format('Y-m-d H:i:s'),
'scope' => 'read write',
'created_at' => $now->format('Y-m-d H:i:s'),
'updated_at' => $now->format('Y-m-d H:i:s'),
];
$stored = StoredOAuthToken::fromArray($row);
expect($stored->id)->toBe(1);
expect($stored->userId)->toBe('user-456');
expect($stored->provider)->toBe('github');
expect($stored->token->accessToken->toString())->toBe('access_1234567890');
expect($stored->token->refreshToken->toString())->toBe('refresh_1234567890');
});
it('updates token immutably', function () {
$originalAccessToken = AccessToken::create('original_access_1234567890', Timestamp::now()->add(Duration::fromSeconds(3600)));
$originalToken = new OAuthToken($originalAccessToken, null, TokenType::BEARER, null);
$originalStored = StoredOAuthToken::create('user-123', 'spotify', $originalToken);
$newAccessToken = AccessToken::create('new_access_1234567890', Timestamp::now()->add(Duration::fromSeconds(7200)));
$newToken = new OAuthToken($newAccessToken, null, TokenType::BEARER, null);
$updatedStored = $originalStored->withRefreshedToken($newToken);
// Original unchanged
expect($originalStored->token->accessToken->toString())->toBe('original_access_1234567890');
expect($originalStored->userId)->toBe('user-123');
// New instance updated
expect($updatedStored->token->accessToken->toString())->toBe('new_access_1234567890');
expect($updatedStored->userId)->toBe('user-123');
expect($updatedStored->provider)->toBe('spotify');
});
it('updates updatedAt timestamp on token refresh', function () {
$originalAccessToken = AccessToken::create('original_1234567890', Timestamp::now()->add(Duration::fromSeconds(3600)));
$originalToken = new OAuthToken($originalAccessToken, null, TokenType::BEARER, null);
$originalStored = StoredOAuthToken::create('user-123', 'spotify', $originalToken);
// Wait a full second to ensure different timestamp
sleep(1);
$newAccessToken = AccessToken::create('new_1234567890', Timestamp::now()->add(Duration::fromSeconds(7200)));
$newToken = new OAuthToken($newAccessToken, null, TokenType::BEARER, null);
$updatedStored = $originalStored->withRefreshedToken($newToken);
expect($updatedStored->updatedAt->toTimestamp())
->toBeGreaterThanOrEqual($originalStored->updatedAt->toTimestamp() + 1);
expect($updatedStored->createdAt->toTimestamp())
->toBe($originalStored->createdAt->toTimestamp());
});
it('preserves id during token refresh', function () {
$accessToken = AccessToken::create('access_1234567890', Timestamp::now()->add(Duration::fromSeconds(3600)));
$oauthToken = new OAuthToken($accessToken, null, TokenType::BEARER, null);
$stored = new StoredOAuthToken(
id: 42,
userId: 'user-123',
provider: 'spotify',
token: $oauthToken,
createdAt: Timestamp::now(),
updatedAt: Timestamp::now()
);
$newAccessToken = AccessToken::create('new_1234567890', Timestamp::now()->add(Duration::fromSeconds(7200)));
$newToken = new OAuthToken($newAccessToken, null, TokenType::BEARER, null);
$updated = $stored->withRefreshedToken($newToken);
expect($updated->id)->toBe(42);
});
it('detects expired token', function () {
$expiredAccessToken = AccessToken::create('expired_1234567890', Timestamp::now()->subtract(Duration::fromSeconds(100)));
$expiredToken = new OAuthToken($expiredAccessToken, null, TokenType::BEARER, null);
$stored = StoredOAuthToken::create('user-123', 'spotify', $expiredToken);
expect($stored->isExpired())->toBeTrue();
});
it('detects valid non-expired token', function () {
$validAccessToken = AccessToken::create('valid_1234567890', Timestamp::now()->add(Duration::fromSeconds(3600)));
$validToken = new OAuthToken($validAccessToken, null, TokenType::BEARER, null);
$stored = StoredOAuthToken::create('user-123', 'spotify', $validToken);
expect($stored->isExpired())->toBeFalse();
});
it('checks if token can refresh', function () {
$accessToken = AccessToken::create('access_1234567890', Timestamp::now()->add(Duration::fromSeconds(3600)));
$refreshToken = RefreshToken::create('refresh_1234567890');
$oauthToken = new OAuthToken($accessToken, $refreshToken, TokenType::BEARER, null);
$stored = StoredOAuthToken::create('user-123', 'spotify', $oauthToken);
expect($stored->canRefresh())->toBeTrue();
});
it('checks if token cannot refresh without refresh token', function () {
$accessToken = AccessToken::create('access_1234567890', Timestamp::now()->add(Duration::fromSeconds(3600)));
$oauthToken = new OAuthToken($accessToken, null, TokenType::BEARER, null);
$stored = StoredOAuthToken::create('user-123', 'spotify', $oauthToken);
expect($stored->canRefresh())->toBeFalse();
});
it('converts to array for database storage', function () {
$accessToken = AccessToken::create('access_1234567890', Timestamp::now()->add(Duration::fromSeconds(3600)));
$refreshToken = RefreshToken::create('refresh_1234567890');
$oauthToken = new OAuthToken($accessToken, $refreshToken, TokenType::BEARER, null);
$stored = new StoredOAuthToken(
id: 1,
userId: 'user-123',
provider: 'spotify',
token: $oauthToken,
createdAt: Timestamp::now(),
updatedAt: Timestamp::now()
);
$array = $stored->toArray();
expect($array['id'])->toBe(1);
expect($array['user_id'])->toBe('user-123');
expect($array['provider'])->toBe('spotify');
expect($array['access_token'])->toBe('access_1234567890');
expect($array['refresh_token'])->toBe('refresh_1234567890');
expect($array['token_type'])->toBe('Bearer');
expect($array)->toHaveKey('expires_at');
expect($array)->toHaveKey('created_at');
expect($array)->toHaveKey('updated_at');
});
it('handles null id in array conversion', function () {
$accessToken = AccessToken::create('access_1234567890', Timestamp::now()->add(Duration::fromSeconds(3600)));
$oauthToken = new OAuthToken($accessToken, null, TokenType::BEARER, null);
$stored = StoredOAuthToken::create('user-123', 'spotify', $oauthToken);
$array = $stored->toArray();
expect(array_key_exists('id', $array))->toBeFalse();
});
});