entityManager = $this->getMockBuilder(EntityManagerInterface::class)->disableOriginalConstructor()->getMock(); $this->mailer = $this->getMockBuilder(Mailer::class)->disableOriginalConstructor()->getMock(); $this->publicBaseUrl = 'https://example.com'; $this->organizationFactory = $this->getMockBuilder(OrganizationFactory::class)->disableOriginalConstructor()->getMock(); $this->serializer = $this->getMockBuilder(SerializerInterface::class)->disableOriginalConstructor()->getMock(); $this->logger = $this->getMockBuilder(LoggerInterface::class)->disableOriginalConstructor()->getMock(); $this->dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)->disableOriginalConstructor()->getMock(); $this->dolibarrUtils = $this->getMockBuilder(DolibarrUtils::class)->disableOriginalConstructor()->getMock(); $this->messageBus = $this->getMockBuilder(MessageBusInterface::class)->disableOriginalConstructor()->getMock(); } private function getShopServiceMockFor(string $methodName): TestableShopService|MockObject { $shopService = $this ->getMockBuilder(TestableShopService::class) ->setConstructorArgs( [ $this->entityManager, $this->mailer, $this->publicBaseUrl, $this->organizationFactory, $this->serializer, $this->logger, $this->dolibarrApiService, $this->dolibarrUtils, $this->messageBus, ] ) ->setMethodsExcept([$methodName]) ->getMock(); return $shopService; } public function tearDown(): void { DatesUtils::clearFakeDatetime(); } /** * Test registerNewShopRequest method */ public function testRegisterNewShopRequest(): void { $shopService = $this->getShopServiceMockFor('registerNewShopRequest'); $type = ShopRequestType::NEW_STRUCTURE_ARTIST_PREMIUM_TRIAL; $data = [ 'representativeEmail' => 'test@example.com', 'representativeFirstName' => 'John', 'representativeLastName' => 'Doe', 'structureName' => 'Test Structure', ]; $shopRequest = $this->getMockBuilder(ShopRequest::class)->getMock(); $shopService->expects(self::once()) ->method('controlShopRequestData') ->with($type, $data); $shopService->expects(self::once()) ->method('createRequest') ->with($type, $data) ->willReturn($shopRequest); $shopService->expects(self::once()) ->method('sendRequestValidationLink') ->with($shopRequest); $result = $shopService->registerNewShopRequest($type, $data); $this->assertSame($shopRequest, $result); } /** * Test controlShopRequestData method for NEW_STRUCTURE_ARTIST_PREMIUM_TRIAL type */ public function testControlShopRequestDataForNewStructureArtistPremiumTrial(): void { $shopService = $this->getShopServiceMockFor('controlShopRequestData'); $type = ShopRequestType::NEW_STRUCTURE_ARTIST_PREMIUM_TRIAL; $data = [ 'representativeEmail' => 'test@example.com', 'representativeFirstName' => 'John', 'representativeLastName' => 'Doe', 'structureName' => 'Test Structure', ]; $shopService->expects(self::once()) ->method('validateNewStructureArtistPremiumTrialRequest') ->with($data); $shopService->controlShopRequestData($type, $data); } /** * Test processShopRequest method for NEW_STRUCTURE_ARTIST_PREMIUM_TRIAL type */ public function testProcessShopRequest(): void { $shopService = $this->getShopServiceMockFor('processShopRequest'); $shopRequest = $this->getMockBuilder(ShopRequest::class)->getMock(); $shopRequest->method('getType')->willReturn(ShopRequestType::NEW_STRUCTURE_ARTIST_PREMIUM_TRIAL); $shopRequest->method('getToken')->willReturn('test-token'); $this->messageBus ->expects(self::once()) ->method('dispatch') ->with(self::callback(function ($message) { return $message instanceof NewStructureArtistPremiumTrial && $message->getToken() === 'test-token'; })) ->willReturn(new Envelope(new NewStructureArtistPremiumTrial('azerty'))); $shopRequest ->expects(self::once()) ->method('setStatus') ->with(ShopRequestStatus::VALIDATED); $this->entityManager->expects(self::once()) ->method('persist') ->with($shopRequest); $this->entityManager->expects(self::once()) ->method('flush'); $shopService->processShopRequest($shopRequest); } /** * Test createRequest method */ public function testCreateRequest(): void { $shopService = $this->getShopServiceMockFor('createRequest'); $uuidRx = "/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i"; $type = ShopRequestType::NEW_STRUCTURE_ARTIST_PREMIUM_TRIAL; $data = [ 'representativeEmail' => 'test@example.com', 'representativeFirstName' => 'John', 'representativeLastName' => 'Doe', 'structureName' => 'Test Structure', ]; $this->entityManager->expects(self::once()) ->method('persist') ->with(self::callback(function ($shopRequest) use ($type, $data, $uuidRx) { return $shopRequest instanceof ShopRequest && $shopRequest->getType() === $type && $shopRequest->getData() === $data && preg_match($uuidRx, $shopRequest->getToken()); })); $this->entityManager->expects(self::once()) ->method('flush'); $result = $shopService->createRequest($type, $data); $this->assertInstanceOf(ShopRequest::class, $result); $this->assertSame($type, $result->getType()); $this->assertSame($data, $result->getData()); $this->assertMatchesRegularExpression($uuidRx, $result->getToken()); } /** * Test sendRequestValidationLink method */ public function testSendRequestValidationLink(): void { $shopService = $this->getShopServiceMockFor('sendRequestValidationLink'); $token = 'test-token'; $data = [ 'representativeEmail' => 'test@example.com', 'representativeFirstName' => 'John', 'representativeLastName' => 'Doe', 'structureName' => 'Test Structure', ]; $shopRequest = $this->getMockBuilder(ShopRequest::class)->getMock(); $shopRequest->method('getToken')->willReturn($token); $shopRequest->method('getData')->willReturn($data); $this->mailer ->expects(self::once()) ->method('main') ->with(self::callback(function ($model) use ($token, $data) { var_dump($model); return $model->getToken() === $token && $model->getRepresentativeEmail() === $data['representativeEmail'] && $model->getRepresentativeFirstName() === $data['representativeFirstName'] && $model->getRepresentativeLastName() === $data['representativeLastName'] && $model->getStructureName() === $data['structureName'] && $model->getValidationUrl() === 'https://example.com/api/public/shop/validate/' . $token; })); $shopRequest->expects(self::once()) ->method('setStatus') ->with(ShopRequestStatus::ACTIVATION_LINK_SENT); $this->entityManager->expects(self::once()) ->method('persist') ->with($shopRequest); $this->entityManager->expects(self::once()) ->method('flush'); $shopService->sendRequestValidationLink($shopRequest); } /** * Test startArtistPremiumTrial method */ public function testStartArtistPremiumTrial(): void { $shopService = $this->getShopServiceMockFor('startArtistPremiumTrial'); DatesUtils::setFakeDatetime('2025-01-01 12:00:00'); $organization = $this->getMockBuilder(Organization::class)->getMock(); $settings = $this->getMockBuilder(Settings::class)->getMock(); $settings->method('getProduct')->willReturn(SettingsProductEnum::FREEMIUM); $organization->method('getSettings')->willReturn($settings); $organization->method('getId')->willReturn(123); $request = $this->getMockBuilder(NewStructureArtistPremiumTrialRequest::class)->getMock(); $request->method('getRepresentativeFirstName')->willReturn('John'); $request->method('getRepresentativeLastName')->willReturn('Doe'); $request->method('getRepresentativeFunction')->willReturn('Manager'); $request->method('getRepresentativeEmail')->willReturn('test@example.com'); $request->method('getRepresentativePhone')->willReturn('+33123456789'); $settings ->expects(self::once()) ->method('setProductBeforeTrial') ->with(SettingsProductEnum::FREEMIUM); $settings ->expects(self::once()) ->method('setTrialActive') ->with(true); $settings ->expects(self::once()) ->method('setLastTrialStartDate') ->with(self::callback(function ($dateTime) { return $dateTime instanceof \DateTime && $dateTime->format('Y-m-d H:i:s') === '2025-01-01 12:00:00'; })); $settings->expects(self::once()) ->method('setProduct') ->with(SettingsProductEnum::ARTIST_PREMIUM); $this->entityManager->expects(self::once()) ->method('persist') ->with($settings); $this->entityManager->expects(self::once()) ->method('flush'); $this->dolibarrApiService->expects(self::once()) ->method('getSocietyId') ->with(123) ->willReturn(456); $this->dolibarrUtils->expects(self::once()) ->method('getProductId') ->with(SettingsProductEnum::ARTIST_PREMIUM, true) ->willReturn(789); $this->dolibarrApiService->expects(self::once()) ->method('createContract') ->with(456, 789, true, 1) ->willReturn(101112); $this->dolibarrApiService->expects(self::once()) ->method('createContractLine') ->with(101112, 789, 1); $this->dolibarrUtils->expects(self::once()) ->method('updateSocietyCommercialsWithApi') ->with(456); $this->dolibarrUtils->expects(self::once()) ->method('getDolibarrProductName') ->with(SettingsProductEnum::ARTIST_PREMIUM, true) ->willReturn('Artist Premium (essai)'); $this->dolibarrApiService->expects(self::once()) ->method('updateSocietyProduct') ->with(456, 'Artist Premium (essai)'); $this->dolibarrUtils->expects(self::once()) ->method('addActionComm') ->with(456, "Ouverture de la période d'essai", self::callback(function ($message) { return strpos($message, 'John') !== false && strpos($message, 'Doe') !== false && strpos($message, 'Manager') !== false && strpos($message, 'test@example.com') !== false && strpos($message, '+33123456789') !== false; })); $shopService->startArtistPremiumTrial($organization, $request); // Clear the fake date time after the test DatesUtils::clearFakeDatetime(); } /** * Test handleNewStructureArtistPremiumTrialRequest method */ public function testHandleNewStructureArtistPremiumTrialRequest(): void { $shopService = $this->getShopServiceMockFor('handleNewStructureArtistPremiumTrialRequest'); $token = 'test-token'; $data = [ 'representativeEmail' => 'test@example.com', 'representativeFirstName' => 'John', 'representativeLastName' => 'Doe', 'structureName' => 'Test Structure', ]; $shopRequest = $this->getMockBuilder(ShopRequest::class)->getMock(); $shopRequest->method('getToken')->willReturn($token); $shopRequest->method('getData')->willReturn($data); $this->entityManager ->expects(self::once()) ->method('find') ->with(ShopRequest::class, $token) ->willReturn($shopRequest); $trialRequest = $this->getMockBuilder(\App\ApiResources\Shop\NewStructureArtistPremiumTrialRequest::class) ->getMock(); $this->serializer ->expects(self::once()) ->method('deserialize') ->with(json_encode($data), NewStructureArtistPremiumTrialRequest::class, 'json') ->willReturn($trialRequest); $organization = $this->getMockBuilder(Organization::class)->getMock(); $shopService ->expects(self::once()) ->method('createOrganization') ->with($trialRequest) ->willReturn($organization); $shopService ->expects(self::once()) ->method('startArtistPremiumTrial') ->with($organization, $trialRequest); $this->logger->expects(self::once()) ->method('info') ->with('Successfully processed NewStructureArtistPremiumTrial for token: ' . $token); $shopService->handleNewStructureArtistPremiumTrialRequest($token); } /** * Test handleNewStructureArtistPremiumTrialRequest method with non-existent token */ public function testHandleNewStructureArtistPremiumTrialRequestWithNonExistentToken(): void { $shopService = $this->getShopServiceMockFor('handleNewStructureArtistPremiumTrialRequest'); $this->entityManager->expects(self::once()) ->method('find') ->with(ShopRequest::class, 'non-existent-token') ->willReturn(null); $this->logger->expects(self::once()) ->method('error') ->with('Cannot find ShopRequest with token: non-existent-token'); $shopService->handleNewStructureArtistPremiumTrialRequest('non-existent-token'); } /** * Test createOrganization method */ public function testCreateOrganization(): void { $shopService = $this->getShopServiceMockFor('createOrganization'); $trialRequest = $this ->getMockBuilder(NewStructureArtistPremiumTrialRequest::class) ->getMock(); $trialRequest->method('getStructureName')->willReturn('Test Structure'); $trialRequest->method('getAddress')->willReturn('123 Main St'); $trialRequest->method('getAddressComplement')->willReturn('Suite 456'); $trialRequest->method('getPostalCode')->willReturn('75000'); $trialRequest->method('getCity')->willReturn('Paris'); $trialRequest->method('getStructureEmail')->willReturn('structure@example.com'); $trialRequest->method('getStructureType')->willReturn(PrincipalTypeEnum::ARTISTIC_EDUCATION_ONLY); $trialRequest->method('getLegalStatus')->willReturn(LegalEnum::ASSOCIATION_LAW_1901); $trialRequest->method('getSiren')->willReturn('123456789'); $trialRequest->method('getRepresentativePhone')->willReturn('+33123456789'); $shopService ->expects(self::once()) ->method('generateSubdomain') ->with('Test Structure') ->willReturn('test-structure'); $organization = $this->getMockBuilder(Organization::class)->getMock(); // Mock the organizationFactory.create method to return the mock Organization $this->organizationFactory ->expects(self::once()) ->method('create') ->with(self::callback(function ($organizationCreationRequest) { return $organizationCreationRequest->getName() === 'Test Structure' && $organizationCreationRequest->getStreetAddress1() === '123 Main St' && $organizationCreationRequest->getStreetAddress2() === 'Suite 456' && $organizationCreationRequest->getPostalCode() === '75000' && $organizationCreationRequest->getCity() === 'Paris' && $organizationCreationRequest->getEmail() === 'structure@example.com' && $organizationCreationRequest->getPrincipalType() === PrincipalTypeEnum::ARTISTIC_EDUCATION_ONLY && $organizationCreationRequest->getLegalStatus() === LegalEnum::ASSOCIATION_LAW_1901 && $organizationCreationRequest->getSiretNumber() === '123456789' && $organizationCreationRequest->getPhoneNumber() === '+33123456789' && $organizationCreationRequest->getSubdomain() === 'test-structure' && $organizationCreationRequest->getProduct()->value === SettingsProductEnum::FREEMIUM->value && $organizationCreationRequest->getCreateWebsite() === false && $organizationCreationRequest->isClient() === false; })) ->willReturn($organization); $result = $shopService->createOrganization($trialRequest); $this->assertSame($organization, $result); } /** * Test generateSubdomain method */ public function testGenerateSubdomain(): void { $shopService = $this->getShopServiceMockFor('generateSubdomain'); // Test with a simple name $this->assertEquals('test-structure', $shopService->generateSubdomain('Test Structure')); // Test with accents and special characters $this->assertEquals('ecole-de-musique', $shopService->generateSubdomain('École de Musique')); // Test with multiple spaces and special characters $this->assertEquals('conservatoire-regional', $shopService->generateSubdomain('Conservatoire Régional!@#')); // Test with a very long name (should be truncated to 30 characters) $longName = 'This is a very long name that should be truncated'; $this->assertEquals('this-is-a-very-long-name-that', $shopService->generateSubdomain($longName)); // Test with leading and trailing hyphens (should be trimmed) $this->assertEquals('trimmed', $shopService->generateSubdomain('--Trimmed--')); // Test with consecutive special characters (should be replaced with a single hyphen) $this->assertEquals('multiple-hyphens', $shopService->generateSubdomain('Multiple!!!Hyphens')); } /** * Test validateNewStructureArtistPremiumTrialRequest method with valid data */ public function testValidateNewStructureArtistPremiumTrialRequest(): void { $shopService = $this->getShopServiceMockFor('validateNewStructureArtistPremiumTrialRequest'); $phoneNumberUtil = $this ->getMockBuilder(PhoneNumberUtil::class) ->disableOriginalConstructor() ->getMock(); $shopService->phoneNumberUtil = $phoneNumberUtil; $request = new NewStructureArtistPremiumTrialRequest(); $request->setStructureName('Test Structure'); $request->setCity('Test City'); $request->setPostalCode('12345'); $request->setAddress('Test Address'); $request->setAddressComplement('Test Address Complement'); $request->setRepresentativePhone('+33123456789'); $phoneNumberUtil->expects(self::once()) ->method('isPossibleNumber') ->with('+33123456789') ->willReturn(true); $this->organizationFactory->expects(self::once()) ->method('interruptIfOrganizationExists') ->with(self::callback(function ($organizationCreationRequest) { return $organizationCreationRequest->getName() === 'Test Structure' && $organizationCreationRequest->getCity() === 'Test City' && $organizationCreationRequest->getPostalCode() === '12345' && $organizationCreationRequest->getStreetAddress1() === 'Test Address' && $organizationCreationRequest->getStreetAddress2() === 'Test Address Complement' && $organizationCreationRequest->getStreetAddress3() === ''; })); $shopService->validateNewStructureArtistPremiumTrialRequest($request); } /** * Test validateNewStructureArtistPremiumTrialRequest method with invalid phone number */ public function testValidateNewStructureArtistPremiumTrialRequestWithInvalidPhoneNumber(): void { $shopService = $this->getShopServiceMockFor('validateNewStructureArtistPremiumTrialRequest'); $phoneNumberUtil = $this->getMockBuilder(PhoneNumberUtil::class) ->disableOriginalConstructor() ->getMock(); $shopService->phoneNumberUtil = $phoneNumberUtil; $request = new NewStructureArtistPremiumTrialRequest(); $request->setRepresentativePhone('invalid-phone'); // Mock the phoneNumberUtil to return false for isPossibleNumber $phoneNumberUtil->expects(self::once()) ->method('isPossibleNumber') ->with('invalid-phone') ->willReturn(false); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Invalid phone number'); $shopService->validateNewStructureArtistPremiumTrialRequest($request); } }