service = new DriftSimulationService( $this->createStub(OpenMeteoClient::class), $this->createStub(OceanCurrentClient::class), $this->createStub(Connection::class), $this->createStub(EntityManagerInterface::class), new NullLogger(), ); } // ── computeConfidence ───────────────────────────────────────────────────── #[\PHPUnit\Framework\Attributes\DataProvider('confidenceProvider')] public function testComputeConfidence(int $horizon, float $expected): void { $method = new \ReflectionMethod(DriftSimulationService::class, 'computeConfidence'); $result = $method->invoke($this->service, $horizon); self::assertSame($expected, $result); } /** @return array */ public static function confidenceProvider(): array { return [ 'H+6 → 0.85' => [6, 0.85], 'H+12 → 0.75' => [12, 0.75], 'H+24 → 0.60' => [24, 0.60], 'H+48 → 0.45' => [48, 0.45], 'inconnu → 0.50' => [72, 0.50], ]; } // ── avgDriftVector ──────────────────────────────────────────────────────── public function testAvgDriftVectorWithNoWindNoOcean(): void { $method = new \ReflectionMethod(DriftSimulationService::class, 'avgDriftVector'); $result = $method->invoke($this->service, [], 0, 6, 0.0, 0.0); self::assertSame(0.0, $result['lat']); self::assertSame(0.0, $result['lng']); } public function testAvgDriftVectorWithPureEastwardOceanCurrent(): void { // Courant purement Est (u=1.0 m/s), vent nul, 6 heures $method = new \ReflectionMethod(DriftSimulationService::class, 'avgDriftVector'); $result = $method->invoke($this->service, [], 0, 6, 1.0, 0.0); self::assertSame(1.0, $result['lng']); // Est-Ouest = 1 m/s self::assertSame(0.0, $result['lat']); // Nord-Sud = 0 } public function testAvgDriftVectorWithNorthwardOceanCurrent(): void { $method = new \ReflectionMethod(DriftSimulationService::class, 'avgDriftVector'); $result = $method->invoke($this->service, [], 0, 6, 0.0, 1.0); self::assertSame(0.0, $result['lng']); self::assertSame(1.0, $result['lat']); } public function testAvgDriftVectorWithNorthWind(): void { // Vent venant du Nord (direction=0°), vitesse=10 m/s, courant nul // sin(0°)=0 (pas de composante E-O), cos(0°)=1 (composante N-S) // Stokes = 3% → 10 * 0.03 * cos(0) = 0.3 m/s vers Nord $windData = array_fill(0, 6, ['speed' => 10.0, 'direction' => 0.0]); $method = new \ReflectionMethod(DriftSimulationService::class, 'avgDriftVector'); $result = $method->invoke($this->service, $windData, 0, 6, 0.0, 0.0); self::assertEqualsWithDelta(0.0, $result['lng'], 1e-10); self::assertEqualsWithDelta(0.3, $result['lat'], 1e-10); } public function testAvgDriftVectorWithEastWind(): void { // Vent venant de l'Est (direction=90°), vitesse=10 m/s // sin(90°)=1 → composante Est = 10*0.03 = 0.3 m/s // cos(90°)=0 → composante Nord = 0 $windData = array_fill(0, 6, ['speed' => 10.0, 'direction' => 90.0]); $method = new \ReflectionMethod(DriftSimulationService::class, 'avgDriftVector'); $result = $method->invoke($this->service, $windData, 0, 6, 0.0, 0.0); self::assertEqualsWithDelta(0.3, $result['lng'], 1e-10); self::assertEqualsWithDelta(0.0, $result['lat'], 1e-10); } public function testAvgDriftVectorUsesOnlyRequestedHourRange(): void { // windData[0..5] = vent Nord 10 m/s, windData[6..11] = vent Est 10 m/s // Si on demande fromHour=6, toHour=12 → seules les heures 6..11 comptent $windData = array_merge( array_fill(0, 6, ['speed' => 10.0, 'direction' => 0.0]), // heures 0-5 : Nord array_fill(6, 6, ['speed' => 10.0, 'direction' => 90.0]), // heures 6-11 : Est ); $method = new \ReflectionMethod(DriftSimulationService::class, 'avgDriftVector'); $result = $method->invoke($this->service, $windData, 6, 12, 0.0, 0.0); self::assertEqualsWithDelta(0.3, $result['lng'], 1e-10); // Est self::assertEqualsWithDelta(0.0, $result['lat'], 1e-10); } }