getProperty('cachedDhlConfig'); $property->setAccessible(true); $property->setValue(null, [ 'api_key' => 'cached-test-api-key-7777', 'use_queue' => false, ]); DhlTrackingService::clearQuotaPause(); DhlTrackingService::setCallIntervalSeconds(0); }); afterEach(function () { SettingController::flushDhlConfigCache(); DhlTrackingService::clearQuotaPause(); DhlTrackingService::setCallIntervalSeconds(5); Mockery::close(); }); /** * @return DhlShipment&\Mockery\MockInterface */ function makeTrackableShipment(int $id, string $trackingNumber) { /** @var DhlShipment&\Mockery\MockInterface $shipment */ $shipment = Mockery::mock(DhlShipment::class)->makePartial(); $shipment->id = $id; $shipment->dhl_shipment_no = $trackingNumber; $shipment->status = 'in_transit'; return $shipment; } it('fires exactly one DHL request per shipment - no pseudo-batch comma list anymore', function () { Http::fake([ 'api-eu.dhl.com/track/shipments*' => Http::response([ 'shipments' => [ [ 'id' => 'A1', 'status' => [ 'statusCode' => 'transit', 'status' => 'Sendung in Zustellung', 'timestamp' => '2026-05-27T12:00:00+02:00', ], 'events' => [], ], ], ], 200), ]); $shipments = new Collection([ makeTrackableShipment(1, 'A1'), makeTrackableShipment(2, 'B2'), makeTrackableShipment(3, 'C3'), ]); foreach ($shipments as $shipment) { $shipment->shouldReceive('update')->andReturnTrue(); } $service = new DhlTrackingService; $stats = $service->updateTrackingBatch($shipments); expect($stats['updated'])->toBe(3); expect($stats['failed'])->toBe(0); // 3 shipments -> exactly 3 HTTP calls, each with a single trackingNumber. Http::assertSentCount(3); Http::assertSent(function ($request) { $value = $request->data()['trackingNumber'] ?? null; return $value !== null && ! str_contains((string) $value, ','); }); }); it('sleeps between calls when the throttle is enabled', function () { DhlTrackingService::setCallIntervalSeconds(1); Http::fake([ 'api-eu.dhl.com/track/shipments*' => Http::response([ 'shipments' => [ [ 'id' => 'A1', 'status' => ['statusCode' => 'transit', 'status' => 'In Zustellung', 'timestamp' => '2026-05-27T12:00:00+02:00'], 'events' => [], ], ], ], 200), ]); $shipments = new Collection([ makeTrackableShipment(1, 'A1'), makeTrackableShipment(2, 'A1'), makeTrackableShipment(3, 'A1'), ]); foreach ($shipments as $shipment) { $shipment->shouldReceive('update')->andReturnTrue(); } $service = new DhlTrackingService; $start = microtime(true); $stats = $service->updateTrackingBatch($shipments); $elapsed = microtime(true) - $start; expect($stats['updated'])->toBe(3); // 3 calls with a 1s gap between calls -> >=2s total wall-clock time. expect($elapsed)->toBeGreaterThanOrEqual(2.0); }); it('respects setCallIntervalSeconds(0) for the test suite', function () { expect(DhlTrackingService::getCallIntervalSeconds())->toBe(0); DhlTrackingService::setCallIntervalSeconds(5); expect(DhlTrackingService::getCallIntervalSeconds())->toBe(5); DhlTrackingService::setCallIntervalSeconds(-99); expect(DhlTrackingService::getCallIntervalSeconds())->toBe(0); }); it('does not have a trackMultipleShipments method anymore (removed in Phase 13)', function () { expect(method_exists(DhlTrackingService::class, 'trackMultipleShipments'))->toBeFalse(); });