|
|
@@ -0,0 +1,1037 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace App\Tests\Unit\Service\HelloAsso;
|
|
|
+
|
|
|
+use App\ApiResources\HelloAsso\AuthUrl;
|
|
|
+use App\ApiResources\HelloAsso\EventForm;
|
|
|
+use App\ApiResources\HelloAsso\HelloAssoProfile;
|
|
|
+use App\Entity\Booking\Event;
|
|
|
+use App\Entity\HelloAsso\HelloAsso;
|
|
|
+use App\Entity\Organization\Organization;
|
|
|
+use App\Repository\Booking\EventRepository;
|
|
|
+use App\Repository\Organization\OrganizationRepository;
|
|
|
+use App\Service\HelloAsso\HelloAssoService;
|
|
|
+use App\Service\Security\OAuthPkceGenerator;
|
|
|
+use App\Service\Utils\DatesUtils;
|
|
|
+use App\Service\Utils\UrlBuilder;
|
|
|
+use Doctrine\ORM\EntityManagerInterface;
|
|
|
+use PHPUnit\Framework\MockObject\MockObject;
|
|
|
+use PHPUnit\Framework\TestCase;
|
|
|
+use Psr\Log\LoggerInterface;
|
|
|
+use Symfony\Contracts\HttpClient\HttpClientInterface;
|
|
|
+use Symfony\Contracts\HttpClient\ResponseInterface;
|
|
|
+
|
|
|
+// Classe testable pour accéder aux méthodes protégées
|
|
|
+class TestableHelloAssoService extends HelloAssoService
|
|
|
+{
|
|
|
+ public function getHelloAssoEntityFor(int $organizationId, bool $shallHaveToken = true): HelloAsso
|
|
|
+ {
|
|
|
+ return parent::getHelloAssoEntityFor($organizationId, $shallHaveToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function makeHelloAssoEventForm(array $formData): EventForm
|
|
|
+ {
|
|
|
+ return parent::makeHelloAssoEventForm($formData);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function getCallbackUrl(): string
|
|
|
+ {
|
|
|
+ return parent::getCallbackUrl();
|
|
|
+ }
|
|
|
+
|
|
|
+ public function fetchAccessToken(?string $authorizationCode = null, ?string $challengeVerifier = null): array
|
|
|
+ {
|
|
|
+ return parent::fetchAccessToken($authorizationCode, $challengeVerifier);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function updateDomain(array|string $accessToken, string $domain): void
|
|
|
+ {
|
|
|
+ if (is_array($accessToken)) {
|
|
|
+ $accessToken = $accessToken['access_token'];
|
|
|
+ }
|
|
|
+ parent::updateDomain($accessToken, $domain);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function refreshTokenIfNeeded(HelloAsso $helloAssoEntity): HelloAsso
|
|
|
+ {
|
|
|
+ return parent::refreshTokenIfNeeded($helloAssoEntity);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function refreshTokens(HelloAsso $helloAssoEntity): HelloAsso
|
|
|
+ {
|
|
|
+ return parent::refreshTokens($helloAssoEntity);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class HelloAssoServiceTest extends TestCase
|
|
|
+{
|
|
|
+ private HttpClientInterface|MockObject $httpClient;
|
|
|
+ private OrganizationRepository|MockObject $organizationRepository;
|
|
|
+ private EventRepository|MockObject $eventRepository;
|
|
|
+ private EntityManagerInterface|MockObject $entityManager;
|
|
|
+ private LoggerInterface|MockObject $logger;
|
|
|
+
|
|
|
+ private string $baseUrl = 'https://test-base.com';
|
|
|
+ private string $publicAppBaseUrl = 'https://test-public.com';
|
|
|
+ private string $helloAssoApiBaseUrl = 'https://api.helloasso.com/v5';
|
|
|
+ private string $helloAssoAuthBaseUrl = 'https://auth.helloasso.com';
|
|
|
+ private string $helloAssoClientId = 'test-client-id';
|
|
|
+ private string $helloAssoClientSecret = 'test-client-secret';
|
|
|
+
|
|
|
+ public function setUp(): void
|
|
|
+ {
|
|
|
+ $this->httpClient = $this->getMockBuilder(HttpClientInterface::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $this->organizationRepository = $this->getMockBuilder(OrganizationRepository::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $this->eventRepository = $this->getMockBuilder(EventRepository::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $this->entityManager = $this->getMockBuilder(EntityManagerInterface::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $this->logger = $this->getMockBuilder(LoggerInterface::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+ }
|
|
|
+
|
|
|
+ private function getHelloAssoServiceMockFor(string $methodName): TestableHelloAssoService|MockObject
|
|
|
+ {
|
|
|
+ return $this->getMockBuilder(TestableHelloAssoService::class)
|
|
|
+ ->setConstructorArgs([
|
|
|
+ $this->httpClient,
|
|
|
+ $this->organizationRepository,
|
|
|
+ $this->eventRepository,
|
|
|
+ $this->baseUrl,
|
|
|
+ $this->publicAppBaseUrl,
|
|
|
+ $this->helloAssoApiBaseUrl,
|
|
|
+ $this->helloAssoAuthBaseUrl,
|
|
|
+ $this->helloAssoClientId,
|
|
|
+ $this->helloAssoClientSecret,
|
|
|
+ $this->entityManager,
|
|
|
+ $this->logger,
|
|
|
+ ])
|
|
|
+ ->setMethodsExcept([$methodName])
|
|
|
+ ->getMock();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::setupOpentalentDomain()
|
|
|
+ */
|
|
|
+ public function testSetupOpentalentDomain(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('setupOpentalentDomain');
|
|
|
+
|
|
|
+ $tokensData = ['access_token' => 'test-token'];
|
|
|
+
|
|
|
+ $service->expects(self::once())
|
|
|
+ ->method('fetchAccessToken')
|
|
|
+ ->with(null)
|
|
|
+ ->willReturn($tokensData);
|
|
|
+
|
|
|
+ $service->expects(self::once())
|
|
|
+ ->method('updateDomain')
|
|
|
+ ->with($tokensData, 'https://*.opentalent.fr');
|
|
|
+
|
|
|
+ $service->setupOpentalentDomain();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::getAuthUrl()
|
|
|
+ */
|
|
|
+ public function testGetAuthUrl(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('getAuthUrl');
|
|
|
+ $organizationId = 1;
|
|
|
+
|
|
|
+ $organization = $this->getMockBuilder(Organization::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $helloAssoEntity = $this->getMockBuilder(HelloAsso::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $this->organizationRepository->expects(self::once())
|
|
|
+ ->method('find')
|
|
|
+ ->with($organizationId)
|
|
|
+ ->willReturn($organization);
|
|
|
+
|
|
|
+ $organization->expects(self::once())
|
|
|
+ ->method('getHelloAsso')
|
|
|
+ ->willReturn($helloAssoEntity);
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setChallengeVerifier')
|
|
|
+ ->with(self::isType('string'));
|
|
|
+
|
|
|
+ $this->entityManager->expects(self::once())
|
|
|
+ ->method('persist')
|
|
|
+ ->with($helloAssoEntity);
|
|
|
+
|
|
|
+ $this->entityManager->expects(self::once())
|
|
|
+ ->method('flush');
|
|
|
+
|
|
|
+ $result = $service->getAuthUrl($organizationId);
|
|
|
+
|
|
|
+ $this->assertInstanceOf(AuthUrl::class, $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::connect()
|
|
|
+ */
|
|
|
+ public function testConnect(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('connect');
|
|
|
+ $organizationId = 1;
|
|
|
+ $authorizationCode = 'test-auth-code';
|
|
|
+
|
|
|
+ $organization = $this->getMockBuilder(Organization::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $helloAssoEntity = $this->getMockBuilder(HelloAsso::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $challengeVerifier = 'test-verifier';
|
|
|
+ $tokensData = [
|
|
|
+ 'access_token' => 'test-access-token',
|
|
|
+ 'refresh_token' => 'test-refresh-token',
|
|
|
+ 'expires_in' => 3600,
|
|
|
+ 'token_type' => 'bearer',
|
|
|
+ ];
|
|
|
+
|
|
|
+ $this->organizationRepository->expects(self::once())
|
|
|
+ ->method('find')
|
|
|
+ ->with($organizationId)
|
|
|
+ ->willReturn($organization);
|
|
|
+
|
|
|
+ $organization->expects(self::once())
|
|
|
+ ->method('getHelloAsso')
|
|
|
+ ->willReturn($helloAssoEntity);
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('getChallengeVerifier')
|
|
|
+ ->willReturn($challengeVerifier);
|
|
|
+
|
|
|
+ $service->expects(self::once())
|
|
|
+ ->method('fetchAccessToken')
|
|
|
+ ->with($authorizationCode, $challengeVerifier)
|
|
|
+ ->willReturn($tokensData);
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setToken')
|
|
|
+ ->with('test-access-token');
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setTokenCreatedAt')
|
|
|
+ ->with(self::isInstanceOf(\DateTime::class));
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setRefreshToken')
|
|
|
+ ->with('test-refresh-token');
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setRefreshTokenCreatedAt')
|
|
|
+ ->with(self::isInstanceOf(\DateTime::class));
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setOrganizationSlug')
|
|
|
+ ->with(null);
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setChallengeVerifier')
|
|
|
+ ->with(null);
|
|
|
+
|
|
|
+ $this->entityManager->expects(self::once())
|
|
|
+ ->method('persist')
|
|
|
+ ->with($helloAssoEntity);
|
|
|
+
|
|
|
+ $this->entityManager->expects(self::once())
|
|
|
+ ->method('flush');
|
|
|
+
|
|
|
+ $result = $service->connect($organizationId, $authorizationCode);
|
|
|
+
|
|
|
+ $this->assertSame($helloAssoEntity, $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::makeHelloAssoProfile()
|
|
|
+ */
|
|
|
+ public function testMakeHelloAssoProfile(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('makeHelloAssoProfile');
|
|
|
+ $organizationId = 1;
|
|
|
+
|
|
|
+ $organization = $this->getMockBuilder(Organization::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $helloAssoEntity = $this->getMockBuilder(HelloAsso::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $this->organizationRepository->expects(self::once())
|
|
|
+ ->method('find')
|
|
|
+ ->with($organizationId)
|
|
|
+ ->willReturn($organization);
|
|
|
+
|
|
|
+ $organization->expects(self::once())
|
|
|
+ ->method('getHelloAsso')
|
|
|
+ ->willReturn($helloAssoEntity);
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('getToken')
|
|
|
+ ->willReturn('test-token');
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('getOrganizationSlug')
|
|
|
+ ->willReturn('test-org-slug');
|
|
|
+
|
|
|
+ $result = $service->makeHelloAssoProfile($organizationId);
|
|
|
+
|
|
|
+ $this->assertInstanceOf(HelloAssoProfile::class, $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::unlinkHelloAssoAccount()
|
|
|
+ */
|
|
|
+ public function testUnlinkHelloAssoAccount(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('unlinkHelloAssoAccount');
|
|
|
+ $organizationId = 1;
|
|
|
+
|
|
|
+ $organization = $this->getMockBuilder(Organization::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $helloAssoEntity = $this->getMockBuilder(HelloAsso::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $this->organizationRepository->expects(self::once())
|
|
|
+ ->method('find')
|
|
|
+ ->with($organizationId)
|
|
|
+ ->willReturn($organization);
|
|
|
+
|
|
|
+ $organization->expects(self::once())
|
|
|
+ ->method('getHelloAsso')
|
|
|
+ ->willReturn($helloAssoEntity);
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setToken')
|
|
|
+ ->with(null);
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setTokenCreatedAt')
|
|
|
+ ->with(null);
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setRefreshToken')
|
|
|
+ ->with(null);
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setRefreshTokenCreatedAt')
|
|
|
+ ->with(null);
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setOrganizationSlug')
|
|
|
+ ->with(null);
|
|
|
+
|
|
|
+ $this->entityManager->expects(self::once())
|
|
|
+ ->method('persist')
|
|
|
+ ->with($helloAssoEntity);
|
|
|
+
|
|
|
+ $this->entityManager->expects(self::once())
|
|
|
+ ->method('flush');
|
|
|
+
|
|
|
+ $service->unlinkHelloAssoAccount($organizationId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::getResource()
|
|
|
+ */
|
|
|
+ public function testGetResource(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('getResource');
|
|
|
+
|
|
|
+ $helloAssoEntity = $this->getMockBuilder(HelloAsso::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $response = $this->getMockBuilder(ResponseInterface::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $routeParts = ['organizations', 'test-slug', 'forms'];
|
|
|
+ $expectedData = ['test' => 'data'];
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('getOrganizationSlug')
|
|
|
+ ->willReturn('test-org-slug');
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::atLeastOnce())
|
|
|
+ ->method('getToken')
|
|
|
+ ->willReturn('test-token');
|
|
|
+
|
|
|
+ $service->expects(self::once())
|
|
|
+ ->method('refreshTokenIfNeeded')
|
|
|
+ ->with($helloAssoEntity)
|
|
|
+ ->willReturn($helloAssoEntity);
|
|
|
+
|
|
|
+ $service->expects(self::once())
|
|
|
+ ->method('get')
|
|
|
+ ->with('https://api.helloasso.com/v5/v5/organizations/test-slug/forms', [], [
|
|
|
+ 'headers' => [
|
|
|
+ 'accept' => 'application/json',
|
|
|
+ 'authorization' => 'Bearer test-token',
|
|
|
+ ],
|
|
|
+ ])
|
|
|
+ ->willReturn($response);
|
|
|
+
|
|
|
+ $response->expects(self::once())
|
|
|
+ ->method('getStatusCode')
|
|
|
+ ->willReturn(200);
|
|
|
+
|
|
|
+ $response->expects(self::once())
|
|
|
+ ->method('getContent')
|
|
|
+ ->willReturn(json_encode($expectedData));
|
|
|
+
|
|
|
+ $result = $service->getResource($helloAssoEntity, $routeParts);
|
|
|
+
|
|
|
+ $this->assertSame($expectedData, $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::getHelloAssoEventForms()
|
|
|
+ */
|
|
|
+ public function testGetHelloAssoEventForms(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('getHelloAssoEventForms');
|
|
|
+ $organizationId = 1;
|
|
|
+
|
|
|
+ $helloAssoEntity = $this->getMockBuilder(HelloAsso::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $formsData = [
|
|
|
+ 'data' => [
|
|
|
+ ['formSlug' => 'form1', 'title' => 'Event 1'],
|
|
|
+ ['formSlug' => 'form2', 'title' => 'Event 2'],
|
|
|
+ ]
|
|
|
+ ];
|
|
|
+
|
|
|
+ $service->expects(self::once())
|
|
|
+ ->method('getHelloAssoEntityFor')
|
|
|
+ ->with($organizationId, true)
|
|
|
+ ->willReturn($helloAssoEntity);
|
|
|
+
|
|
|
+ $helloAssoEntity
|
|
|
+ ->method('getOrganizationSlug')
|
|
|
+ ->willReturn('test-org-slug');
|
|
|
+
|
|
|
+ $helloAssoEntity
|
|
|
+ ->method('getToken')
|
|
|
+ ->willReturn('abcd123');
|
|
|
+
|
|
|
+ $service->expects(self::once())
|
|
|
+ ->method('getResource')
|
|
|
+ ->with($helloAssoEntity, ['organizations', 'test-org-slug', 'forms'])
|
|
|
+ ->willReturn($formsData);
|
|
|
+
|
|
|
+ $result = $service->getHelloAssoEventForms($organizationId);
|
|
|
+
|
|
|
+ $this->assertIsArray($result);
|
|
|
+ $this->assertCount(2, $result);
|
|
|
+ $this->assertInstanceOf(EventForm::class, $result[0]);
|
|
|
+ $this->assertInstanceOf(EventForm::class, $result[1]);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::getHelloAssoEventForms()
|
|
|
+ */
|
|
|
+ public function testGetHelloAssoEventFormsWhenIncomplete(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('getHelloAssoEventForms');
|
|
|
+ $organizationId = 1;
|
|
|
+
|
|
|
+ $service->expects(self::once())
|
|
|
+ ->method('getHelloAssoEntityFor')
|
|
|
+ ->with($organizationId, true)
|
|
|
+ ->willThrowException(new \RuntimeException('HelloAsso entity incomplete'));
|
|
|
+
|
|
|
+ $this->expectException(\RuntimeException::class);
|
|
|
+ $this->expectExceptionMessage('HelloAsso entity incomplete');
|
|
|
+
|
|
|
+ $service->getHelloAssoEventForms($organizationId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::getHelloAssoEventForm()
|
|
|
+ */
|
|
|
+ public function testGetHelloAssoEventForm(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('getHelloAssoEventForm');
|
|
|
+ $organizationId = 1;
|
|
|
+ $formSlug = 'test-form-slug';
|
|
|
+
|
|
|
+ $helloAssoEntity = $this->getMockBuilder(HelloAsso::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $formData = [
|
|
|
+ 'formSlug' => 'test-form-slug',
|
|
|
+ 'title' => 'Test Event',
|
|
|
+ 'widgetFullUrl' => 'https://widget.url'
|
|
|
+ ];
|
|
|
+
|
|
|
+ $service->expects(self::once())
|
|
|
+ ->method('getHelloAssoEntityFor')
|
|
|
+ ->with($organizationId, true)
|
|
|
+ ->willReturn($helloAssoEntity);
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::atLeastOnce())
|
|
|
+ ->method('getOrganizationSlug')
|
|
|
+ ->willReturn('test-org-slug');
|
|
|
+
|
|
|
+ $service->expects(self::once())
|
|
|
+ ->method('getResource')
|
|
|
+ ->with($helloAssoEntity, ['organizations', 'test-org-slug', 'forms', 'Event', 'test-form-slug', 'public'])
|
|
|
+ ->willReturn($formData);
|
|
|
+
|
|
|
+ $service->expects(self::once())
|
|
|
+ ->method('makeHelloAssoEventForm')
|
|
|
+ ->with($formData)
|
|
|
+ ->willReturn(new EventForm());
|
|
|
+
|
|
|
+ $result = $service->getHelloAssoEventForm($organizationId, $formSlug);
|
|
|
+
|
|
|
+ $this->assertInstanceOf(EventForm::class, $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::getHelloAssoEventFormByEventId()
|
|
|
+ */
|
|
|
+ public function testGetHelloAssoEventFormByEventId(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('getHelloAssoEventFormByEventId');
|
|
|
+ $eventId = 1;
|
|
|
+
|
|
|
+ $organization = $this->getMockBuilder(Organization::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $event = $this->getMockBuilder(Event::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $eventForm = new EventForm();
|
|
|
+
|
|
|
+ $this->eventRepository->expects(self::once())
|
|
|
+ ->method('find')
|
|
|
+ ->with($eventId)
|
|
|
+ ->willReturn($event);
|
|
|
+
|
|
|
+ $event->expects(self::once())
|
|
|
+ ->method('getOrganization')
|
|
|
+ ->willReturn($organization);
|
|
|
+
|
|
|
+ $organization->expects(self::once())
|
|
|
+ ->method('getId')
|
|
|
+ ->willReturn(1);
|
|
|
+
|
|
|
+ $event->expects(self::once())
|
|
|
+ ->method('getHelloAssoSlug')
|
|
|
+ ->willReturn('test-hello-asso-slug');
|
|
|
+
|
|
|
+ $service->expects(self::once())
|
|
|
+ ->method('getHelloAssoEventForm')
|
|
|
+ ->with(1, 'test-hello-asso-slug')
|
|
|
+ ->willReturn($eventForm);
|
|
|
+
|
|
|
+ $result = $service->getHelloAssoEventFormByEventId($eventId);
|
|
|
+
|
|
|
+ $this->assertSame($eventForm, $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::getHelloAssoEventFormByEventId()
|
|
|
+ */
|
|
|
+ public function testGetHelloAssoEventFormByEventIdWhenEventNotFound(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('getHelloAssoEventFormByEventId');
|
|
|
+ $eventId = 1;
|
|
|
+
|
|
|
+ $this->eventRepository->expects(self::once())
|
|
|
+ ->method('find')
|
|
|
+ ->with($eventId)
|
|
|
+ ->willReturn(null);
|
|
|
+
|
|
|
+ $this->expectException(\RuntimeException::class);
|
|
|
+ $this->expectExceptionMessage('Event not found');
|
|
|
+
|
|
|
+ $service->getHelloAssoEventFormByEventId($eventId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::getHelloAssoEventFormByEventId()
|
|
|
+ */
|
|
|
+ public function testGetHelloAssoEventFormByEventIdWhenNoHelloAssoSlug(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('getHelloAssoEventFormByEventId');
|
|
|
+ $eventId = 1;
|
|
|
+
|
|
|
+ $organization = $this->getMockBuilder(Organization::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $event = $this->getMockBuilder(Event::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $helloAssoEntity = $this->getMockBuilder(HelloAsso::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $this->eventRepository->expects(self::once())
|
|
|
+ ->method('find')
|
|
|
+ ->with($eventId)
|
|
|
+ ->willReturn($event);
|
|
|
+
|
|
|
+ $event->expects(self::once())
|
|
|
+ ->method('getHelloAssoSlug')
|
|
|
+ ->willReturn(null);
|
|
|
+
|
|
|
+ $this->expectException(\RuntimeException::class);
|
|
|
+ $this->expectExceptionMessage('HelloAsso form slug not found');
|
|
|
+
|
|
|
+ $service->getHelloAssoEventFormByEventId($eventId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::getHelloAssoEntityFor()
|
|
|
+ */
|
|
|
+ public function testGetHelloAssoEntityFor(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('getHelloAssoEntityFor');
|
|
|
+ $organizationId = 1;
|
|
|
+
|
|
|
+ $organization = $this->getMockBuilder(Organization::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $helloAssoEntity = $this->getMockBuilder(HelloAsso::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $this->organizationRepository->expects(self::once())
|
|
|
+ ->method('find')
|
|
|
+ ->with($organizationId)
|
|
|
+ ->willReturn($organization);
|
|
|
+
|
|
|
+ $organization->expects(self::once())
|
|
|
+ ->method('getHelloAsso')
|
|
|
+ ->willReturn($helloAssoEntity);
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('getOrganizationSlug')
|
|
|
+ ->willReturn('test-org-slug');
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('getToken')
|
|
|
+ ->willReturn('test-token');
|
|
|
+
|
|
|
+ $result = $service->getHelloAssoEntityFor($organizationId);
|
|
|
+
|
|
|
+ $this->assertSame($helloAssoEntity, $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::getHelloAssoEntityFor()
|
|
|
+ */
|
|
|
+ public function testGetHelloAssoEntityForWhenIncomplete(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('getHelloAssoEntityFor');
|
|
|
+ $organizationId = 1;
|
|
|
+
|
|
|
+ $organization = $this->getMockBuilder(Organization::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $helloAssoEntity = $this->getMockBuilder(HelloAsso::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $this->organizationRepository->expects(self::once())
|
|
|
+ ->method('find')
|
|
|
+ ->with($organizationId)
|
|
|
+ ->willReturn($organization);
|
|
|
+
|
|
|
+ $organization->expects(self::once())
|
|
|
+ ->method('getHelloAsso')
|
|
|
+ ->willReturn($helloAssoEntity);
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('getOrganizationSlug')
|
|
|
+ ->willReturn('test-org-slug');
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('getToken')
|
|
|
+ ->willReturn(null);
|
|
|
+
|
|
|
+ $this->expectException(\RuntimeException::class);
|
|
|
+ $this->expectExceptionMessage('HelloAsso entity incomplete');
|
|
|
+
|
|
|
+ $service->getHelloAssoEntityFor($organizationId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::getHelloAssoEntityFor()
|
|
|
+ */
|
|
|
+ public function testGetHelloAssoEntityForWhenOrganizationNotFound(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('getHelloAssoEntityFor');
|
|
|
+ $organizationId = 1;
|
|
|
+
|
|
|
+ $this->organizationRepository->expects(self::once())
|
|
|
+ ->method('find')
|
|
|
+ ->with($organizationId)
|
|
|
+ ->willReturn(null);
|
|
|
+
|
|
|
+ $this->expectException(\RuntimeException::class);
|
|
|
+ $this->expectExceptionMessage('Organization not found');
|
|
|
+
|
|
|
+ $service->getHelloAssoEntityFor($organizationId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::getHelloAssoEntityFor()
|
|
|
+ */
|
|
|
+ public function testGetHelloAssoEntityForWhenHelloAssoNotFound(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('getHelloAssoEntityFor');
|
|
|
+ $organizationId = 1;
|
|
|
+
|
|
|
+ $organization = $this->getMockBuilder(Organization::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $this->organizationRepository->expects(self::once())
|
|
|
+ ->method('find')
|
|
|
+ ->with($organizationId)
|
|
|
+ ->willReturn($organization);
|
|
|
+
|
|
|
+ $organization->expects(self::once())
|
|
|
+ ->method('getHelloAsso')
|
|
|
+ ->willReturn(null);
|
|
|
+
|
|
|
+ $this->expectException(\RuntimeException::class);
|
|
|
+ $this->expectExceptionMessage('HelloAsso entity not found');
|
|
|
+
|
|
|
+ $service->getHelloAssoEntityFor($organizationId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::makeHelloAssoEventForm()
|
|
|
+ */
|
|
|
+ public function testMakeHelloAssoEventForm(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('makeHelloAssoEventForm');
|
|
|
+
|
|
|
+ $formData = [
|
|
|
+ 'formSlug' => 'test-form-slug',
|
|
|
+ 'title' => 'Test Event Title',
|
|
|
+ 'widgetFullUrl' => 'https://widget.full.url'
|
|
|
+ ];
|
|
|
+
|
|
|
+ $result = $service->makeHelloAssoEventForm($formData);
|
|
|
+
|
|
|
+ $this->assertInstanceOf(EventForm::class, $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::getCallbackUrl()
|
|
|
+ */
|
|
|
+ public function testGetCallbackUrl(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('getCallbackUrl');
|
|
|
+
|
|
|
+ $result = $service->getCallbackUrl();
|
|
|
+
|
|
|
+ $this->assertEquals('https://test-public.com/helloasso/callback', $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::fetchAccessToken()
|
|
|
+ */
|
|
|
+ public function testFetchAccessToken(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('fetchAccessToken');
|
|
|
+
|
|
|
+ $response = $this->getMockBuilder(ResponseInterface::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $expectedTokenData = [
|
|
|
+ 'access_token' => 'test-access-token',
|
|
|
+ 'refresh_token' => 'test-refresh-token',
|
|
|
+ 'token_type' => 'Bearer',
|
|
|
+ 'expires_in' => 3600,
|
|
|
+ 'organization_slug' => null
|
|
|
+ ];
|
|
|
+
|
|
|
+ $this->httpClient->expects(self::once())
|
|
|
+ ->method('request')
|
|
|
+ ->with('POST', 'https://api.helloasso.com/v5/oauth2/token', [
|
|
|
+ 'headers' => ['Content-Type' => 'application/x-www-form-urlencoded'],
|
|
|
+ 'body' => [
|
|
|
+ 'grant_type' => 'client_credentials',
|
|
|
+ 'client_id' => 'test-client-id',
|
|
|
+ 'client_secret' => 'test-client-secret',
|
|
|
+ ]
|
|
|
+ ])
|
|
|
+ ->willReturn($response);
|
|
|
+
|
|
|
+ $response->expects(self::once())
|
|
|
+ ->method('getStatusCode')
|
|
|
+ ->willReturn(200);
|
|
|
+
|
|
|
+ $response->expects(self::once())
|
|
|
+ ->method('getContent')
|
|
|
+ ->willReturn(json_encode($expectedTokenData));
|
|
|
+
|
|
|
+ $result = $service->fetchAccessToken();
|
|
|
+
|
|
|
+ $this->assertSame($expectedTokenData, $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::fetchAccessToken()
|
|
|
+ */
|
|
|
+ public function testFetchAccessTokenWithAuthCode(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('fetchAccessToken');
|
|
|
+
|
|
|
+ $response = $this->getMockBuilder(ResponseInterface::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $expectedTokenData = [
|
|
|
+ 'access_token' => 'test-access-token',
|
|
|
+ 'refresh_token' => 'test-refresh-token',
|
|
|
+ 'token_type' => 'Bearer',
|
|
|
+ 'expires_in' => 3600,
|
|
|
+ 'organization_slug' => null
|
|
|
+ ];
|
|
|
+
|
|
|
+ $service->expects(self::once())
|
|
|
+ ->method('getCallbackUrl')
|
|
|
+ ->willReturn('https://test-public.com/helloasso/callback');
|
|
|
+
|
|
|
+ $this->httpClient->expects(self::once())
|
|
|
+ ->method('request')
|
|
|
+ ->with('POST', 'https://api.helloasso.com/v5/oauth2/token', [
|
|
|
+ 'headers' => ['Content-Type' => 'application/x-www-form-urlencoded'],
|
|
|
+ 'body' => [
|
|
|
+ 'grant_type' => 'authorization_code',
|
|
|
+ 'client_id' => 'test-client-id',
|
|
|
+ 'client_secret' => 'test-client-secret',
|
|
|
+ 'code' => 'test-auth-code',
|
|
|
+ 'redirect_uri' => 'https://test-public.com/helloasso/callback',
|
|
|
+ 'code_verifier' => 'test-verifier'
|
|
|
+ ]
|
|
|
+ ])
|
|
|
+ ->willReturn($response);
|
|
|
+
|
|
|
+ $response->expects(self::once())
|
|
|
+ ->method('getStatusCode')
|
|
|
+ ->willReturn(200);
|
|
|
+
|
|
|
+ $response->expects(self::once())
|
|
|
+ ->method('getContent')
|
|
|
+ ->willReturn(json_encode($expectedTokenData));
|
|
|
+
|
|
|
+ $result = $service->fetchAccessToken('test-auth-code', 'test-verifier');
|
|
|
+
|
|
|
+ $this->assertSame($expectedTokenData, $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::updateDomain()
|
|
|
+ */
|
|
|
+ public function testUpdateDomain(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('updateDomain');
|
|
|
+
|
|
|
+ $response = $this->getMockBuilder(ResponseInterface::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $service->expects(self::once())
|
|
|
+ ->method('put')
|
|
|
+ ->with('https://api.helloasso.com/v5/v5/partners/me/api-clients', ['domain' => 'https://test-domain.com'], [], [
|
|
|
+ 'headers' => [
|
|
|
+ 'Authorization' => 'Bearer test-access-token',
|
|
|
+ 'Content-Type' => 'application/json',
|
|
|
+ ],
|
|
|
+ ])
|
|
|
+ ->willReturn($response);
|
|
|
+
|
|
|
+ $response->expects(self::once())
|
|
|
+ ->method('getStatusCode')
|
|
|
+ ->willReturn(200);
|
|
|
+
|
|
|
+ $service->updateDomain('test-access-token', 'https://test-domain.com');
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::refreshTokenIfNeeded()
|
|
|
+ */
|
|
|
+ public function testRefreshTokenIfNeeded(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('refreshTokenIfNeeded');
|
|
|
+
|
|
|
+ $helloAssoEntity = $this->getMockBuilder(HelloAsso::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $futureDate = new \DateTime('+1 hour');
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('getRefreshToken')
|
|
|
+ ->willReturn('test-refresh-token');
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::atLeastOnce())
|
|
|
+ ->method('getRefreshTokenCreatedAt')
|
|
|
+ ->willReturn($futureDate);
|
|
|
+
|
|
|
+ $result = $service->refreshTokenIfNeeded($helloAssoEntity);
|
|
|
+
|
|
|
+ $this->assertSame($helloAssoEntity, $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::refreshTokenIfNeeded()
|
|
|
+ */
|
|
|
+ public function testRefreshTokenIfNeededWhenTokenExpired(): void
|
|
|
+ {
|
|
|
+ // Set fake current time to control the test scenario
|
|
|
+ DatesUtils::setFakeDatetime('2024-01-01 12:00:00');
|
|
|
+
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('refreshTokenIfNeeded');
|
|
|
+
|
|
|
+ $helloAssoEntity = $this->getMockBuilder(HelloAsso::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $refreshedEntity = $this->getMockBuilder(HelloAsso::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ // Create a date that when 25 minutes are added, will be less than current fake time
|
|
|
+ $pastDate = DatesUtils::new('2024-01-01 11:30:00');
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('getRefreshToken')
|
|
|
+ ->willReturn('test-refresh-token');
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::atLeastOnce())
|
|
|
+ ->method('getRefreshTokenCreatedAt')
|
|
|
+ ->willReturn($pastDate);
|
|
|
+
|
|
|
+ $service->expects(self::once())
|
|
|
+ ->method('refreshTokens')
|
|
|
+ ->with($helloAssoEntity)
|
|
|
+ ->willReturn($helloAssoEntity);
|
|
|
+
|
|
|
+ $result = $service->refreshTokenIfNeeded($helloAssoEntity);
|
|
|
+
|
|
|
+ $this->assertSame($helloAssoEntity, $result);
|
|
|
+
|
|
|
+ // Clean up fake datetime
|
|
|
+ DatesUtils::clearFakeDatetime();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @see HelloAssoService::refreshTokens()
|
|
|
+ */
|
|
|
+ public function testRefreshTokens(): void
|
|
|
+ {
|
|
|
+ $service = $this->getHelloAssoServiceMockFor('refreshTokens');
|
|
|
+
|
|
|
+ $helloAssoEntity = $this->getMockBuilder(HelloAsso::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $response = $this->getMockBuilder(ResponseInterface::class)
|
|
|
+ ->disableOriginalConstructor()
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $tokensData = [
|
|
|
+ 'access_token' => 'new-access-token',
|
|
|
+ 'refresh_token' => 'new-refresh-token',
|
|
|
+ 'expires_in' => 3600
|
|
|
+ ];
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::atLeastOnce())
|
|
|
+ ->method('getRefreshToken')
|
|
|
+ ->willReturn('current-refresh-token');
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('getRefreshTokenCreatedAt')
|
|
|
+ ->willReturn(new \DateTime());
|
|
|
+
|
|
|
+ $this->httpClient->expects(self::once())
|
|
|
+ ->method('request')
|
|
|
+ ->with('POST', 'https://api.helloasso.com/v5/oauth2/token', [
|
|
|
+ 'headers' => ['Content-Type' => 'application/x-www-form-urlencoded'],
|
|
|
+ 'body' => [
|
|
|
+ 'grant_type' => 'refresh_token',
|
|
|
+ 'refresh_token' => 'current-refresh-token'
|
|
|
+ ]
|
|
|
+ ])
|
|
|
+ ->willReturn($response);
|
|
|
+
|
|
|
+ $response->expects(self::once())
|
|
|
+ ->method('getStatusCode')
|
|
|
+ ->willReturn(200);
|
|
|
+
|
|
|
+ $response->expects(self::once())
|
|
|
+ ->method('getContent')
|
|
|
+ ->willReturn(json_encode($tokensData));
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setToken')
|
|
|
+ ->with('new-access-token');
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setRefreshToken')
|
|
|
+ ->with('new-refresh-token');
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setTokenCreatedAt')
|
|
|
+ ->with(self::isInstanceOf(\DateTime::class));
|
|
|
+
|
|
|
+ $helloAssoEntity->expects(self::once())
|
|
|
+ ->method('setRefreshTokenCreatedAt')
|
|
|
+ ->with(self::isInstanceOf(\DateTime::class));
|
|
|
+
|
|
|
+ $this->entityManager->expects(self::once())
|
|
|
+ ->method('persist')
|
|
|
+ ->with($helloAssoEntity);
|
|
|
+
|
|
|
+ $this->entityManager->expects(self::once())
|
|
|
+ ->method('flush');
|
|
|
+
|
|
|
+ $result = $service->refreshTokens($helloAssoEntity);
|
|
|
+
|
|
|
+ $this->assertSame($helloAssoEntity, $result);
|
|
|
+ }
|
|
|
+}
|