Phase 7 complete (5 plans): - 07-01: Performance (N+1→LEFT JOIN, static cache, DB indexes) - 07-02: Stability (SSL verification, cron throttle DB, migration 000014b) - 07-03: UX (orderpro_to_allegro disable, lista zamówień fixes, SSL hotfix) - 07-04: Tests (12 unit tests for AllegroTokenManager + AllegroOrderImportService) - 07-05: InPost ShipX API (natywny provider, workaround remap usunięty) Additional fixes: - 5 broken use-statements fixed across 4 files - vendor/ excluded from ftp-kr auto-upload - PHPUnit + dg/bypass-finals infrastructure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
268 lines
8.3 KiB
PHP
268 lines
8.3 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Unit;
|
|
|
|
use App\Core\Exceptions\AllegroOAuthException;
|
|
use App\Modules\Settings\AllegroIntegrationRepository;
|
|
use App\Modules\Settings\AllegroOAuthClient;
|
|
use App\Modules\Settings\AllegroTokenManager;
|
|
use DateInterval;
|
|
use DateTimeImmutable;
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class AllegroTokenManagerTest extends TestCase
|
|
{
|
|
private AllegroIntegrationRepository&MockObject $repository;
|
|
private AllegroOAuthClient&MockObject $oauthClient;
|
|
private AllegroTokenManager $manager;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->repository = $this->createMock(AllegroIntegrationRepository::class);
|
|
$this->oauthClient = $this->createMock(AllegroOAuthClient::class);
|
|
$this->manager = new AllegroTokenManager($this->repository, $this->oauthClient);
|
|
}
|
|
|
|
public function testResolveTokenReturnsCachedTokenWhenFresh(): void
|
|
{
|
|
$expiresAt = (new DateTimeImmutable('now'))
|
|
->add(new DateInterval('PT30M'))
|
|
->format('Y-m-d H:i:s');
|
|
|
|
$this->repository
|
|
->method('getTokenCredentials')
|
|
->willReturn([
|
|
'environment' => 'sandbox',
|
|
'client_id' => 'cid',
|
|
'client_secret' => 'cs',
|
|
'refresh_token' => 'rt',
|
|
'access_token' => 'fresh-token',
|
|
'token_expires_at' => $expiresAt,
|
|
]);
|
|
|
|
$this->oauthClient
|
|
->expects($this->never())
|
|
->method('refreshAccessToken');
|
|
|
|
$result = $this->manager->resolveToken();
|
|
|
|
$this->assertSame('fresh-token', $result[0]);
|
|
$this->assertSame('sandbox', $result[1]);
|
|
}
|
|
|
|
public function testResolveTokenRefreshesWhenExpiresWithinFiveMinutes(): void
|
|
{
|
|
$expiresAt = (new DateTimeImmutable('now'))
|
|
->add(new DateInterval('PT3M'))
|
|
->format('Y-m-d H:i:s');
|
|
|
|
$credentials = [
|
|
'environment' => 'production',
|
|
'client_id' => 'cid',
|
|
'client_secret' => 'cs',
|
|
'refresh_token' => 'rt',
|
|
'access_token' => 'old-token',
|
|
'token_expires_at' => $expiresAt,
|
|
];
|
|
|
|
$this->repository
|
|
->method('getTokenCredentials')
|
|
->willReturnOnConsecutiveCalls(
|
|
$credentials,
|
|
$credentials,
|
|
array_merge($credentials, ['access_token' => 'new-token'])
|
|
);
|
|
|
|
$this->oauthClient
|
|
->expects($this->once())
|
|
->method('refreshAccessToken')
|
|
->with('production', 'cid', 'cs', 'rt')
|
|
->willReturn([
|
|
'access_token' => 'new-token',
|
|
'refresh_token' => 'new-rt',
|
|
'token_type' => 'Bearer',
|
|
'scope' => '',
|
|
'expires_in' => 3600,
|
|
]);
|
|
|
|
$this->repository
|
|
->expects($this->once())
|
|
->method('saveTokens');
|
|
|
|
$result = $this->manager->resolveToken();
|
|
|
|
$this->assertSame('new-token', $result[0]);
|
|
$this->assertSame('production', $result[1]);
|
|
}
|
|
|
|
public function testResolveTokenRefreshesWhenAlreadyExpired(): void
|
|
{
|
|
$expiresAt = (new DateTimeImmutable('now'))
|
|
->sub(new DateInterval('PT10M'))
|
|
->format('Y-m-d H:i:s');
|
|
|
|
$credentials = [
|
|
'environment' => 'sandbox',
|
|
'client_id' => 'cid',
|
|
'client_secret' => 'cs',
|
|
'refresh_token' => 'rt',
|
|
'access_token' => 'expired-token',
|
|
'token_expires_at' => $expiresAt,
|
|
];
|
|
|
|
$this->repository
|
|
->method('getTokenCredentials')
|
|
->willReturnOnConsecutiveCalls(
|
|
$credentials,
|
|
$credentials,
|
|
array_merge($credentials, ['access_token' => 'refreshed-token'])
|
|
);
|
|
|
|
$this->oauthClient
|
|
->expects($this->once())
|
|
->method('refreshAccessToken')
|
|
->willReturn([
|
|
'access_token' => 'refreshed-token',
|
|
'refresh_token' => 'rt',
|
|
'token_type' => 'Bearer',
|
|
'scope' => '',
|
|
'expires_in' => 3600,
|
|
]);
|
|
|
|
$this->repository->expects($this->once())->method('saveTokens');
|
|
|
|
$result = $this->manager->resolveToken();
|
|
|
|
$this->assertSame('refreshed-token', $result[0]);
|
|
}
|
|
|
|
public function testResolveTokenThrowsWhenNoOAuthConfig(): void
|
|
{
|
|
$this->repository
|
|
->method('getTokenCredentials')
|
|
->willReturn(null);
|
|
|
|
$this->expectException(AllegroOAuthException::class);
|
|
$this->expectExceptionMessage('Brak polaczenia OAuth Allegro');
|
|
|
|
$this->manager->resolveToken();
|
|
}
|
|
|
|
public function testResolveTokenRefreshesWhenAccessTokenEmpty(): void
|
|
{
|
|
$credentials = [
|
|
'environment' => 'sandbox',
|
|
'client_id' => 'cid',
|
|
'client_secret' => 'cs',
|
|
'refresh_token' => 'rt',
|
|
'access_token' => '',
|
|
'token_expires_at' => '',
|
|
];
|
|
|
|
$this->repository
|
|
->method('getTokenCredentials')
|
|
->willReturnOnConsecutiveCalls(
|
|
$credentials,
|
|
$credentials,
|
|
array_merge($credentials, ['access_token' => 'brand-new'])
|
|
);
|
|
|
|
$this->oauthClient
|
|
->expects($this->once())
|
|
->method('refreshAccessToken')
|
|
->willReturn([
|
|
'access_token' => 'brand-new',
|
|
'refresh_token' => 'rt',
|
|
'token_type' => 'Bearer',
|
|
'scope' => '',
|
|
'expires_in' => 3600,
|
|
]);
|
|
|
|
$this->repository->expects($this->once())->method('saveTokens');
|
|
|
|
$result = $this->manager->resolveToken();
|
|
|
|
$this->assertSame('brand-new', $result[0]);
|
|
}
|
|
|
|
public function testForceRefreshReReadsTokenFromRepository(): void
|
|
{
|
|
$expiresAt = (new DateTimeImmutable('now'))
|
|
->sub(new DateInterval('PT1M'))
|
|
->format('Y-m-d H:i:s');
|
|
|
|
$credentials = [
|
|
'environment' => 'sandbox',
|
|
'client_id' => 'cid',
|
|
'client_secret' => 'cs',
|
|
'refresh_token' => 'rt',
|
|
'access_token' => 'stale',
|
|
'token_expires_at' => $expiresAt,
|
|
];
|
|
|
|
$this->repository
|
|
->expects($this->exactly(3))
|
|
->method('getTokenCredentials')
|
|
->willReturnOnConsecutiveCalls(
|
|
$credentials,
|
|
$credentials,
|
|
array_merge($credentials, ['access_token' => 'from-db'])
|
|
);
|
|
|
|
$this->oauthClient
|
|
->method('refreshAccessToken')
|
|
->willReturn([
|
|
'access_token' => 'api-returned',
|
|
'refresh_token' => 'rt',
|
|
'token_type' => 'Bearer',
|
|
'scope' => '',
|
|
'expires_in' => 3600,
|
|
]);
|
|
|
|
$this->repository->expects($this->once())->method('saveTokens');
|
|
|
|
$result = $this->manager->resolveToken();
|
|
|
|
// Token comes from re-read (3rd call), not directly from API response
|
|
$this->assertSame('from-db', $result[0]);
|
|
}
|
|
|
|
public function testResolveTokenRefreshesWhenExpiresAtInvalidFormat(): void
|
|
{
|
|
$credentials = [
|
|
'environment' => 'sandbox',
|
|
'client_id' => 'cid',
|
|
'client_secret' => 'cs',
|
|
'refresh_token' => 'rt',
|
|
'access_token' => 'some-token',
|
|
'token_expires_at' => 'not-a-date',
|
|
];
|
|
|
|
$this->repository
|
|
->method('getTokenCredentials')
|
|
->willReturnOnConsecutiveCalls(
|
|
$credentials,
|
|
$credentials,
|
|
array_merge($credentials, ['access_token' => 'refreshed'])
|
|
);
|
|
|
|
$this->oauthClient
|
|
->expects($this->once())
|
|
->method('refreshAccessToken')
|
|
->willReturn([
|
|
'access_token' => 'refreshed',
|
|
'refresh_token' => 'rt',
|
|
'token_type' => 'Bearer',
|
|
'scope' => '',
|
|
'expires_in' => 3600,
|
|
]);
|
|
|
|
$this->repository->expects($this->once())->method('saveTokens');
|
|
|
|
$result = $this->manager->resolveToken();
|
|
$this->assertSame('refreshed', $result[0]);
|
|
}
|
|
}
|