update tests
This commit is contained in:
parent
2502baf265
commit
856e51ff09
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
|
@ -12,6 +12,7 @@
|
||||||
<server name="APPLICATION" value="solutions" force="true" />
|
<server name="APPLICATION" value="solutions" force="true" />
|
||||||
<server name="AWS_S3_PORTAL_URL" value="solutions" force="true" />
|
<server name="AWS_S3_PORTAL_URL" value="solutions" force="true" />
|
||||||
<env name="S3_PORTAL_BUCKET" value="test-bucket-placeholder" force="true" />
|
<env name="S3_PORTAL_BUCKET" value="test-bucket-placeholder" force="true" />
|
||||||
|
<env name="KERNEL_CLASS" value="App\Kernel" />
|
||||||
<server name="AWS_ENDPOINT" value="solutions" force="true" />
|
<server name="AWS_ENDPOINT" value="solutions" force="true" />
|
||||||
<server name="SHELL_VERBOSITY" value="-1" />
|
<server name="SHELL_VERBOSITY" value="-1" />
|
||||||
</php>
|
</php>
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ class ActionController extends AbstractController
|
||||||
#[Route('/organization/{id}/activities-ajax', name: 'app_organization_activities_ajax', methods: ['GET'])]
|
#[Route('/organization/{id}/activities-ajax', name: 'app_organization_activities_ajax', methods: ['GET'])]
|
||||||
public function fetchActivitiesAjax(Organizations $organization): JsonResponse
|
public function fetchActivitiesAjax(Organizations $organization): JsonResponse
|
||||||
{
|
{
|
||||||
|
$this->denyAccessUnlessGranted('ROLE_ADMIN');
|
||||||
$actions = $this->entityManager->getRepository(Actions::class)->findBy(
|
$actions = $this->entityManager->getRepository(Actions::class)->findBy(
|
||||||
['Organization' => $organization],
|
['Organization' => $organization],
|
||||||
['date' => 'DESC'],
|
['date' => 'DESC'],
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,12 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
use Symfony\Component\Asset\Packages;
|
use Symfony\Component\Asset\Packages;
|
||||||
|
use function Webmozart\Assert\Tests\StaticAnalysis\inArray;
|
||||||
|
|
||||||
#[Route(path: '/application', name: 'application_')]
|
#[Route(path: '/application', name: 'application_')]
|
||||||
|
|
||||||
class ApplicationController extends AbstractController
|
class ApplicationController extends AbstractController
|
||||||
|
|
@ -71,8 +75,9 @@ class ApplicationController extends AbstractController
|
||||||
$data = $request->request->all();
|
$data = $request->request->all();
|
||||||
$application->setName($data['name']);
|
$application->setName($data['name']);
|
||||||
$application->setDescription($data['description']);
|
$application->setDescription($data['description']);
|
||||||
$application->setDescriptionSmall($data['descriptionSmall']);
|
if (!empty($data['logo'])) {
|
||||||
$this->applicationService->handleLogoUpload($application, $data['logo']);
|
$this->applicationService->handleLogoUpload($application, $data['logo']);
|
||||||
|
}
|
||||||
$this->entityManager->persist($application);
|
$this->entityManager->persist($application);
|
||||||
$this->actionService->createAction("Modification de l'application ", $actingUser, null, $application->getId());
|
$this->actionService->createAction("Modification de l'application ", $actingUser, null, $application->getId());
|
||||||
$this->loggerService->logApplicationInformation('Application Edited', [
|
$this->loggerService->logApplicationInformation('Application Edited', [
|
||||||
|
|
@ -132,6 +137,8 @@ class ApplicationController extends AbstractController
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
$this->actionService->createAction("Authorization d'accès", $actingUser, $organization, $application->getName());
|
$this->actionService->createAction("Authorization d'accès", $actingUser, $organization, $application->getName());
|
||||||
return new Response('', Response::HTTP_OK);
|
return new Response('', Response::HTTP_OK);
|
||||||
|
}catch (HttpExceptionInterface $e){
|
||||||
|
throw $e;
|
||||||
} catch (\Exception $e){
|
} catch (\Exception $e){
|
||||||
$this->loggerService->logError('Application Authorization Failed', [
|
$this->loggerService->logError('Application Authorization Failed', [
|
||||||
'applicationId' => $id,
|
'applicationId' => $id,
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ class UserController extends AbstractController
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
// Calcul du flag de modification : utilisateur admin ET exactement 1 UO
|
// Calcul du flag de modification : utilisateur admin ET exactement 1 UO
|
||||||
if (empty($uoa)){
|
if (empty($uoa) || !$orgId){
|
||||||
$canEdit = false;
|
$canEdit = false;
|
||||||
}else{
|
}else{
|
||||||
$canEdit = $this->userService->canEditRolesCheck($actingUser, $user, $this->isGranted('ROLE_ADMIN'), $singleUo, $organization);
|
$canEdit = $this->userService->canEditRolesCheck($actingUser, $user, $this->isGranted('ROLE_ADMIN'), $singleUo, $organization);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
#[ORM\Entity(repositoryClass: OrganizationsRepository::class)]
|
#[ORM\Entity(repositoryClass: OrganizationsRepository::class)]
|
||||||
#[ORM\UniqueConstraint(name: 'UNIQ_ORGANIZATION_EMAIL', fields: ['email'])]
|
#[ORM\UniqueConstraint(name: 'UNIQ_ORGANIZATION_EMAIL', fields: ['email'])]
|
||||||
#[UniqueEntity(fields: ['email'], message: 'Une organisation avec cet email existe déjà.')]
|
#[UniqueEntity(fields: ['email'], message: 'Une organisation avec cet email existe déjà.')]
|
||||||
|
|
@ -46,6 +46,7 @@ class Organizations
|
||||||
private Collection $apps;
|
private Collection $apps;
|
||||||
|
|
||||||
#[ORM\Column(length: 255)]
|
#[ORM\Column(length: 255)]
|
||||||
|
#[Assert\NotBlank(message: "Le nom ne peut pas être vide.")]
|
||||||
private ?string $name = null;
|
private ?string $name = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@ class OrganizationForm extends AbstractType
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
{
|
{
|
||||||
$builder
|
$builder
|
||||||
->add('email', EmailType::class, ['required' => true, 'label' => 'Email*'])
|
->add('email', EmailType::class, ['required' => true, 'label' => 'Email*','empty_data' => ''])
|
||||||
->add('name', TextType::class, ['required' => true, 'label' => 'Nom de l\'organisation*'])
|
->add('name', TextType::class, ['required' => true, 'label' => 'Nom de l\'organisation*','empty_data' => ''])
|
||||||
->add('address', TextType::class, ['required' => true, 'label' => 'Adresse'])
|
->add('address', TextType::class, ['required' => true, 'label' => 'Adresse','empty_data' => ''])
|
||||||
->add('number', TextType::class, ['required' => true, 'label' => 'Numéro de téléphone'])
|
->add('number', TextType::class, ['required' => true, 'label' => 'Numéro de téléphone','empty_data' => ''])
|
||||||
->add('logoUrl', FileType::class, [
|
->add('logoUrl', FileType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'label' => 'Logo',
|
'label' => 'Logo',
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,9 @@ class UserForm extends AbstractType
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
{
|
{
|
||||||
$builder
|
$builder
|
||||||
->add('email', EmailType::class, ['required' => true, 'label' => 'Email*'])
|
->add('email', EmailType::class, ['required' => true, 'label' => 'Email*','empty_data' => ''])
|
||||||
->add('name', TextType::class, ['required' => true, 'label' => 'Prénom*'])
|
->add('name', TextType::class, ['required' => true, 'label' => 'Prénom*','empty_data' => ''])
|
||||||
->add('surname', TextType::class, ['required' => true, 'label' => 'Nom*'])
|
->add('surname', TextType::class, ['required' => true, 'label' => 'Nom*','empty_data' => ''])
|
||||||
->add('phoneNumber', TextType::class, ['required' => false, 'label' => 'Numéro de téléphone'])
|
->add('phoneNumber', TextType::class, ['required' => false, 'label' => 'Numéro de téléphone'])
|
||||||
->add('pictureUrl', FileType::class, [
|
->add('pictureUrl', FileType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ class UserService
|
||||||
|
|
||||||
public function __construct(private readonly EntityManagerInterface $entityManager,
|
public function __construct(private readonly EntityManagerInterface $entityManager,
|
||||||
private readonly Security $security,
|
private readonly Security $security,
|
||||||
private readonly AwsService $awsService,
|
|
||||||
private readonly LoggerService $loggerService,
|
private readonly LoggerService $loggerService,
|
||||||
private readonly ActionService $actionService,
|
private readonly ActionService $actionService,
|
||||||
private readonly EmailService $emailService,
|
private readonly EmailService $emailService,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<div class="card no-header-bg">
|
<div class="card no-header-bg">
|
||||||
|
|
||||||
<div class="card-header d-flex gap-2 mt-2">
|
<div class="card-header d-flex gap-2 mt-2">
|
||||||
<img class="rounded-circle " style="width:50px; height:50px;" src="{{ asset(application.logoMiniUrl)}}"
|
<img class="rounded-circle " style="width:50px; height:50px;" src="{{ asset(application.logoMiniUrl ?: 'img/sudalys_icon.png')}}"
|
||||||
alt="Logo application">
|
alt="Logo application">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h1>{{ application.name }}</h1>
|
<h1>{{ application.name }}</h1>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<div class="card ">
|
<div class="card ">
|
||||||
<div class="card-header d-flex gap-2">
|
<div class="card-header d-flex gap-2">
|
||||||
<img class="rounded-circle " style="width:50px; height:50px;" src="{{ asset(application.entity.logoMiniUrl) }}"
|
<img class="rounded-circle " style="width:50px; height:50px;" src="{{ asset(application.entity.logoMiniUrl ?: 'img/sudalys-icon.png') }}"
|
||||||
alt="Logo application">
|
alt="Logo application">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h1>{{ application.entity.name }}</h1>
|
<h1>{{ application.entity.name }}</h1>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Tests\Controller;
|
||||||
|
|
||||||
|
use App\Entity\Actions;
|
||||||
|
use App\Tests\Functional\AbstractFunctional;
|
||||||
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
|
|
||||||
|
class ActionController extends AbstractFunctional
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function fetch_activities_ajax_returns_json_response(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange: Authenticate
|
||||||
|
$user = $this->createUser('user@user.com', ['ROLE_SUPER_ADMIN']);
|
||||||
|
$this->client->loginUser($user);
|
||||||
|
|
||||||
|
$organization = $this->createOrganization('org');
|
||||||
|
|
||||||
|
// 3. Arrange: Create an Action linked to the Organization
|
||||||
|
$action = new Actions();
|
||||||
|
$action->setOrganization($organization); // Link to the org
|
||||||
|
$action->setUsers($user); // Link to the user
|
||||||
|
$action->setActionType('UPDATE'); // Required string
|
||||||
|
$action->setDescription('Updated profile details');
|
||||||
|
// Date is set automatically in __construct
|
||||||
|
|
||||||
|
$this->entityManager->persist($action);
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
// 4. Act: Request the URL using the Organization ID
|
||||||
|
$url = sprintf('/actions/organization/%d/activities-ajax', $organization->getId());
|
||||||
|
$this->client->request('GET', $url);
|
||||||
|
|
||||||
|
// 5. Assert: Verify Success
|
||||||
|
$this->assertResponseIsSuccessful(); // Status 200
|
||||||
|
$this->assertResponseHeaderSame('content-type', 'application/json');
|
||||||
|
|
||||||
|
// 6. Assert: Verify JSON Content
|
||||||
|
$responseContent = $this->client->getResponse()->getContent();
|
||||||
|
$this->assertJson($responseContent);
|
||||||
|
|
||||||
|
$data = json_decode($responseContent, true);
|
||||||
|
|
||||||
|
// Since we created 1 action, we expect the array to be non-empty
|
||||||
|
$this->assertIsArray($data);
|
||||||
|
$this->assertNotEmpty($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function fetch_activities_returns_404_for_invalid_organization(): void
|
||||||
|
{
|
||||||
|
$user = $this->createUser('user@user.com');
|
||||||
|
$this->client->loginUser($user);
|
||||||
|
// Act: Request with an ID that definitely doesn't exist (e.g., extremely high int)
|
||||||
|
$this->client->request('GET', '/actions/organization/99999999/activities-ajax');
|
||||||
|
|
||||||
|
$this->assertResponseStatusCodeSame(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Tests\Controller;
|
|
||||||
|
|
||||||
use App\Entity\Actions;
|
|
||||||
use App\Entity\Organizations;
|
|
||||||
use App\Entity\User;
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
|
||||||
|
|
||||||
class ActionControllerTest extends WebTestCase
|
|
||||||
{
|
|
||||||
private KernelBrowser $client;
|
|
||||||
private EntityManagerInterface $entityManager;
|
|
||||||
|
|
||||||
protected function setUp(): void
|
|
||||||
{
|
|
||||||
$this->client = static::createClient();
|
|
||||||
|
|
||||||
// Retrieve the EntityManager from the test container
|
|
||||||
$this->entityManager = static::getContainer()->get('doctrine')->getManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to create a valid User entity with all required fields and log them in.
|
|
||||||
*/
|
|
||||||
private function authenticateUser(): User
|
|
||||||
{
|
|
||||||
$user = new User();
|
|
||||||
$user->setEmail('test_' . uniqid() . '@example.com'); // Ensure uniqueness
|
|
||||||
$user->setPassword('secure_password');
|
|
||||||
$user->setName('Test');
|
|
||||||
$user->setSurname('User');
|
|
||||||
$user->setRoles(['ROLE_USER']);
|
|
||||||
|
|
||||||
// Defaults (isActive, isDeleted, dates) are handled by the User constructor
|
|
||||||
|
|
||||||
$this->entityManager->persist($user);
|
|
||||||
$this->entityManager->flush();
|
|
||||||
|
|
||||||
$this->client->loginUser($user);
|
|
||||||
|
|
||||||
return $user;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Test]
|
|
||||||
public function fetch_activities_ajax_returns_json_response(): void
|
|
||||||
{
|
|
||||||
// 1. Arrange: Authenticate
|
|
||||||
$user = $this->authenticateUser();
|
|
||||||
|
|
||||||
// 2. Arrange: Create a valid Organization
|
|
||||||
$organization = new Organizations();
|
|
||||||
$organization->setName('Test Corp');
|
|
||||||
$organization->setEmail('contact@testcorp.com');
|
|
||||||
$organization->setNumber(101); // Required int
|
|
||||||
$organization->setAddress('123 Main St'); // Required string
|
|
||||||
$organization->setLogoUrl('logo.png'); // Required string
|
|
||||||
// Defaults (isActive, isDeleted, collections) handled by Constructor
|
|
||||||
|
|
||||||
$this->entityManager->persist($organization);
|
|
||||||
|
|
||||||
// 3. Arrange: Create an Action linked to the Organization
|
|
||||||
$action = new Actions();
|
|
||||||
$action->setOrganization($organization); // Link to the org
|
|
||||||
$action->setUsers($user); // Link to the user
|
|
||||||
$action->setActionType('UPDATE'); // Required string
|
|
||||||
$action->setDescription('Updated profile details');
|
|
||||||
// Date is set automatically in __construct
|
|
||||||
|
|
||||||
$this->entityManager->persist($action);
|
|
||||||
$this->entityManager->flush();
|
|
||||||
|
|
||||||
// 4. Act: Request the URL using the Organization ID
|
|
||||||
$url = sprintf('/actions/organization/%d/activities-ajax', $organization->getId());
|
|
||||||
$this->client->request('GET', $url);
|
|
||||||
|
|
||||||
// 5. Assert: Verify Success
|
|
||||||
$this->assertResponseIsSuccessful(); // Status 200
|
|
||||||
$this->assertResponseHeaderSame('content-type', 'application/json');
|
|
||||||
|
|
||||||
// 6. Assert: Verify JSON Content
|
|
||||||
$responseContent = $this->client->getResponse()->getContent();
|
|
||||||
$this->assertJson($responseContent);
|
|
||||||
|
|
||||||
$data = json_decode($responseContent, true);
|
|
||||||
|
|
||||||
// Since we created 1 action, we expect the array to be non-empty
|
|
||||||
$this->assertIsArray($data);
|
|
||||||
$this->assertNotEmpty($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Test]
|
|
||||||
public function fetch_activities_returns_404_for_invalid_organization(): void
|
|
||||||
{
|
|
||||||
$this->authenticateUser();
|
|
||||||
|
|
||||||
// Act: Request with an ID that definitely doesn't exist (e.g., extremely high int)
|
|
||||||
$this->client->request('GET', '/actions/organization/99999999/activities-ajax');
|
|
||||||
|
|
||||||
// Assert: 404 Not Found (Standard Symfony ParamConverter behavior)
|
|
||||||
$this->assertResponseStatusCodeSame(404);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,10 +6,10 @@ use App\Entity\Apps;
|
||||||
use App\Entity\Organizations;
|
use App\Entity\Organizations;
|
||||||
use App\Service\ActionService;
|
use App\Service\ActionService;
|
||||||
use App\Service\LoggerService;
|
use App\Service\LoggerService;
|
||||||
use App\Tests\Functional\AbstractFunctionalTest;
|
use App\Tests\Functional\AbstractFunctional;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
|
|
||||||
class ApplicationControllerTest extends AbstractFunctionalTest
|
class ApplicationController extends AbstractFunctional
|
||||||
{
|
{
|
||||||
|
|
||||||
//region Index Tests
|
//region Index Tests
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
namespace App\Tests\Controller;
|
namespace App\Tests\Controller;
|
||||||
|
|
||||||
use App\Tests\Functional\AbstractFunctionalTest;
|
use App\Tests\Functional\AbstractFunctional;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
|
|
||||||
class IndexControllerTest extends AbstractFunctionalTest
|
class IndexController extends AbstractFunctional
|
||||||
{
|
{
|
||||||
|
|
||||||
//Region dashboard tests
|
//Region dashboard tests
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
namespace App\Tests\Controller;
|
namespace App\Tests\Controller;
|
||||||
|
|
||||||
use App\Tests\Functional\AbstractFunctionalTest;
|
use App\Tests\Functional\AbstractFunctional;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
|
|
||||||
|
|
||||||
class NotificationControllerTest extends AbstractFunctionalTest{
|
class NotificationController extends AbstractFunctional{
|
||||||
|
|
||||||
//region index tests
|
//region index tests
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
@ -61,7 +61,9 @@ class NotificationControllerTest extends AbstractFunctionalTest{
|
||||||
public function test_unread_unauthenticated_user_forbidden(): void
|
public function test_unread_unauthenticated_user_forbidden(): void
|
||||||
{
|
{
|
||||||
$this->client->request('GET', '/notifications/unread');
|
$this->client->request('GET', '/notifications/unread');
|
||||||
self::assertResponseStatusCodeSame(401);
|
self::assertResponseRedirects('/login');
|
||||||
|
$this->client->followRedirect();
|
||||||
|
self::assertResponseStatusCodeSame(200); // Login page
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
@ -8,11 +8,11 @@ use App\Entity\Roles;
|
||||||
use App\Entity\UserOrganizatonApp;
|
use App\Entity\UserOrganizatonApp;
|
||||||
use App\Entity\UsersOrganizations;
|
use App\Entity\UsersOrganizations;
|
||||||
use App\Service\AwsService;
|
use App\Service\AwsService;
|
||||||
use App\Tests\Functional\AbstractFunctionalTest;
|
use App\Tests\Functional\AbstractFunctional;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||||
|
|
||||||
class OrganizationControllerTest extends AbstractFunctionalTest
|
class OrganizationController extends AbstractFunctional
|
||||||
{
|
{
|
||||||
|
|
||||||
//region INDEX tests
|
//region INDEX tests
|
||||||
|
|
@ -246,23 +246,24 @@ class OrganizationControllerTest extends AbstractFunctionalTest
|
||||||
#[Test]
|
#[Test]
|
||||||
public function test_edit_super_admin_invalid_data(): void
|
public function test_edit_super_admin_invalid_data(): void
|
||||||
{
|
{
|
||||||
// 1. Arrange
|
|
||||||
$admin = $this->createUser('admin@mail.com', ['ROLE_SUPER_ADMIN']);
|
$admin = $this->createUser('admin@mail.com', ['ROLE_SUPER_ADMIN']);
|
||||||
$this->client->loginUser($admin);
|
$this->client->loginUser($admin);
|
||||||
// Create an organization to edit
|
|
||||||
$organization = $this->createOrganization('Org to Edit');
|
$organization = $this->createOrganization('Org to Edit');
|
||||||
// 2. Act
|
|
||||||
$this->client->request('GET', '/organization/edit/' . $organization->getId());
|
|
||||||
$this->client->submitForm('Enregistrer', [
|
|
||||||
'organization_form[name]' => '', // Invalid: name is required
|
|
||||||
'organization_form[email]' => 'not-an-email', // Invalid email format
|
|
||||||
'organization_form[address]' => '123 Test St',
|
|
||||||
'organization_form[number]' => '0102030405',
|
|
||||||
]);
|
|
||||||
// 3. Assert
|
|
||||||
self::assertResponseIsSuccessful(); // Form isn't redirected
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$this->client->request('GET', '/organization/edit/' . $organization->getId());
|
||||||
|
|
||||||
|
// Submit the form
|
||||||
|
$this->client->submitForm('Enregistrer', [
|
||||||
|
'organization_form[name]' => '',
|
||||||
|
'organization_form[email]' => 'not-an-email',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 1. Assert we are NOT redirected (Status 200)
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
|
||||||
|
// 2. Assert that validation errors appear in the HTML
|
||||||
|
self::assertSelectorExists('.invalid-feedback');
|
||||||
|
}
|
||||||
#[Test]
|
#[Test]
|
||||||
public function test_edit_nonexistent_organization_not_found(): void
|
public function test_edit_nonexistent_organization_not_found(): void
|
||||||
{
|
{
|
||||||
|
|
@ -334,6 +335,7 @@ class OrganizationControllerTest extends AbstractFunctionalTest
|
||||||
$app = $this->createApp('Dependent App');
|
$app = $this->createApp('Dependent App');
|
||||||
$role = $this->createRole('ROLE_USER');
|
$role = $this->createRole('ROLE_USER');
|
||||||
$uoLink = $this->createUOLink($admin, $organization);
|
$uoLink = $this->createUOLink($admin, $organization);
|
||||||
|
|
||||||
$uoaLink = $this->createUOALink($uoLink, $app, $role);
|
$uoaLink = $this->createUOALink($uoLink, $app, $role);
|
||||||
// 2. Act
|
// 2. Act
|
||||||
$this->client->request('POST', '/organization/delete/' . $organization->getId());
|
$this->client->request('POST', '/organization/delete/' . $organization->getId());
|
||||||
|
|
@ -348,7 +350,7 @@ class OrganizationControllerTest extends AbstractFunctionalTest
|
||||||
self::assertCount(1, $this->entityManager->getRepository(UsersOrganizations::class)->findAll());
|
self::assertCount(1, $this->entityManager->getRepository(UsersOrganizations::class)->findAll());
|
||||||
self::assertCount(1, $this->entityManager->getRepository(UserOrganizatonApp::class)->findAll());
|
self::assertCount(1, $this->entityManager->getRepository(UserOrganizatonApp::class)->findAll());
|
||||||
self::assertTrue($this->entityManager->getRepository(Organizations::class)->find($organization->getId())->isDeleted());
|
self::assertTrue($this->entityManager->getRepository(Organizations::class)->find($organization->getId())->isDeleted());
|
||||||
self::assertFalse($this->entityManager->getRepository(UserOrganizatonApp::class)->find($uoLink->getId())->isActive());
|
self::assertFalse($this->entityManager->getRepository(UsersOrganizations::class)->find($uoLink->getId())->isActive());
|
||||||
self::assertFalse($this->entityManager->getRepository(UserOrganizatonApp::class)->find($uoaLink->getId())->isActive());
|
self::assertFalse($this->entityManager->getRepository(UserOrganizatonApp::class)->find($uoaLink->getId())->isActive());
|
||||||
self::assertSelectorNotExists('#tabulator-org');
|
self::assertSelectorNotExists('#tabulator-org');
|
||||||
}
|
}
|
||||||
|
|
@ -12,12 +12,12 @@ use App\Entity\Roles;
|
||||||
use App\Entity\Organizations;
|
use App\Entity\Organizations;
|
||||||
use App\Entity\UsersOrganizations;
|
use App\Entity\UsersOrganizations;
|
||||||
use App\Entity\UserOrganizatonApp;
|
use App\Entity\UserOrganizatonApp;
|
||||||
use App\Tests\Functional\AbstractFunctionalTest;
|
use App\Tests\Functional\AbstractFunctional;
|
||||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||||
use function Symfony\Component\DependencyInjection\Loader\Configurator\param;
|
use function Symfony\Component\DependencyInjection\Loader\Configurator\param;
|
||||||
|
|
||||||
//This test will generate warning, ignore it
|
//This test will generate warning, ignore it
|
||||||
class UserControllerTest extends AbstractFunctionalTest
|
class UserController extends AbstractFunctional
|
||||||
{
|
{
|
||||||
//region Index Tests
|
//region Index Tests
|
||||||
|
|
||||||
|
|
@ -435,7 +435,9 @@ class UserControllerTest extends AbstractFunctionalTest
|
||||||
$this->client->loginUser($admin);
|
$this->client->loginUser($admin);
|
||||||
$this->client->request('GET', '/user/new');
|
$this->client->request('GET', '/user/new');
|
||||||
$this->client->followRedirect();
|
$this->client->followRedirect();
|
||||||
self::assertResponseStatusCodeSame(403);
|
self::assertResponseIsSuccessful();
|
||||||
|
|
||||||
|
self::assertSelectorTextContains('body', 'Accès non autorisé.');
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
@ -13,7 +13,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
abstract class AbstractFunctionalTest extends WebTestCase
|
abstract class AbstractFunctional extends WebTestCase
|
||||||
{
|
{
|
||||||
protected KernelBrowser $client;
|
protected KernelBrowser $client;
|
||||||
protected EntityManagerInterface $entityManager;
|
protected EntityManagerInterface $entityManager;
|
||||||
|
|
@ -77,6 +77,8 @@ abstract class AbstractFunctionalTest extends WebTestCase
|
||||||
$uo = new UsersOrganizations();
|
$uo = new UsersOrganizations();
|
||||||
$uo->setUsers($user);
|
$uo->setUsers($user);
|
||||||
$uo->setOrganization($organization);
|
$uo->setOrganization($organization);
|
||||||
|
$uo->setIsActive(true);
|
||||||
|
$uo->setStatut("ACCEPTED");
|
||||||
$this->entityManager->persist($uo);
|
$this->entityManager->persist($uo);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
|
@ -7,6 +7,7 @@ use App\Entity\CguUser;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Repository\CguRepository; // <--- Import your actual repository
|
use App\Repository\CguRepository; // <--- Import your actual repository
|
||||||
use App\Service\CguUserService;
|
use App\Service\CguUserService;
|
||||||
|
use App\Service\LoggerService;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
|
@ -16,11 +17,13 @@ class CguUserServiceTest extends TestCase
|
||||||
{
|
{
|
||||||
private CguUserService $service;
|
private CguUserService $service;
|
||||||
private MockObject|EntityManagerInterface $entityManager;
|
private MockObject|EntityManagerInterface $entityManager;
|
||||||
|
private MockObject|LoggerService $loggerService;
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
$this->entityManager = $this->createMock(EntityManagerInterface::class);
|
$this->entityManager = $this->createMock(EntityManagerInterface::class);
|
||||||
$this->service = new CguUserService($this->entityManager);
|
$this->loggerService = $this->createMock(LoggerService::class);
|
||||||
|
$this->service = new CguUserService($this->entityManager, $this->loggerService);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ class NotificationServiceTest extends TestCase
|
||||||
->method('dispatch')
|
->method('dispatch')
|
||||||
->with($this->callback(function (NotificationMessage $message) {
|
->with($this->callback(function (NotificationMessage $message) {
|
||||||
return $message->getType() === NotificationService::TYPE_USER_DEACTIVATED
|
return $message->getType() === NotificationService::TYPE_USER_DEACTIVATED
|
||||||
&& $message->getTitle() === 'Membre retiré'
|
&& $message->getTitle() === 'Membre désactivé'
|
||||||
&& str_contains($message->getMessage(), 'Bob Builder a été désactivé de BuildIt')
|
&& str_contains($message->getMessage(), 'Bob Builder a été désactivé de BuildIt')
|
||||||
&& $message->getData()['userId'] === 3;
|
&& $message->getData()['userId'] === 3;
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ use App\Service\NotificationService;
|
||||||
use App\Service\OrganizationsService;
|
use App\Service\OrganizationsService;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use PHPUnit\Framework\Attributes\DataProvider;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
@ -84,47 +85,48 @@ class OrganizationsServiceTest extends TestCase
|
||||||
$file = $this->createMock(UploadedFile::class);
|
$file = $this->createMock(UploadedFile::class);
|
||||||
$file->method('guessExtension')->willReturn('png');
|
$file->method('guessExtension')->willReturn('png');
|
||||||
|
|
||||||
// Expect AWS Upload
|
|
||||||
$this->awsService->expects($this->once())
|
|
||||||
->method('PutDocObj')
|
|
||||||
->with(
|
|
||||||
'test-bucket',
|
|
||||||
$file,
|
|
||||||
$this->stringContains('MyOrg_'), // Filename check
|
|
||||||
'png',
|
|
||||||
'logo/'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Expect Log
|
|
||||||
$this->loggerService->expects($this->once())->method('logAWSAction');
|
|
||||||
|
|
||||||
$this->service->handleLogo($org, $file);
|
$this->service->handleLogo($org, $file);
|
||||||
|
|
||||||
// Assert URL is set on entity
|
// Assert URL is set on entity
|
||||||
$this->assertStringContainsString('logo/MyOrg_', $org->getLogoUrl());
|
$this->assertStringContainsString('uploads/organization_logos/MyOrg_', $org->getLogoUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testHandleLogoThrowsException(): void
|
public function testHandleLogoThrowsException(): void
|
||||||
{
|
{
|
||||||
|
// 1. Setup the Entity
|
||||||
$org = new Organizations();
|
$org = new Organizations();
|
||||||
$this->setEntityId($org, 1);
|
$this->setEntityId($org, 1); // Assuming you have a helper for reflection ID setting
|
||||||
$org->setName('MyOrg');
|
$org->setName('MyOrg');
|
||||||
|
|
||||||
|
// 2. Setup the File Mock
|
||||||
$file = $this->createMock(UploadedFile::class);
|
$file = $this->createMock(UploadedFile::class);
|
||||||
$file->method('guessExtension')->willReturn('png');
|
$file->method('guessExtension')->willReturn('png');
|
||||||
|
|
||||||
// Simulate AWS Failure
|
// --- CRITICAL PART ---
|
||||||
$this->awsService->method('PutDocObj')
|
// We tell the mock: "When move() is called, crash with an exception."
|
||||||
->willThrowException(new FileException('S3 Down'));
|
// We use a generic Exception here because your try/catch block catches \Exception
|
||||||
|
$file->method('move')
|
||||||
|
->willThrowException(new \Exception('Disk full or permission denied'));
|
||||||
|
|
||||||
// Expect Error Log
|
// 3. Expect the Logger Call
|
||||||
$this->loggerService->expects($this->once())
|
$this->loggerService->expects($this->once())
|
||||||
->method('logError')
|
->method('logError')
|
||||||
->with('Failed to upload organization logo to S3', $this->anything());
|
->with(
|
||||||
|
// This string MUST match the first argument in your actual code
|
||||||
|
'File upload failed',
|
||||||
|
// We use a callback to validate the context array contains the right ID
|
||||||
|
$this->callback(function($context) use ($org) {
|
||||||
|
return $context['target_organization_id'] === $org->getId()
|
||||||
|
&& $context['message'] === 'Disk full or permission denied';
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// 4. Expect the final exception re-thrown by your service
|
||||||
$this->expectException(FileException::class);
|
$this->expectException(FileException::class);
|
||||||
$this->expectExceptionMessage('Failed to upload logo to S3: S3 Down');
|
$this->expectExceptionMessage('File upload failed.');
|
||||||
|
|
||||||
|
// 5. Run the method
|
||||||
$this->service->handleLogo($org, $file);
|
$this->service->handleLogo($org, $file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,8 +229,6 @@ class OrganizationsServiceTest extends TestCase
|
||||||
// 7. Run
|
// 7. Run
|
||||||
$result = $this->service->notifyOrganizationAdmins($data, 'USER_ACCEPTED');
|
$result = $this->service->notifyOrganizationAdmins($data, 'USER_ACCEPTED');
|
||||||
|
|
||||||
// The service returns the last admin UO processed (based on loop)
|
|
||||||
$this->assertSame($adminUO, $result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -268,4 +268,118 @@ class OrganizationsServiceTest extends TestCase
|
||||||
|
|
||||||
$this->service->notifyOrganizationAdmins(['user' => $user, 'organization' => $org], 'USER_ACCEPTED');
|
$this->service->notifyOrganizationAdmins(['user' => $user, 'organization' => $org], 'USER_ACCEPTED');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testNotifyOrganizationAdminsSkipsNonAdmins(): void
|
||||||
|
{
|
||||||
|
// 1. Setup Data
|
||||||
|
$targetUser = new User(); $this->setEntityId($targetUser, 100);
|
||||||
|
$nonAdminUser = new User(); $this->setEntityId($nonAdminUser, 200);
|
||||||
|
$org = new Organizations(); $this->setEntityId($org, 50);
|
||||||
|
|
||||||
|
// 2. Setup the "Link" to the Org (The user is in the org, but not an admin)
|
||||||
|
$uoNonAdmin = new UsersOrganizations();
|
||||||
|
$uoNonAdmin->setUsers($nonAdminUser);
|
||||||
|
$uoNonAdmin->setOrganization($org);
|
||||||
|
|
||||||
|
// 3. Mock Repos
|
||||||
|
$rolesRepo = $this->createMock(EntityRepository::class);
|
||||||
|
// It doesn't matter what roles repo returns, the check fails later at UOA
|
||||||
|
|
||||||
|
// The UO Repo finds the user as a member of the org
|
||||||
|
$this->uoRepository->method('findBy')->willReturn([$uoNonAdmin]);
|
||||||
|
|
||||||
|
// CRITICAL: The UOA Repo returns NULL (No Admin record found)
|
||||||
|
$uoaRepo = $this->createMock(EntityRepository::class);
|
||||||
|
$uoaRepo->method('findOneBy')->willReturn(null);
|
||||||
|
|
||||||
|
$this->entityManager->method('getRepository')->willReturnMap([
|
||||||
|
[Roles::class, $rolesRepo],
|
||||||
|
[UserOrganizatonApp::class, $uoaRepo],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 4. Expectations: ensure NOTHING happens
|
||||||
|
$this->notificationService->expects($this->never())->method($this->anything());
|
||||||
|
$this->loggerService->expects($this->never())->method('logAdminNotified');
|
||||||
|
|
||||||
|
// 5. Run
|
||||||
|
$this->service->notifyOrganizationAdmins(
|
||||||
|
['user' => $targetUser, 'organization' => $org],
|
||||||
|
'USER_ACCEPTED'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[DataProvider('notificationCasesProvider')]
|
||||||
|
public function testNotifyOrganizationAdminsHandlesAllCases(string $caseType, string $expectedMethod): void
|
||||||
|
{
|
||||||
|
// 1. Setup Data
|
||||||
|
$targetUser = new User(); $this->setEntityId($targetUser, 100);
|
||||||
|
$adminUser = new User(); $this->setEntityId($adminUser, 999);
|
||||||
|
$org = new Organizations(); $this->setEntityId($org, 50);
|
||||||
|
|
||||||
|
// 2. Setup Admin Link
|
||||||
|
$adminUO = new UsersOrganizations();
|
||||||
|
$this->setEntityId($adminUO, 555);
|
||||||
|
$adminUO->setUsers($adminUser);
|
||||||
|
$adminUO->setOrganization($org);
|
||||||
|
|
||||||
|
// 3. Setup Role & UOA
|
||||||
|
$adminRole = new Roles();
|
||||||
|
$adminRole->setName('ADMIN');
|
||||||
|
|
||||||
|
$uoa = new UserOrganizatonApp();
|
||||||
|
$uoa->setUserOrganization($adminUO);
|
||||||
|
$uoa->setRole($adminRole);
|
||||||
|
$uoa->setIsActive(true);
|
||||||
|
|
||||||
|
// 4. Mocks
|
||||||
|
$rolesRepo = $this->createMock(EntityRepository::class);
|
||||||
|
$rolesRepo->method('findOneBy')->willReturn($adminRole);
|
||||||
|
|
||||||
|
$this->uoRepository->method('findBy')->willReturn([$adminUO]);
|
||||||
|
|
||||||
|
$uoaRepo = $this->createMock(EntityRepository::class);
|
||||||
|
$uoaRepo->method('findOneBy')->willReturn($uoa);
|
||||||
|
|
||||||
|
$this->entityManager->method('getRepository')->willReturnMap([
|
||||||
|
[Roles::class, $rolesRepo],
|
||||||
|
[UserOrganizatonApp::class, $uoaRepo],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 5. Dynamic Expectations
|
||||||
|
// We expect the *variable* method name passed by the provider
|
||||||
|
$this->notificationService->expects($this->once())
|
||||||
|
->method($expectedMethod)
|
||||||
|
->with($adminUser, $targetUser, $org);
|
||||||
|
|
||||||
|
// We expect the logger to receive the specific $caseType
|
||||||
|
$this->loggerService->expects($this->once())
|
||||||
|
->method('logAdminNotified')
|
||||||
|
->with([
|
||||||
|
'admin_user_id' => 999,
|
||||||
|
'target_user_id' => 100,
|
||||||
|
'organization_id' => 50,
|
||||||
|
'case' => $caseType // <--- Verified here
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 6. Run
|
||||||
|
$this->service->notifyOrganizationAdmins(
|
||||||
|
['user' => $targetUser, 'organization' => $org],
|
||||||
|
$caseType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the data for the test above.
|
||||||
|
* Format: [ 'Case String', 'Expected Service Method Name' ]
|
||||||
|
*/
|
||||||
|
public static function notificationCasesProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Invited Case' => ['USER_INVITED', 'notifyUserInvited'],
|
||||||
|
'Deactivated Case' => ['USER_DEACTIVATED', 'notifyUserDeactivated'],
|
||||||
|
'Deleted Case' => ['USER_DELETED', 'notifyUserDeleted'],
|
||||||
|
'Activated Case' => ['USER_ACTIVATED', 'notifyUserActivated'],
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,8 +7,8 @@ use App\Entity\Roles;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Entity\UserOrganizatonApp;
|
use App\Entity\UserOrganizatonApp;
|
||||||
use App\Entity\UsersOrganizations;
|
use App\Entity\UsersOrganizations;
|
||||||
|
use App\Event\UserCreatedEvent;
|
||||||
use App\Service\ActionService;
|
use App\Service\ActionService;
|
||||||
use App\Service\AwsService;
|
|
||||||
use App\Service\EmailService;
|
use App\Service\EmailService;
|
||||||
use App\Service\LoggerService;
|
use App\Service\LoggerService;
|
||||||
use App\Service\OrganizationsService;
|
use App\Service\OrganizationsService;
|
||||||
|
|
@ -19,6 +19,7 @@ use Doctrine\ORM\EntityRepository;
|
||||||
use League\Bundle\OAuth2ServerBundle\Model\AccessToken;
|
use League\Bundle\OAuth2ServerBundle\Model\AccessToken;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||||
use Symfony\Bundle\SecurityBundle\Security;
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||||
|
|
||||||
|
|
@ -29,42 +30,37 @@ class UserServiceTest extends TestCase
|
||||||
// Mocks
|
// Mocks
|
||||||
private MockObject|EntityManagerInterface $entityManager;
|
private MockObject|EntityManagerInterface $entityManager;
|
||||||
private MockObject|Security $security;
|
private MockObject|Security $security;
|
||||||
private MockObject|AwsService $awsService;
|
|
||||||
private MockObject|LoggerService $loggerService;
|
private MockObject|LoggerService $loggerService;
|
||||||
private MockObject|ActionService $actionService;
|
private MockObject|ActionService $actionService;
|
||||||
private MockObject|EmailService $emailService;
|
private MockObject|EmailService $emailService;
|
||||||
private MockObject|OrganizationsService $organizationsService;
|
private MockObject|OrganizationsService $organizationsService;
|
||||||
|
private MockObject|EventDispatcherInterface $eventDispatcher;
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
$this->entityManager = $this->createMock(EntityManagerInterface::class);
|
$this->entityManager = $this->createMock(EntityManagerInterface::class);
|
||||||
$this->security = $this->createMock(Security::class);
|
$this->security = $this->createMock(Security::class);
|
||||||
$this->awsService = $this->createMock(AwsService::class);
|
|
||||||
$this->actionService = $this->createMock(ActionService::class);
|
$this->actionService = $this->createMock(ActionService::class);
|
||||||
$this->emailService = $this->createMock(EmailService::class);
|
$this->emailService = $this->createMock(EmailService::class);
|
||||||
$this->organizationsService = $this->createMock(OrganizationsService::class);
|
$this->organizationsService = $this->createMock(OrganizationsService::class);
|
||||||
|
|
||||||
// HANDLING READONLY LOGGER SERVICE
|
|
||||||
// PHPUnit 10+ generally handles readonly classes fine.
|
|
||||||
// If your LoggerService is 'final readonly', you cannot mock it easily.
|
|
||||||
// Assuming it is just 'readonly class LoggerService':
|
|
||||||
$this->loggerService = $this->createMock(LoggerService::class);
|
$this->loggerService = $this->createMock(LoggerService::class);
|
||||||
|
$this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||||
|
|
||||||
$this->userService = new UserService(
|
$this->userService = new UserService(
|
||||||
$this->entityManager,
|
$this->entityManager,
|
||||||
$this->security,
|
$this->security,
|
||||||
$this->awsService,
|
|
||||||
$this->loggerService,
|
$this->loggerService,
|
||||||
$this->actionService,
|
$this->actionService,
|
||||||
$this->emailService,
|
$this->emailService,
|
||||||
$this->organizationsService
|
$this->organizationsService,
|
||||||
|
$this->eventDispatcher
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGenerateRandomPassword(): void
|
public function testGenerateRandomPassword(): void
|
||||||
{
|
{
|
||||||
$password = $this->userService->generateRandomPassword();
|
$password = $this->userService->generateRandomPassword();
|
||||||
$this->assertEquals(50, strlen($password));
|
$this->assertEquals(64, strlen($password));
|
||||||
$this->assertMatchesRegularExpression('/[a-zA-Z0-9!@#$%^&*()_+]+/', $password);
|
$this->assertMatchesRegularExpression('/[a-zA-Z0-9!@#$%^&*()_+]+/', $password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -178,27 +174,10 @@ class UserServiceTest extends TestCase
|
||||||
$file = $this->createMock(UploadedFile::class);
|
$file = $this->createMock(UploadedFile::class);
|
||||||
$file->method('guessExtension')->willReturn('jpg');
|
$file->method('guessExtension')->willReturn('jpg');
|
||||||
|
|
||||||
// Expect AWS Call
|
|
||||||
$this->awsService->expects($this->once())
|
|
||||||
->method('PutDocObj')
|
|
||||||
->with(
|
|
||||||
$this->anything(), // ENV variable usually
|
|
||||||
$file,
|
|
||||||
$this->stringContains('JohnDoe_'),
|
|
||||||
'jpg',
|
|
||||||
'profile/'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Expect Logger Call
|
|
||||||
$this->loggerService->expects($this->once())
|
|
||||||
->method('logAWSAction');
|
|
||||||
|
|
||||||
// Set fake ENV for test context if needed, or ignore the argument in mock
|
|
||||||
$_ENV['S3_PORTAL_BUCKET'] = 'test-bucket';
|
|
||||||
|
|
||||||
$this->userService->handleProfilePicture($user, $file);
|
$this->userService->handleProfilePicture($user, $file);
|
||||||
|
|
||||||
$this->assertStringContainsString('profile/JohnDoe_', $user->getPictureUrl());
|
$this->assertStringContainsString('uploads/profile_pictures/JohnDoe_', $user->getPictureUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSyncUserRolesAddsRole(): void
|
public function testSyncUserRolesAddsRole(): void
|
||||||
|
|
@ -247,32 +226,37 @@ class UserServiceTest extends TestCase
|
||||||
$newUser->setEmail('jane@doe.com');
|
$newUser->setEmail('jane@doe.com');
|
||||||
|
|
||||||
$actingUser = new User();
|
$actingUser = new User();
|
||||||
$this->setEntityId($actingUser, 99); // Give acting user an ID
|
$this->setEntityId($actingUser, 99);
|
||||||
$actingUser->setEmail('admin@test.com');
|
$actingUser->setEmail('admin@test.com');
|
||||||
|
|
||||||
// When persist is called, we force an ID onto $newUser to simulate DB insertion
|
// 1. Expect the Entity Manager to save the user
|
||||||
$this->entityManager->expects($this->exactly(2))
|
$this->entityManager->expects($this->atLeastOnce())
|
||||||
->method('persist')
|
->method('persist')
|
||||||
->with($newUser)
|
->with($newUser)
|
||||||
->willReturnCallback(function ($entity) {
|
->willReturnCallback(function ($entity) {
|
||||||
$this->setEntityId($entity, 123); // Simulate DB assigning ID 123
|
$this->setEntityId($entity, 123);
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->entityManager->expects($this->exactly(2))->method('flush');
|
$this->entityManager->expects($this->atLeastOnce())->method('flush');
|
||||||
|
|
||||||
// Now expects ID 123
|
// 2. IMPORTANT: Expect the Event Dispatcher to be called
|
||||||
$this->loggerService->expects($this->once())
|
// We check that it receives an instance of UserCreatedEvent
|
||||||
->method('logUserCreated')
|
$this->eventDispatcher->expects($this->once())
|
||||||
->with(123, 99);
|
->method('dispatch')
|
||||||
|
->with($this->isInstanceOf(UserCreatedEvent::class))
|
||||||
|
->willReturn(new UserCreatedEvent($newUser, $actingUser));
|
||||||
|
|
||||||
$this->emailService->expects($this->once())->method('sendPasswordSetupEmail');
|
// 3. REMOVE direct expectations for emailService, loggerService, and actionService
|
||||||
$this->actionService->expects($this->once())->method('createAction');
|
// Because UserService no longer calls them—the Subscriber does.
|
||||||
|
// (If you want to test the Subscriber, that should be in a separate UserSubscriberTest)
|
||||||
|
|
||||||
|
// Execute
|
||||||
$this->userService->createNewUser($newUser, $actingUser, null);
|
$this->userService->createNewUser($newUser, $actingUser, null);
|
||||||
|
|
||||||
// Assertions
|
// 4. Assertions
|
||||||
$this->assertEquals('Jane', $newUser->getName());
|
$this->assertEquals('Jane', $newUser->getName()); // Verify formatting (formatUserData)
|
||||||
$this->assertEquals(123, $newUser->getId()); // Verify ID was "generated"
|
$this->assertEquals(123, $newUser->getId());
|
||||||
|
$this->assertFalse($newUser->isActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLinkUserToOrganization(): void
|
public function testLinkUserToOrganization(): void
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fake image content
|
||||||
Loading…
Reference in New Issue