Organization Controller Tests
This commit is contained in:
parent
68864b3997
commit
d26d1cb118
|
|
@ -5,9 +5,11 @@ import {eyeIconLink, TABULATOR_FR_LANG} from "../js/global.js";
|
||||||
|
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
static values = {aws: String,
|
static values = {aws: String,
|
||||||
id: String,
|
id: String,
|
||||||
activities: Boolean,
|
activities: Boolean,
|
||||||
table: Boolean,
|
table: Boolean,
|
||||||
|
sadmin: Boolean,
|
||||||
|
user: Number
|
||||||
};
|
};
|
||||||
|
|
||||||
static targets = ["activityList", "emptyMessage"]
|
static targets = ["activityList", "emptyMessage"]
|
||||||
|
|
@ -18,7 +20,7 @@ export default class extends Controller {
|
||||||
this.loadActivities();
|
this.loadActivities();
|
||||||
}, 60000); // Refresh every 60 seconds
|
}, 60000); // Refresh every 60 seconds
|
||||||
}
|
}
|
||||||
if (this.tableValue){
|
if (this.tableValue && this.sadminValue) {
|
||||||
this.table();
|
this.table();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,7 +33,7 @@ export default class extends Controller {
|
||||||
placeholder: "Aucun résultat trouvé pour cette recherche",
|
placeholder: "Aucun résultat trouvé pour cette recherche",
|
||||||
locale: "fr", //'en' for English, 'fr' for French (en is default, no need to include it)
|
locale: "fr", //'en' for English, 'fr' for French (en is default, no need to include it)
|
||||||
|
|
||||||
ajaxURL: "/organization/data",
|
ajaxURL: `/organization/data/${this.userValue}`,
|
||||||
ajaxConfig: "GET",
|
ajaxConfig: "GET",
|
||||||
pagination: true,
|
pagination: true,
|
||||||
paginationMode: "remote",
|
paginationMode: "remote",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20260105152103 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_EMAIL ON organizations (email)');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('CREATE SCHEMA public');
|
||||||
|
$this->addSql('DROP INDEX UNIQ_IDENTIFIER_EMAIL');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20260106080636 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('CREATE UNIQUE INDEX UNIQ_ORGANIZATION_EMAIL ON organizations (email)');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('CREATE SCHEMA public');
|
||||||
|
$this->addSql('DROP INDEX UNIQ_ORGANIZATION_EMAIL');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20260106084653 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('ALTER TABLE organizations ALTER logo_url DROP NOT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('CREATE SCHEMA public');
|
||||||
|
$this->addSql('ALTER TABLE organizations ALTER logo_url SET NOT NULL');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,10 @@ use App\Service\LoggerService;
|
||||||
use App\Service\OrganizationsService;
|
use App\Service\OrganizationsService;
|
||||||
use App\Service\UserOrganizationService;
|
use App\Service\UserOrganizationService;
|
||||||
use App\Service\UserService;
|
use App\Service\UserService;
|
||||||
|
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
|
||||||
|
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\NonUniqueResultException;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
|
@ -48,15 +51,19 @@ class OrganizationController extends AbstractController
|
||||||
public function index(): Response
|
public function index(): Response
|
||||||
{
|
{
|
||||||
$this->denyAccessUnlessGranted('ROLE_ADMIN');
|
$this->denyAccessUnlessGranted('ROLE_ADMIN');
|
||||||
|
$actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier());
|
||||||
|
if($this->userService->hasAccessTo($actingUser, true)){
|
||||||
|
$orgCount = $this->organizationsRepository->count(['isDeleted' => false]);
|
||||||
|
|
||||||
$orgCount = $this->organizationsRepository->count(['isDeleted' => false]);
|
return $this->render('organization/index.html.twig', [
|
||||||
|
'hasOrganizations' => $orgCount > 0
|
||||||
return $this->render('organization/index.html.twig', [
|
]);
|
||||||
'hasOrganizations' => $orgCount > 0
|
}
|
||||||
]);
|
$this->loggerService->logAccessDenied($actingUser->getId());
|
||||||
|
throw new AccessDeniedHttpException('Access denied');
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Route(path: '/new', name: 'new', methods: ['GET', 'POST'])]
|
#[Route(path: '/create', name: 'create', methods: ['GET', 'POST'])]
|
||||||
public function new(Request $request): Response
|
public function new(Request $request): Response
|
||||||
{
|
{
|
||||||
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
|
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
|
||||||
|
|
@ -79,6 +86,7 @@ class OrganizationController extends AbstractController
|
||||||
return $this->redirectToRoute('organization_index');
|
return $this->redirectToRoute('organization_index');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$this->addFlash('error', 'Error creating organization: ' . $e->getMessage());
|
$this->addFlash('error', 'Error creating organization: ' . $e->getMessage());
|
||||||
|
$this->loggerService->logError('Error creating organization', ['acting_user_id' => $actingUser->getId(), 'error' => $e->getMessage()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $this->render('organization/new.html.twig', [
|
return $this->render('organization/new.html.twig', [
|
||||||
|
|
@ -146,7 +154,7 @@ class OrganizationController extends AbstractController
|
||||||
}
|
}
|
||||||
$this->actionService->createAction("Edit Organization", $actingUser, $organization, $organization->getName());
|
$this->actionService->createAction("Edit Organization", $actingUser, $organization, $organization->getName());
|
||||||
return $this->redirectToRoute('organization_index');
|
return $this->redirectToRoute('organization_index');
|
||||||
} catch (Exception $e) {
|
}catch (Exception $e) {
|
||||||
$this->addFlash('error', 'Error editing organization: ' . $e->getMessage());
|
$this->addFlash('error', 'Error editing organization: ' . $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -266,7 +274,7 @@ class OrganizationController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
// API endpoint to fetch organization data for Tabulator
|
// API endpoint to fetch organization data for Tabulator
|
||||||
#[Route(path: '/data', name: 'data', methods: ['GET'])]
|
#[Route(path: '/data/{id}', name: 'data', methods: ['GET'])]
|
||||||
public function data(Request $request): JsonResponse
|
public function data(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$this->denyAccessUnlessGranted('ROLE_ADMIN');
|
$this->denyAccessUnlessGranted('ROLE_ADMIN');
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,13 @@ namespace App\Entity;
|
||||||
|
|
||||||
use App\Repository\OrganizationsRepository;
|
use App\Repository\OrganizationsRepository;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
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;
|
||||||
|
|
||||||
#[ORM\Entity(repositoryClass: OrganizationsRepository::class)]
|
#[ORM\Entity(repositoryClass: OrganizationsRepository::class)]
|
||||||
|
#[ORM\UniqueConstraint(name: 'UNIQ_ORGANIZATION_EMAIL', fields: ['email'])]
|
||||||
|
#[UniqueEntity(fields: ['email'], message: 'Une organisation avec cet email existe déjà.')]
|
||||||
class Organizations
|
class Organizations
|
||||||
{
|
{
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
|
|
@ -24,7 +27,7 @@ class Organizations
|
||||||
#[ORM\Column(length: 255)]
|
#[ORM\Column(length: 255)]
|
||||||
private ?string $address = null;
|
private ?string $address = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 255)]
|
#[ORM\Column(length: 255, nullable: true)]
|
||||||
private ?string $logo_url = null;
|
private ?string $logo_url = null;
|
||||||
|
|
||||||
#[ORM\Column(options: ['default' => 'CURRENT_TIMESTAMP'])]
|
#[ORM\Column(options: ['default' => 'CURRENT_TIMESTAMP'])]
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ use Doctrine\DBAL\Types\Types;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
|
||||||
|
|
||||||
#[ORM\Entity(repositoryClass: UserRepository::class)]
|
#[ORM\Entity(repositoryClass: UserRepository::class)]
|
||||||
#[ORM\Table(name: '`user`')]
|
#[ORM\Table(name: '`user`')]
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ class OrganizationForm extends AbstractType
|
||||||
$builder
|
$builder
|
||||||
->add('email', EmailType::class, ['required' => true, 'label' => 'Email*'])
|
->add('email', EmailType::class, ['required' => true, 'label' => 'Email*'])
|
||||||
->add('name', TextType::class, ['required' => true, 'label' => 'Nom de l\'organisation*'])
|
->add('name', TextType::class, ['required' => true, 'label' => 'Nom de l\'organisation*'])
|
||||||
->add('address', TextType::class, ['required' => false, 'label' => 'Adresse'])
|
->add('address', TextType::class, ['required' => true, 'label' => 'Adresse'])
|
||||||
->add('number', TextType::class, ['required' => false, 'label' => 'Numéro de téléphone'])
|
->add('number', TextType::class, ['required' => true, 'label' => 'Numéro de téléphone'])
|
||||||
->add('logoUrl', FileType::class, [
|
->add('logoUrl', FileType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'label' => 'Logo',
|
'label' => 'Logo',
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,6 @@
|
||||||
<div class="card no-header-bg p-3 m-3">
|
<div class="card no-header-bg p-3 m-3">
|
||||||
<div class="card-header border-0">
|
<div class="card-header border-0">
|
||||||
<h2>Modifier l'organisation</h2>
|
<h2>Modifier l'organisation</h2>
|
||||||
{% if is_granted("ROLE_SUPER_ADMIN") %}
|
|
||||||
{# <a href="{{ path('organization_delete', {'id': organization.id}) }}" class="btn btn-danger">Supprimer</a>#}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if is_granted("ROLE_SUPER_ADMIN") %}
|
{% if is_granted("ROLE_SUPER_ADMIN") %}
|
||||||
<a href="{{ path('organization_new') }}" class="btn btn-primary">Ajouter une organisation</a>
|
<a href="{{ path('organization_create') }}" class="btn btn-primary">Ajouter une organisation</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
<div class="div text-center my-5 py-5">
|
<div class="div text-center my-5 py-5">
|
||||||
<h1 class="my-5 ty-5"> Aucune organisation trouvée. </h1>
|
<h1 class="my-5 ty-5"> Aucune organisation trouvée. </h1>
|
||||||
<a href="{{ path('organization_new') }}" class="btn btn-primary">Créer une organisation</a>
|
<a href="{{ path('organization_create') }}" class="btn btn-primary">Créer une organisation</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
@ -29,6 +29,8 @@
|
||||||
<div id="tabulator-org"
|
<div id="tabulator-org"
|
||||||
data-controller="organization"
|
data-controller="organization"
|
||||||
data-organization-table-value="true"
|
data-organization-table-value="true"
|
||||||
|
data-organization-user-value={{ app.user.getId() }}
|
||||||
|
data-organization-sadmin-value="{{ is_granted('ROLE_SUPER_ADMIN') ? true : false }}"
|
||||||
data-organization-aws-value="{{ aws_url }}">
|
data-organization-aws-value="{{ aws_url }}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,20 @@
|
||||||
<div class="w-100 h-100 p-5 m-auto">
|
<div class="w-100 h-100 p-5 m-auto">
|
||||||
<div class="card no-header-bg p-3 m-3">
|
<div class="card no-header-bg p-3 m-3">
|
||||||
<div class="card-header border-0">
|
<div class="card-header border-0">
|
||||||
|
{% for type, messages in app.flashes %}
|
||||||
|
{% for message in messages %}
|
||||||
|
<div class="alert alert-{{ type }}">
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
<div class="card-title d-flex justify-content-between align-items-center">
|
<div class="card-title d-flex justify-content-between align-items-center">
|
||||||
<h1>Ajouter une organisation</h1>
|
<h1>Ajouter une organisation</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form method="post" action="{{ path('organization_new') }}" enctype="multipart/form-data">
|
<form method="post" action="{{ path('organization_create') }}" enctype="multipart/form-data">
|
||||||
{{ form_start(form) }}
|
{{ form_start(form) }}
|
||||||
{{ form_widget(form) }}
|
{{ form_widget(form) }}
|
||||||
<button type="submit" class="btn btn-primary">Enregistrer</button>
|
<button type="submit" class="btn btn-primary">Enregistrer</button>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,359 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Tests\Controller;
|
||||||
|
|
||||||
|
use App\Entity\Apps;
|
||||||
|
use App\Entity\Organizations;
|
||||||
|
use App\Entity\Roles;
|
||||||
|
use App\Entity\UserOrganizatonApp;
|
||||||
|
use App\Entity\UsersOrganizations;
|
||||||
|
use App\Service\AwsService;
|
||||||
|
use App\Tests\Functional\AbstractFunctionalTest;
|
||||||
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
|
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||||
|
|
||||||
|
class OrganizationControllerTest extends AbstractFunctionalTest
|
||||||
|
{
|
||||||
|
|
||||||
|
//region INDEX tests
|
||||||
|
#[Test]
|
||||||
|
public function test_index_super_admin_success(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange
|
||||||
|
$admin = $this->createUser('sAdmin@test.com', ['ROLE_SUPER_ADMIN']);
|
||||||
|
$this->client->loginUser($admin);
|
||||||
|
|
||||||
|
// Create at least one org so 'hasOrganizations' becomes true
|
||||||
|
$this->createOrganization('Organization 1');
|
||||||
|
$this->createOrganization('Organization 2');
|
||||||
|
|
||||||
|
$this->client->request('GET', '/organization/');
|
||||||
|
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
self::assertSelectorTextNotContains('body', 'Aucune organisation trouvée');
|
||||||
|
|
||||||
|
self::assertSelectorExists('#tabulator-org');
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function test_index_regular_user_forbidden(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange
|
||||||
|
$user = $this->createUser('user@mail.com');
|
||||||
|
$this->client->loginUser($user);
|
||||||
|
|
||||||
|
// 2. Act
|
||||||
|
$this->client->request('GET', '/organization/');
|
||||||
|
// 3. Assert
|
||||||
|
self::assertResponseStatusCodeSame(403);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function test_index_no_organizations(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange
|
||||||
|
$admin = $this->createUser('user@mail.com', ['ROLE_SUPER_ADMIN']);
|
||||||
|
$this->client->loginUser($admin);
|
||||||
|
// 2. Act
|
||||||
|
$this->client->request('GET', '/organization/');
|
||||||
|
// 3. Assert
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
self::assertSelectorTextContains('body', 'Aucune organisation trouvée');
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//region CREATE tests
|
||||||
|
#[Test]
|
||||||
|
public function test_create_super_admin_success(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange: Disable reboot to keep our AWS mock alive
|
||||||
|
$this->client->disableReboot();
|
||||||
|
|
||||||
|
$admin = $this->createUser('admin@user.com', ['ROLE_SUPER_ADMIN']);
|
||||||
|
$this->client->loginUser($admin);
|
||||||
|
|
||||||
|
// 2. MOCK AWS Service (Crucial!)
|
||||||
|
// Your code calls $awsService->PutDocObj, so we must intercept that.
|
||||||
|
// 2. MOCK AWS Service
|
||||||
|
$awsMock = $this->createMock(AwsService::class);
|
||||||
|
$awsMock->expects($this->any())
|
||||||
|
->method('PutDocObj')
|
||||||
|
->willReturn(1); // <--- FIXED: Return an integer, not a boolean
|
||||||
|
|
||||||
|
// Inject the mock into the test container
|
||||||
|
static::getContainer()->set(AwsService::class, $awsMock);
|
||||||
|
|
||||||
|
// 3. Create a Dummy Image File
|
||||||
|
$tempFile = tempnam(sys_get_temp_dir(), 'test_logo');
|
||||||
|
file_put_contents($tempFile, 'fake image content'); // Create a dummy file
|
||||||
|
|
||||||
|
$logo = new UploadedFile(
|
||||||
|
$tempFile,
|
||||||
|
'logo.png',
|
||||||
|
'image/png',
|
||||||
|
null,
|
||||||
|
true // 'test' mode = true
|
||||||
|
);
|
||||||
|
|
||||||
|
// 4. Act: Request the page
|
||||||
|
$this->client->request('GET', '/organization/create');
|
||||||
|
|
||||||
|
// 5. Submit Form with the FILE object and correct field name 'logoUrl'
|
||||||
|
$this->client->submitForm('Enregistrer', [
|
||||||
|
'organization_form[name]' => 'New Organization',
|
||||||
|
'organization_form[email]' => 'unique-' . uniqid('', true) . '@test.com',
|
||||||
|
'organization_form[address]' => '123 Test Street',
|
||||||
|
'organization_form[number]' => '0102030405',
|
||||||
|
'organization_form[logoUrl]' => $logo, // Pass the OBJECT, not a string
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 6. Assert
|
||||||
|
// Check for redirect (302)
|
||||||
|
self::assertResponseRedirects('/organization/');
|
||||||
|
|
||||||
|
$this->client->followRedirect();
|
||||||
|
|
||||||
|
// Ensure we see the success state
|
||||||
|
self::assertSelectorTextNotContains('body', 'Aucune organisation trouvée');
|
||||||
|
self::assertSelectorExists('#tabulator-org');
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function test_create_regular_user_forbidden(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange
|
||||||
|
$user = $this->createUser('user@email.com');
|
||||||
|
$this->client->loginUser($user);
|
||||||
|
// 2. Act
|
||||||
|
$this->client->request('GET', '/organization/create');
|
||||||
|
// 3. Assert
|
||||||
|
self::assertResponseStatusCodeSame(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function test_create_super_admin_invalid_data(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange
|
||||||
|
$admin = $this->createUser('admin@email.com', ['ROLE_SUPER_ADMIN']);
|
||||||
|
$this->client->loginUser($admin);
|
||||||
|
// 2. Act
|
||||||
|
$this->client->request('GET', '/organization/create');
|
||||||
|
$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
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function test_create_super_admin_duplicate_email(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange
|
||||||
|
$admin = $this->createUser('admin@email.com', ['ROLE_SUPER_ADMIN']);
|
||||||
|
$this->client->loginUser($admin);
|
||||||
|
$existingOrg = $this->createOrganization('Existing Org');
|
||||||
|
// 2. Act
|
||||||
|
$this->client->request('GET', '/organization/create');
|
||||||
|
$this->client->submitForm('Enregistrer', [
|
||||||
|
'organization_form[name]' => 'New Org',
|
||||||
|
'organization_form[email]' => $existingOrg->getEmail(), // Duplicate email
|
||||||
|
'organization_form[address]' => '123 Test St',
|
||||||
|
'organization_form[number]' => '0102030405',
|
||||||
|
]);
|
||||||
|
// 3. Assert
|
||||||
|
self::assertResponseIsSuccessful(); // Form isn't redirected
|
||||||
|
self::assertSelectorTextContains('body', 'Une organisation avec cet email existe déjà.');
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//region EDIT tests
|
||||||
|
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function test_edit_super_admin_success(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange: Disable reboot to keep our AWS mock alive
|
||||||
|
$this->client->disableReboot();
|
||||||
|
|
||||||
|
$admin = $this->createUser('admin@user.com', ['ROLE_SUPER_ADMIN']);
|
||||||
|
$this->client->loginUser($admin);
|
||||||
|
|
||||||
|
// 2. MOCK AWS Service (Crucial!)
|
||||||
|
// Your code calls $awsService->PutDocObj, so we must intercept that.
|
||||||
|
// 2. MOCK AWS Service
|
||||||
|
$awsMock = $this->createMock(AwsService::class);
|
||||||
|
$awsMock->expects($this->any())
|
||||||
|
->method('PutDocObj')
|
||||||
|
->willReturn(1); // <--- FIXED: Return an integer, not a boolean
|
||||||
|
|
||||||
|
// Inject the mock into the test container
|
||||||
|
static::getContainer()->set(AwsService::class, $awsMock);
|
||||||
|
|
||||||
|
// 3. Create a Dummy Image File
|
||||||
|
$tempFile = tempnam(sys_get_temp_dir(), 'test_logo');
|
||||||
|
file_put_contents($tempFile, 'fake image content'); // Create a dummy file
|
||||||
|
|
||||||
|
$logo = new UploadedFile(
|
||||||
|
$tempFile,
|
||||||
|
'logo.png',
|
||||||
|
'image/png',
|
||||||
|
null,
|
||||||
|
true // 'test' mode = true
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create an organization to edit
|
||||||
|
$organization = $this->createOrganization('Org to Edit');
|
||||||
|
// 4. Act: Request the edit page
|
||||||
|
$this->client->request('GET', '/organization/edit/' . $organization->getId());
|
||||||
|
// 5. Submit Form with the FILE object and correct field name 'logoUrl'
|
||||||
|
$this->client->submitForm('Enregistrer', [
|
||||||
|
'organization_form[name]' => 'Edited Organization',
|
||||||
|
'organization_form[email]' => 'edited-' . uniqid('', true) . '@test.com',
|
||||||
|
'organization_form[address]' => '456 Edited Street',
|
||||||
|
'organization_form[number]' => '0504030201',
|
||||||
|
'organization_form[logoUrl]' => $logo, // Pass the OBJECT, not a
|
||||||
|
]);
|
||||||
|
// 6. Assert
|
||||||
|
// Check for redirect (302)
|
||||||
|
self::assertResponseRedirects('/organization/');
|
||||||
|
$this->client->followRedirect();
|
||||||
|
// Ensure we see the success state
|
||||||
|
self::assertSelectorTextNotContains('body', 'Aucune organisation trouvée');
|
||||||
|
self::assertSelectorExists('#tabulator-org');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function test_edit_regular_user_forbidden(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange
|
||||||
|
$user = $this->createUser('user@email.com');
|
||||||
|
$this->client->loginUser($user);
|
||||||
|
// Create an organization to edit
|
||||||
|
$organization = $this->createOrganization('Org to Edit');
|
||||||
|
// 2. Act
|
||||||
|
$this->client->request('GET', '/organization/edit/' . $organization->getId());
|
||||||
|
// 3. Assert
|
||||||
|
self::assertResponseStatusCodeSame(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function test_edit_super_admin_invalid_data(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange
|
||||||
|
$admin = $this->createUser('admin@mail.com', ['ROLE_SUPER_ADMIN']);
|
||||||
|
$this->client->loginUser($admin);
|
||||||
|
// Create an organization 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
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function test_edit_nonexistent_organization_not_found(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange
|
||||||
|
$admin = $this->createUser('admin@mail.com', ['ROLE_SUPER_ADMIN']);
|
||||||
|
$this->client->loginUser($admin);
|
||||||
|
// 2. Act
|
||||||
|
$this->client->request('GET', '/organization/edit/99999'); // Assuming
|
||||||
|
// 3. Assert
|
||||||
|
self::assertResponseStatusCodeSame(302);
|
||||||
|
|
||||||
|
self::assertResponseRedirects('/organization/');
|
||||||
|
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
//region DELETE tests
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function test_delete_super_admin_success(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange
|
||||||
|
$admin = $this->createUser('admin@email.com', ['ROLE_SUPER_ADMIN']);
|
||||||
|
$this->client->loginUser($admin);
|
||||||
|
$organization = $this->createOrganization('Org to Delete');
|
||||||
|
// 2. Act
|
||||||
|
$this->client->request('POST', '/organization/delete/' . $organization->getId());
|
||||||
|
// 3. Assert
|
||||||
|
self::assertResponseRedirects('/organization/');
|
||||||
|
$this->client->followRedirect();
|
||||||
|
self::assertSelectorTextNotContains('body', 'Org to Delete');
|
||||||
|
self::assertTrue($this->entityManager->getRepository(Organizations::class)->find($organization->getId())->isDeleted());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function test_delete_regular_user_forbidden(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange
|
||||||
|
$user = $this->createUser('user@mail.com');
|
||||||
|
$this->client->loginUser($user);
|
||||||
|
$organization = $this->createOrganization('Org to Delete');
|
||||||
|
// 2. Act
|
||||||
|
$this->client->request('POST', '/organization/delete/' . $organization->getId());
|
||||||
|
// 3. Assert
|
||||||
|
self::assertResponseStatusCodeSame(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function test_delete_nonexistent_organization_not_found(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange
|
||||||
|
$admin = $this->createUser('admin@user.com', ['ROLE_SUPER_ADMIN']);
|
||||||
|
$this->client->loginUser($admin);
|
||||||
|
// 2. Act
|
||||||
|
$this->client->request('POST', '/organization/delete/99999'); // Assuming
|
||||||
|
// 3. Assert
|
||||||
|
self::assertResponseStatusCodeSame(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function test_delete_organization_with_dependencies(): void
|
||||||
|
{
|
||||||
|
// 1. Arrange
|
||||||
|
$admin = $this->createUser('user@admin.com', ['ROLE_SUPER_ADMIN']);
|
||||||
|
$this->client->loginUser($admin);
|
||||||
|
$organization = $this->createOrganization('Org with Deps');
|
||||||
|
$app = $this->createApp('Dependent App');
|
||||||
|
$role = $this->createRole('ROLE_USER');
|
||||||
|
$uoLink = $this->createUOLink($admin, $organization);
|
||||||
|
$uoaLink = $this->createUOALink($uoLink, $app, $role);
|
||||||
|
// 2. Act
|
||||||
|
$this->client->request('POST', '/organization/delete/' . $organization->getId());
|
||||||
|
// 3. Assert
|
||||||
|
self::assertResponseRedirects('/organization/');
|
||||||
|
$this->client->followRedirect();
|
||||||
|
|
||||||
|
self::assertSelectorTextContains('body', 'Aucune organisation trouvée');
|
||||||
|
//link should be deactivated, not deleted
|
||||||
|
self::assertCount(1, $this->entityManager->getRepository(Apps::class)->findAll());
|
||||||
|
self::assertCount(1, $this->entityManager->getRepository(Roles::class)->findAll());
|
||||||
|
self::assertCount(1, $this->entityManager->getRepository(UsersOrganizations::class)->findAll());
|
||||||
|
self::assertCount(1, $this->entityManager->getRepository(UserOrganizatonApp::class)->findAll());
|
||||||
|
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(UserOrganizatonApp::class)->find($uoaLink->getId())->isActive());
|
||||||
|
self::assertSelectorNotExists('#tabulator-org');
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -66,7 +66,6 @@ abstract class AbstractFunctionalTest extends WebTestCase
|
||||||
$org->setNumber(100 + rand(1, 900)); // Example number
|
$org->setNumber(100 + rand(1, 900)); // Example number
|
||||||
$org->setAddress('123 ' . $name . ' St'); // Example address
|
$org->setAddress('123 ' . $name . ' St'); // Example address
|
||||||
$org->setLogoUrl('https://example.com/org_logo.png');
|
$org->setLogoUrl('https://example.com/org_logo.png');
|
||||||
// Add other required fields if Organizations has non-nullable columns
|
|
||||||
|
|
||||||
$this->entityManager->persist($org);
|
$this->entityManager->persist($org);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
@ -115,4 +114,9 @@ abstract class AbstractFunctionalTest extends WebTestCase
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
return $notification;
|
return $notification;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function countEntities(string $entityClass): int
|
||||||
|
{
|
||||||
|
return $this->entityManager->getRepository($entityClass)->count([]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue