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::getAuthUrl() */ public function testGetAuthUrlWhenOrganizationNotFound(): void { $service = $this->getHelloAssoServiceMockFor('getAuthUrl'); $organizationId = 1; $this->organizationRepository->expects(self::once()) ->method('find') ->with($organizationId) ->willReturn(null); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Organization not found'); $service->getAuthUrl($organizationId); } /** * @see HelloAssoService::getAuthUrl() */ public function testGetAuthUrlWhenHelloAssoEntityNotExists(): void { $service = $this->getHelloAssoServiceMockFor('getAuthUrl'); $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->entityManager->expects(self::once()) ->method('persist') ->with(self::isInstanceOf(HelloAsso::class)); $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::connect() */ public function testConnectWhenOrganizationNotFound(): void { $service = $this->getHelloAssoServiceMockFor('connect'); $organizationId = 1; $authorizationCode = 'test-auth-code'; $this->organizationRepository->expects(self::once()) ->method('find') ->with($organizationId) ->willReturn(null); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Organization not found'); $service->connect($organizationId, $authorizationCode); } /** * @see HelloAssoService::connect() */ public function testConnectWhenHelloAssoEntityNotFound(): void { $service = $this->getHelloAssoServiceMockFor('connect'); $organizationId = 1; $authorizationCode = 'test-auth-code'; $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->connect($organizationId, $authorizationCode); } /** * @see HelloAssoService::connect() */ public function testConnectWhenInvalidTokenType(): 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' => 'invalid', ]; $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); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Invalid token type received'); $service->connect($organizationId, $authorizationCode); } /** * @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::makeHelloAssoProfile() */ public function testMakeHelloAssoProfileWhenOrganizationNotFound(): void { $service = $this->getHelloAssoServiceMockFor('makeHelloAssoProfile'); $organizationId = 1; $this->organizationRepository->expects(self::once()) ->method('find') ->with($organizationId) ->willReturn(null); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Organization not found'); $service->makeHelloAssoProfile($organizationId); } /** * @see HelloAssoService::makeHelloAssoProfile() */ public function testMakeHelloAssoProfileWhenHelloAssoEntityNotFound(): void { $service = $this->getHelloAssoServiceMockFor('makeHelloAssoProfile'); $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); $result = $service->makeHelloAssoProfile($organizationId); $this->assertInstanceOf(HelloAssoProfile::class, $result); $this->assertFalse($result->isExisting()); } /** * @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::unlinkHelloAssoAccount() */ public function testUnlinkHelloAssoAccountWhenOrganizationNotFound(): void { $service = $this->getHelloAssoServiceMockFor('unlinkHelloAssoAccount'); $organizationId = 1; $this->organizationRepository->expects(self::once()) ->method('find') ->with($organizationId) ->willReturn(null); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Organization not found'); $service->unlinkHelloAssoAccount($organizationId); } /** * @see HelloAssoService::unlinkHelloAssoAccount() */ public function testUnlinkHelloAssoAccountWhenHelloAssoEntityNotFound(): void { $service = $this->getHelloAssoServiceMockFor('unlinkHelloAssoAccount'); $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->entityManager->expects(self::never()) ->method('persist'); $this->entityManager->expects(self::never()) ->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::getResource() */ public function testGetResourceWhenIncompleteEntity(): void { $service = $this->getHelloAssoServiceMockFor('getResource'); $helloAssoEntity = $this->getMockBuilder(HelloAsso::class) ->disableOriginalConstructor() ->getMock(); $routeParts = ['organizations', 'test-slug', 'forms']; $helloAssoEntity->expects(self::once()) ->method('getOrganizationSlug') ->willReturn(null); // getToken() won't be called because getOrganizationSlug() returns null first $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('HelloAsso entity incomplete'); $service->getResource($helloAssoEntity, $routeParts); } /** * @see HelloAssoService::getResource() */ public function testGetResourceWhenHttpError(): void { $service = $this->getHelloAssoServiceMockFor('getResource'); $helloAssoEntity = $this->getMockBuilder(HelloAsso::class) ->disableOriginalConstructor() ->getMock(); $response = $this->getMockBuilder(ResponseInterface::class) ->disableOriginalConstructor() ->getMock(); $routeParts = ['organizations', 'test-slug', 'forms']; $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') ->willReturn($response); $response->expects(self::atLeastOnce()) ->method('getStatusCode') ->willReturn(404); $response->expects(self::once()) ->method('getContent') ->with(false) ->willReturn('Not Found'); $this->expectException(\Symfony\Component\HttpKernel\Exception\HttpException::class); $this->expectExceptionMessage('Failed to fetch resource: [404] Not Found'); $service->getResource($helloAssoEntity, $routeParts); } /** * @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::fetchAccessToken() */ public function testFetchAccessTokenWhenHttpError(): void { $service = $this->getHelloAssoServiceMockFor('fetchAccessToken'); $response = $this->getMockBuilder(ResponseInterface::class) ->disableOriginalConstructor() ->getMock(); $this->httpClient->expects(self::once()) ->method('request') ->willReturn($response); $response->expects(self::once()) ->method('getStatusCode') ->willReturn(400); $response->expects(self::once()) ->method('getContent') ->with(false) ->willReturn('Bad Request'); $this->expectException(\Symfony\Component\HttpKernel\Exception\HttpException::class); $this->expectExceptionMessage('Failed to fetch access token: Bad Request'); $service->fetchAccessToken(); } /** * @see HelloAssoService::fetchAccessToken() */ public function testFetchAccessTokenWhenJsonError(): void { $service = $this->getHelloAssoServiceMockFor('fetchAccessToken'); $response = $this->getMockBuilder(ResponseInterface::class) ->disableOriginalConstructor() ->getMock(); $this->httpClient->expects(self::once()) ->method('request') ->willReturn($response); $response->expects(self::once()) ->method('getStatusCode') ->willReturn(200); $response->expects(self::once()) ->method('getContent') ->willReturn('invalid-json'); $this->expectException(\Symfony\Component\HttpKernel\Exception\HttpException::class); $this->expectExceptionMessage('Failed to parse authentication response:'); $service->fetchAccessToken(); } /** * @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::updateDomain() */ public function testUpdateDomainWhenHttpError(): void { $service = $this->getHelloAssoServiceMockFor('updateDomain'); $response = $this->getMockBuilder(ResponseInterface::class) ->disableOriginalConstructor() ->getMock(); $service->expects(self::once()) ->method('put') ->willReturn($response); $response->expects(self::once()) ->method('getStatusCode') ->willReturn(403); $response->expects(self::once()) ->method('getContent') ->willReturn('Forbidden'); $this->expectException(\Symfony\Component\HttpKernel\Exception\HttpException::class); $this->expectExceptionMessage('Failed to update domain: Forbidden'); $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); } /** * @see HelloAssoService::refreshTokens() */ public function testRefreshTokensWhenIncompleteEntity(): void { $service = $this->getHelloAssoServiceMockFor('refreshTokens'); $helloAssoEntity = $this->getMockBuilder(HelloAsso::class) ->disableOriginalConstructor() ->getMock(); $helloAssoEntity->expects(self::once()) ->method('getRefreshToken') ->willReturn(null); // getRefreshTokenCreatedAt() won't be called because getRefreshToken() returns null first $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('HelloAsso entity incomplete'); $service->refreshTokens($helloAssoEntity); } /** * @see HelloAssoService::refreshTokens() */ public function testRefreshTokensWhenHttpError(): void { $service = $this->getHelloAssoServiceMockFor('refreshTokens'); $helloAssoEntity = $this->getMockBuilder(HelloAsso::class) ->disableOriginalConstructor() ->getMock(); $response = $this->getMockBuilder(ResponseInterface::class) ->disableOriginalConstructor() ->getMock(); $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') ->willReturn($response); $response->expects(self::once()) ->method('getStatusCode') ->willReturn(401); $response->expects(self::once()) ->method('getContent') ->with(false) ->willReturn('Unauthorized'); $this->expectException(\Symfony\Component\HttpKernel\Exception\HttpException::class); $this->expectExceptionMessage('Failed to refresh access token: Unauthorized'); $service->refreshTokens($helloAssoEntity); } /** * @see HelloAssoService::refreshTokenIfNeeded() */ public function testRefreshTokenIfNeededWhenIncompleteEntity(): void { $service = $this->getHelloAssoServiceMockFor('refreshTokenIfNeeded'); $helloAssoEntity = $this->getMockBuilder(HelloAsso::class) ->disableOriginalConstructor() ->getMock(); $helloAssoEntity->expects(self::once()) ->method('getRefreshToken') ->willReturn('test-refresh-token'); $helloAssoEntity->expects(self::once()) ->method('getRefreshTokenCreatedAt') ->willReturn(null); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('HelloAsso entity incomplete'); $service->refreshTokenIfNeeded($helloAssoEntity); } }