Files
e-ticket/tests/Controller/AccountControllerTest.php
Serreau Jovann ab52a8d02f Add payouts, PDF attestations, sub-accounts, and webhook improvements
Payout system:
- Create Payout entity (stripePayoutId, status, amount, currency, destination, arrivalDate)
- Webhook handles payout.created/updated/paid/failed/canceled with email notification
- Payout list in /mon-compte virements tab with status badges
- PDF attestation on paid payouts with email attachment

PDF attestation:
- dompdf with DejaVu Sans font, yellow-orange gradient background
- Orange centered title bar, E-Cosplay logo, emitter/beneficiary info blocks
- QR code linking to /attestation/check/{payoutId} for authenticity verification
- Public verification page: shows payout details if valid, error if altered
- Legal disclaimer and CGV reference
- Button visible only when status is paid, opens in new tab

Sub-accounts:
- Add parentOrganizer (self-referencing ManyToOne) and subAccountPermissions (JSON) to User
- Permissions: scanner (validate tickets), events (CRUD), tickets (free invitations)
- Create sub-account with random password, send email with credentials
- Edit page with name/email/permissions checkboxes
- Delete with confirmation
- hasPermission() helper method

Account improvements:
- Block entire page for unapproved organizers with validation pending message
- Display stripeStatus in Stripe Connect banners
- Remove test payout button

Webhook v2 Connect events:
- v2.core.account.created/updated/closed → update stripeStatus
- capability_status_updated → sync charges/payouts enabled from capabilities
- PayoutPdfService for reusable PDF generation

Migrations: stripeStatus, Payout table, sub-account fields, drop pdfPath

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 23:49:48 +01:00

284 lines
8.1 KiB
PHP

