get(Connection::class); $connection->executeStatement('DELETE FROM tax_data'); // Jeu de donnees minimal pour eviter la dependance aux fixtures lourdes. $rows = [ [2019, '11', 'ILE-DE-FRANCE', '75', '75056', 12.5, 20.0, 11.0, 3.2, 100000, 200000, 300000, 400000, 1000000], [2020, '11', 'ILE-DE-FRANCE', '75', '75056', 12.7, 20.4, 11.1, 3.4, 110000, 210000, 310000, 410000, 1040000], [2022, '32', 'LES-HAUTS-DE-FRANCE', '59', '59350', 22.1, 31.5, 17.3, 7.8, 120000, 220000, 320000, 420000, 1080000], // Deuxieme commune de la meme region/annee pour verifier l'agregation SQL. [2022, '32', 'LES-HAUTS-DE-FRANCE', '62', '62041', 24.4, 0.0, 19.5, 9.2, 90000, 160000, 250000, 380000, 880000], // Region distincte pour verifier l'ordre et les aggregations de repartition. [2022, '84', 'AUVERGNE-RHONE-ALPES', '69', '69123', 18.2, 27.0, 14.2, 6.1, 150000, 520000, 410000, 300000, 1380000], ]; foreach ($rows as $row) { $connection->executeStatement( 'INSERT INTO tax_data (year, region_code, region_name, department_code, commune_code, rate_tfpnb, rate_tfpb, rate_th, rate_cfe, volume_tfpnb, volume_tfpb, volume_th, volume_cfe, collected_volume) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', $row ); } } public function testRegionEvolutionEndpointReturnsData(): void { $response = static::createClient()->request('GET', '/api/stats/region-evolution?tax=tfpb&startYear=2019&endYear=2022'); $this->assertResponseIsSuccessful(); $data = $response->toArray(false); $members = $this->extractMembers($data); $this->assertNotEmpty($members, 'Le endpoint region-evolution doit retourner des données.'); $this->assertArrayHasKey('year', $members[0]); $this->assertArrayHasKey('regionCode', $members[0]); $this->assertArrayHasKey('regionName', $members[0]); $this->assertArrayHasKey('rate', $members[0]); $first = $this->findMember($members, static fn (array $item): bool => (int) $item['year'] === 2019 && $item['regionCode'] === '11'); $this->assertNotNull($first); $this->assertSame('ILE-DE-FRANCE', $first['regionName']); $this->assertEquals(20.0, (float) $first['rate']); } public function testDepartmentCorrelationEndpointReturnsData(): void { $response = static::createClient()->request('GET', '/api/stats/department-correlation?tax=tfpb&year=2022&department=59'); $this->assertResponseIsSuccessful(); $data = $response->toArray(false); $members = $this->extractMembers($data); $this->assertNotEmpty($members, 'Le endpoint department-correlation doit retourner des données.'); $this->assertArrayHasKey('communeCode', $members[0]); $this->assertArrayHasKey('rate', $members[0]); $this->assertArrayHasKey('collectedVolume', $members[0]); $this->assertSame('59350', $members[0]['communeCode']); $this->assertEquals(220000.0, (float) $members[0]['collectedVolume']); } public function testDepartmentCorrelationReturnsTaxSpecificVolume(): void { $responseTfpb = static::createClient()->request('GET', '/api/stats/department-correlation?tax=tfpb&year=2022&department=59'); $responseCfe = static::createClient()->request('GET', '/api/stats/department-correlation?tax=cfe&year=2022&department=59'); $this->assertResponseIsSuccessful(); $membersTfpb = $this->extractMembers($responseTfpb->toArray(false)); $membersCfe = $this->extractMembers($responseCfe->toArray(false)); $this->assertNotEmpty($membersTfpb); $this->assertNotEmpty($membersCfe); $this->assertSame('59350', $membersTfpb[0]['communeCode']); $this->assertSame('59350', $membersCfe[0]['communeCode']); $this->assertEquals(220000.0, (float) $membersTfpb[0]['collectedVolume']); $this->assertEquals(420000.0, (float) $membersCfe[0]['collectedVolume']); } public function testRegionalDistributionEndpointReturnsData(): void { $response = static::createClient()->request('GET', '/api/stats/regional-distribution?year=2022'); $this->assertResponseIsSuccessful(); $data = $response->toArray(false); $members = $this->extractMembers($data); $this->assertNotEmpty($members, 'Le endpoint regional-distribution doit retourner des données.'); $this->assertArrayHasKey('regionCode', $members[0]); $this->assertArrayHasKey('regionName', $members[0]); $this->assertArrayHasKey('collectedVolume', $members[0]); // En default tax=tfpb, la region 84 est devant avec 520000. $this->assertSame('84', $members[0]['regionCode']); $this->assertEquals(520000.0, (float) $members[0]['collectedVolume']); } public function testDepartmentCorrelationReturnsEmptyForUnknownDepartment(): void { $response = static::createClient()->request('GET', '/api/stats/department-correlation?tax=tfpb&year=2022&department=00'); $this->assertResponseIsSuccessful(); $data = $response->toArray(false); $members = $this->extractMembers($data); $this->assertSame([], $members); } public function testRegionEvolutionFallsBackWhenTaxIsInvalid(): void { $response = static::createClient()->request('GET', '/api/stats/region-evolution?tax=invalid&startYear=2019&endYear=2022'); $this->assertResponseIsSuccessful(); $data = $response->toArray(false); $members = $this->extractMembers($data); $this->assertNotEmpty($members); $member = $this->findMember($members, static fn (array $item): bool => (int) $item['year'] === 2019 && $item['regionCode'] === '11'); $this->assertNotNull($member); } public function testRegionEvolutionReturnsEmptyWhenStartYearIsGreaterThanEndYear(): void { $response = static::createClient()->request('GET', '/api/stats/region-evolution?tax=tfpb&startYear=2022&endYear=2019'); $this->assertResponseIsSuccessful(); $data = $response->toArray(false); $members = $this->extractMembers($data); $this->assertSame([], $members); } public function testRegionEvolutionUsesDefaultParameters(): void { $response = static::createClient()->request('GET', '/api/stats/region-evolution'); $this->assertResponseIsSuccessful(); $data = $response->toArray(false); $members = $this->extractMembers($data); $this->assertNotEmpty($members); // Par defaut: tax=tfpb, startYear=2019, endYear=2022. $first = $this->findMember($members, static fn (array $item): bool => (int) $item['year'] === 2019 && $item['regionCode'] === '11'); $this->assertNotNull($first); $this->assertEquals(20.0, (float) $first['rate']); $years = array_values(array_unique(array_map( static fn (array $item): int => (int) $item['year'], $members ))); sort($years); $this->assertSame([2019, 2020, 2022], $years); } public function testRegionEvolutionIgnoresZeroRatesInAverage(): void { $response = static::createClient()->request('GET', '/api/stats/region-evolution?tax=tfpb&startYear=2022&endYear=2022'); $this->assertResponseIsSuccessful(); $members = $this->extractMembers($response->toArray(false)); $target = $this->findMember($members, static fn (array $item): bool => $item['regionCode'] === '32'); // Region 32 a deux lignes en 2022: 31.5 et 0.0, la requete doit ignorer 0. $this->assertNotNull($target); $this->assertEquals(31.5, (float) $target['rate']); } public function testDepartmentCorrelationFallsBackWhenTaxIsInvalid(): void { $fallbackResponse = static::createClient()->request('GET', '/api/stats/department-correlation?tax=invalid&year=2022&department=59'); $expectedResponse = static::createClient()->request('GET', '/api/stats/department-correlation?tax=tfpb&year=2022&department=59'); $this->assertSame(200, $fallbackResponse->getStatusCode()); $this->assertSame(200, $expectedResponse->getStatusCode()); $fallbackMembers = $this->extractMembers($fallbackResponse->toArray(false)); $expectedMembers = $this->extractMembers($expectedResponse->toArray(false)); $this->assertSame($expectedMembers, $fallbackMembers); } public function testDepartmentCorrelationReturnsEmptyWhenDepartmentIsMissing(): void { $response = static::createClient()->request('GET', '/api/stats/department-correlation?tax=tfpb&year=2022'); $this->assertResponseIsSuccessful(); $members = $this->extractMembers($response->toArray(false)); $this->assertSame([], $members); } public function testDepartmentCorrelationUsesDefaultParametersWhenMissing(): void { $response = static::createClient()->request('GET', '/api/stats/department-correlation'); $this->assertResponseIsSuccessful(); $members = $this->extractMembers($response->toArray(false)); // Default provider: tax=tfpb, year=2022, department=73 (absent du jeu minimal). $this->assertSame([], $members); } public function testRegionalDistributionReturnsDifferentVolumesDependingOnTax(): void { $responseTfpb = static::createClient()->request('GET', '/api/stats/regional-distribution?tax=tfpb&year=2022'); $responseCfe = static::createClient()->request('GET', '/api/stats/regional-distribution?tax=cfe&year=2022'); $this->assertSame(200, $responseTfpb->getStatusCode()); $this->assertSame(200, $responseCfe->getStatusCode()); $tfpbMembers = $this->extractMembers($responseTfpb->toArray(false)); $cfeMembers = $this->extractMembers($responseCfe->toArray(false)); $this->assertNotEmpty($tfpbMembers); $this->assertNotEmpty($cfeMembers); $targetTfpb = $this->findMember($tfpbMembers, static fn (array $item): bool => $item['regionCode'] === '32'); $targetCfe = $this->findMember($cfeMembers, static fn (array $item): bool => $item['regionCode'] === '32'); $this->assertNotNull($targetTfpb); $this->assertNotNull($targetCfe); $this->assertEquals(380000.0, (float) $targetTfpb['collectedVolume']); $this->assertEquals(800000.0, (float) $targetCfe['collectedVolume']); $this->assertNotSame((float) $targetTfpb['collectedVolume'], (float) $targetCfe['collectedVolume']); } public function testRegionalDistributionFallsBackWhenTaxIsInvalid(): void { $fallbackResponse = static::createClient()->request('GET', '/api/stats/regional-distribution?tax=invalid&year=2022'); $expectedResponse = static::createClient()->request('GET', '/api/stats/regional-distribution?tax=tfpb&year=2022'); $this->assertSame(200, $fallbackResponse->getStatusCode()); $this->assertSame(200, $expectedResponse->getStatusCode()); $fallbackMembers = $this->extractMembers($fallbackResponse->toArray(false)); $expectedMembers = $this->extractMembers($expectedResponse->toArray(false)); $this->assertSame($expectedMembers, $fallbackMembers); } public function testRegionalDistributionReturnsEmptyForUnknownYear(): void { $response = static::createClient()->request('GET', '/api/stats/regional-distribution?tax=tfpb&year=1990'); $this->assertResponseIsSuccessful(); $members = $this->extractMembers($response->toArray(false)); $this->assertSame([], $members); } public function testRegionalDistributionUsesDefaultParameters(): void { $response = static::createClient()->request('GET', '/api/stats/regional-distribution'); $this->assertResponseIsSuccessful(); $members = $this->extractMembers($response->toArray(false)); // Default year=2019, tax=tfpb sur le jeu minimal => seulement region 11. $this->assertCount(1, $members); $this->assertSame('11', $members[0]['regionCode']); $this->assertEquals(200000.0, (float) $members[0]['collectedVolume']); } public function testRegionalDistributionIsSortedDescendingByCollectedVolume(): void { $response = static::createClient()->request('GET', '/api/stats/regional-distribution?tax=tfpb&year=2022'); $this->assertResponseIsSuccessful(); $members = $this->extractMembers($response->toArray(false)); $this->assertGreaterThanOrEqual(2, count($members)); $this->assertGreaterThanOrEqual((float) $members[1]['collectedVolume'], (float) $members[0]['collectedVolume']); } public function testRegionEvolutionSupportsDifferentTaxes(): void { $responseTh = static::createClient()->request('GET', '/api/stats/region-evolution?tax=th&startYear=2022&endYear=2022'); $responseCfe = static::createClient()->request('GET', '/api/stats/region-evolution?tax=cfe&startYear=2022&endYear=2022'); $this->assertSame(200, $responseTh->getStatusCode()); $this->assertSame(200, $responseCfe->getStatusCode()); $thMembers = $this->extractMembers($responseTh->toArray(false)); $cfeMembers = $this->extractMembers($responseCfe->toArray(false)); $this->assertNotEmpty($thMembers); $this->assertNotEmpty($cfeMembers); $thRegion32 = $this->findMember($thMembers, static fn (array $item): bool => $item['regionCode'] === '32'); $cfeRegion32 = $this->findMember($cfeMembers, static fn (array $item): bool => $item['regionCode'] === '32'); $this->assertNotNull($thRegion32); $this->assertNotNull($cfeRegion32); $this->assertNotSame((float) $thRegion32['rate'], (float) $cfeRegion32['rate']); } /** * API Platform peut renvoyer "member" ou "hydra:member" selon la config. * * @param array $data * @return array> */ private function extractMembers(array $data): array { if (isset($data['member']) && is_array($data['member'])) { return $data['member']; } if (isset($data['hydra:member']) && is_array($data['hydra:member'])) { return $data['hydra:member']; } return []; } /** * @param array> $members * @param callable(array): bool $predicate * @return array|null */ private function findMember(array $members, callable $predicate): ?array { foreach ($members as $member) { if ($predicate($member)) { return $member; } } return null; } }