createAdminClient(); $this->seedCarrier('Export Alpha'); $response = $client->request('GET', self::EXPORT_URL); self::assertResponseIsSuccessful(); $headers = $response->getHeaders(false); self::assertStringContainsString(self::XLSX_MIME, $headers['content-type'][0] ?? ''); $disposition = $headers['content-disposition'][0] ?? ''; self::assertStringContainsString('attachment; filename="repertoire-transporteurs-', $disposition); self::assertMatchesRegularExpression( '/filename="repertoire-transporteurs-\d{8}\.xlsx"/', $disposition, ); // Le binaire est un XLSX relisible dont la 1re ligne porte les en-tetes. $headers = $this->gridFromResponse($response->getContent())[0]; self::assertSame('Nom', $headers[0]); self::assertContains('Certification', $headers); self::assertContains('Statut QUALIMAT', $headers); self::assertContains('Date de validité', $headers); self::assertContains('Affrété', $headers); self::assertContains('Volume m³', $headers); self::assertContains('Date de création', $headers); } public function testExportExcludesArchivedByDefault(): void { $client = $this->createAdminClient(); $this->seedCarrier('Active One'); $this->seedCarrier('Archived One', true); $names = $this->carrierNames($client->request('GET', self::EXPORT_URL)->getContent()); self::assertContains('ACTIVE ONE', $names); self::assertNotContains('ARCHIVED ONE', $names); } public function testExportRespectsSearchFilter(): void { $client = $this->createAdminClient(); $this->seedCarrier('Searchable Alpha'); $this->seedCarrier('Other Beta'); $names = $this->carrierNames( $client->request('GET', self::EXPORT_URL.'?search=alpha')->getContent(), ); self::assertContains('SEARCHABLE ALPHA', $names); self::assertNotContains('OTHER BETA', $names); } /** * Colonnes « Statut QUALIMAT » et « Date de validite » : alimentees par le * referentiel QUALIMAT lie (RG-4.04). Un transporteur complet seede un lien * QUALIMAT (statut « Valide », validite 31/12/2027). */ public function testExportPopulatesQualimatColumns(): void { $client = $this->createAdminClient(); $this->seedCompleteCarrier('Grelillier'); $flat = $this->flatten($this->gridFromResponse($client->request('GET', self::EXPORT_URL)->getContent())); self::assertStringContainsString('QUALIMAT', $flat); self::assertStringContainsString('Valide', $flat); self::assertStringContainsString('31/12/2027', $flat); } public function testForbiddenWithoutCarriersViewPermission(): void { $creds = $this->createUserWithPermission('core.users.view'); $client = $this->authenticatedClient($creds['username'], $creds['password']); $client->request('GET', self::EXPORT_URL); self::assertResponseStatusCodeSame(403); } public function testUnauthorizedWhenAnonymous(): void { $client = self::createClient(); $client->request('GET', self::EXPORT_URL); self::assertResponseStatusCodeSame(401); } /** * Relit le binaire XLSX d'une reponse et renvoie la grille de cellules. * * @return array> */ private function gridFromResponse(string $binary): array { $tmp = tempnam(sys_get_temp_dir(), 'xlsx_carrier_export_test_'); self::assertIsString($tmp); file_put_contents($tmp, $binary); try { return IOFactory::load($tmp)->getActiveSheet()->toArray(); } finally { @unlink($tmp); } } /** * Extrait la colonne « Nom » (1re colonne) des lignes de donnees. * * @return list */ private function carrierNames(string $binary): array { $rows = array_slice($this->gridFromResponse($binary), 1); // saute l'en-tete return array_values(array_map(static fn (array $row): string => (string) ($row[0] ?? ''), $rows)); } /** * Aplatit toute la grille en une chaine, pour les assertions de presence. * * @param array> $grid */ private function flatten(array $grid): string { return implode('|', array_map( static fn (array $row): string => implode('|', array_map(static fn ($cell): string => (string) $cell, $row)), $grid, )); } }