<?php
namespace App\Tests\Controller;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class AccountControllerTest extends WebTestCase
{
public function testAccountRedirectsWhenNotAuthenticated(): void
{
$client = static::createClient();
$client->request('GET', '/mon-compte');
self::assertResponseRedirects();
}
public function testAccountReturnsSuccessWhenAuthenticated(): void
{
$client = static::createClient();
$user = $this->createUser();
$client->loginUser($user);
$client->request('GET', '/mon-compte');
self::assertResponseIsSuccessful();
}
public function testAccountTicketsTab(): void
{
$client = static::createClient();
$user = $this->createUser();
$client->loginUser($user);
$client->request('GET', '/mon-compte?tab=tickets');
self::assertResponseIsSuccessful();
}
public function testAccountPurchasesTab(): void
{
$client = static::createClient();
$user = $this->createUser();
$client->loginUser($user);
$client->request('GET', '/mon-compte?tab=purchases');
self::assertResponseIsSuccessful();
}
public function testAccountInvoicesTab(): void
{
$client = static::createClient();
$user = $this->createUser();
$client->loginUser($user);
$client->request('GET', '/mon-compte?tab=invoices');
self::assertResponseIsSuccessful();
}
public function testAccountSettingsTab(): void
{
$client = static::createClient();
$user = $this->createUser();
$client->loginUser($user);
$client->request('GET', '/mon-compte?tab=settings');
self::assertResponseIsSuccessful();
}
public function testAccountSettingsSubmit(): void
{
$client = static::createClient();
$user = $this->createUser();
$client->loginUser($user);
$client->request('POST', '/mon-compte/parametres', [
'first_name' => 'Updated',
'last_name' => 'Name',
'email' => $user->getEmail(),
'phone' => '0699887766',
'address' => '1 rue Test',
'postal_code' => '75001',
'city' => 'Paris',
]);
self::assertResponseRedirects('/mon-compte?tab=settings');
}
public function testOrganizerEventsTab(): void
{
$client = static::createClient();
$user = $this->createUser(['ROLE_ORGANIZER'], true);
$client->loginUser($user);
$client->request('GET', '/mon-compte?tab=events');
self::assertResponseIsSuccessful();
}
public function testOrganizerSubaccountsTab(): void
{
$client = static::createClient();
$user = $this->createUser(['ROLE_ORGANIZER'], true);
$client->loginUser($user);
$client->request('GET', '/mon-compte?tab=subaccounts');
self::assertResponseIsSuccessful();
}
public function testOrganizerPayoutsTab(): void
{
$client = static::createClient();
$user = $this->createUser(['ROLE_ORGANIZER'], true);
$client->loginUser($user);
$client->request('GET', '/mon-compte?tab=payouts');
self::assertResponseIsSuccessful();
}
public function testOrganizerSettingsDisablesNameFields(): void
{
$client = static::createClient();
$user = $this->createUser(['ROLE_ORGANIZER'], true);
$client->loginUser($user);
$client->request('POST', '/mon-compte/parametres', [
'email' => $user->getEmail(),
'phone' => '0699887766',
]);
self::assertResponseRedirects('/mon-compte?tab=settings');
}
public function testOrganizerNotApprovedShowsBlockingMessage(): void
{
$client = static::createClient();
$user = $this->createUser(['ROLE_ORGANIZER'], false);
$client->loginUser($user);
$client->request('GET', '/mon-compte');
self::assertResponseIsSuccessful();
self::assertSelectorTextContains('body', 'en cours de validation');
}
public function testOrganizerDefaultTabIsEvents(): void
{
$client = static::createClient();
$user = $this->createUser(['ROLE_ORGANIZER'], true);
$client->loginUser($user);
$client->request('GET', '/mon-compte');
self::assertResponseIsSuccessful();
}
public function testStripeConnectRedirectsForNonOrganizer(): void
{
$client = static::createClient();
$user = $this->createUser();
$client->loginUser($user);
$client->request('POST', '/mon-compte/stripe-connect');
self::assertResponseRedirects('/mon-compte');
}
public function testOrganizerWithoutStripeShowsSetupMessage(): void
{
$client = static::createClient();
$user = $this->createUser(['ROLE_ORGANIZER'], true);
$client->loginUser($user);
$crawler = $client->request('GET', '/mon-compte');
self::assertResponseIsSuccessful();
self::assertSelectorTextContains('body', 'Configuration Stripe requise');
}
public function testOrganizerWithStripePendingShowsMessage(): void
{
$client = static::createClient();
$em = static::getContainer()->get(EntityManagerInterface::class);
$user = $this->createUser(['ROLE_ORGANIZER'], true);
$user->setStripeAccountId('acct_pending');
$em->flush();
$client->loginUser($user);
$crawler = $client->request('GET', '/mon-compte');
self::assertResponseIsSuccessful();
self::assertSelectorTextContains('body', 'en cours de verification');
}
public function testOrganizerWithStripeActiveShowsSuccess(): void
{
$client = static::createClient();
$em = static::getContainer()->get(EntityManagerInterface::class);
$user = $this->createUser(['ROLE_ORGANIZER'], true);
$user->setStripeAccountId('acct_active');
$user->setStripeChargesEnabled(true);
$user->setStripePayoutsEnabled(true);
$em->flush();
$client->loginUser($user);
$crawler = $client->request('GET', '/mon-compte');
self::assertResponseIsSuccessful();
self::assertSelectorTextContains('body', 'Stripe Connect actif');
}
public function testStripeConnectReturn(): void
{
$client = static::createClient();
$user = $this->createUser(['ROLE_ORGANIZER'], true);
$client->loginUser($user);
$client->request('GET', '/stripe/connect/return');
self::assertResponseRedirects('/mon-compte');
}
public function testStripeConnectRefresh(): void
{
$client = static::createClient();
$user = $this->createUser(['ROLE_ORGANIZER'], true);
$client->loginUser($user);
$client->request('GET', '/stripe/connect/refresh');
self::assertResponseRedirects('/mon-compte/stripe-connect');
}
public function testStripeCancelResetsAccount(): void
{
$client = static::createClient();
$em = static::getContainer()->get(EntityManagerInterface::class);
$user = $this->createUser(['ROLE_ORGANIZER'], true);
$user->setStripeAccountId('acct_cancel');
$em->flush();
$client->loginUser($user);
$client->request('POST', '/mon-compte/stripe-cancel');
self::assertResponseRedirects('/mon-compte');
$em->refresh($user);
self::assertNull($user->getStripeAccountId());
}
/**
* @param list<string> $roles
*/
/**
* @param list<string> $roles
*/
private function createUser(array $roles = [], bool $approved = false): User
{
$em = static::getContainer()->get(EntityManagerInterface::class);
$user = new User();
$user->setEmail('test-account-'.uniqid().'@example.com');
$user->setFirstName('Test');
$user->setLastName('User');
$user->setPassword('$2y$13$hashed');
$user->setRoles($roles);
if ($approved) {
$user->setIsApproved(true);
}
$em->persist($user);
$em->flush();
return $user;
}
}