27-05-2026 DHL Modul v2.1 / Optimierung tracking
This commit is contained in:
parent
036595be94
commit
2bdc9ada3c
33 changed files with 2367 additions and 2086 deletions
119
tests/Unit/Dhl/DhlTrackingStaleProtectionTest.php
Normal file
119
tests/Unit/Dhl/DhlTrackingStaleProtectionTest.php
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
use Acme\Dhl\Models\DhlShipment;
|
||||
use App\Http\Controllers\SettingController;
|
||||
use App\Services\DhlTrackingService;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
beforeEach(function () {
|
||||
$reflection = new ReflectionClass(SettingController::class);
|
||||
$property = $reflection->getProperty('cachedDhlConfig');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue(null, [
|
||||
'api_key' => 'cached-test-api-key-1234',
|
||||
'use_queue' => false,
|
||||
]);
|
||||
|
||||
DhlTrackingService::clearQuotaPause();
|
||||
DhlTrackingService::setCallIntervalSeconds(0);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
SettingController::flushDhlConfigCache();
|
||||
DhlTrackingService::clearQuotaPause();
|
||||
DhlTrackingService::setCallIntervalSeconds(5);
|
||||
Mockery::close();
|
||||
});
|
||||
|
||||
/**
|
||||
* @return DhlShipment&\Mockery\MockInterface
|
||||
*/
|
||||
function makeFakeShipment(int $id, string $trackingNumber, ?Carbon\Carbon $lastTrackedAt = null)
|
||||
{
|
||||
/** @var DhlShipment&\Mockery\MockInterface $shipment */
|
||||
$shipment = Mockery::mock(DhlShipment::class)->makePartial();
|
||||
$shipment->id = $id;
|
||||
$shipment->dhl_shipment_no = $trackingNumber;
|
||||
$shipment->last_tracked_at = $lastTrackedAt;
|
||||
|
||||
return $shipment;
|
||||
}
|
||||
|
||||
it('does not update last_tracked_at when the Unified API returns 401', function () {
|
||||
Http::fake([
|
||||
'api-eu.dhl.com/track/shipments*' => Http::response([], 401),
|
||||
]);
|
||||
|
||||
$shipment = makeFakeShipment(123, '00340434292135100148', now()->subDays(2));
|
||||
|
||||
// The sync path must never call ->update() on the shipment when an auth
|
||||
// error occurs. Asserting "never called" is the strongest contract we
|
||||
// can express here without a database.
|
||||
$shipment->shouldNotReceive('update');
|
||||
|
||||
$service = new DhlTrackingService;
|
||||
$result = $service->updateTracking($shipment, ['auto_retrack' => false]);
|
||||
|
||||
expect($result)
|
||||
->toHaveKey('success', false)
|
||||
->toHaveKey('auth_error', true)
|
||||
->toHaveKey('http_status', 401);
|
||||
});
|
||||
|
||||
it('does update last_tracked_at when DHL says the shipment is not found', function () {
|
||||
Http::fake([
|
||||
'api-eu.dhl.com/track/shipments*' => Http::response(['shipments' => []], 200),
|
||||
]);
|
||||
|
||||
$shipment = makeFakeShipment(123, '00340434292135100148');
|
||||
|
||||
$shipment->shouldReceive('update')
|
||||
->once()
|
||||
->withArgs(function (array $payload) {
|
||||
return array_keys($payload) === ['last_tracked_at']
|
||||
&& ! array_key_exists('tracking_status', $payload)
|
||||
&& ! array_key_exists('status', $payload);
|
||||
})
|
||||
->andReturnTrue();
|
||||
|
||||
$service = new DhlTrackingService;
|
||||
$result = $service->updateTracking($shipment, ['auto_retrack' => false]);
|
||||
|
||||
expect($result)
|
||||
->toHaveKey('success', false)
|
||||
->toHaveKey('auth_error', false);
|
||||
});
|
||||
|
||||
it('stops the batch tracker on the first auth error to avoid burning the API quota', function () {
|
||||
Http::fake([
|
||||
'api-eu.dhl.com/track/shipments*' => Http::response([], 401),
|
||||
]);
|
||||
|
||||
$shipments = new Collection([
|
||||
makeFakeShipment(1, 'A1'),
|
||||
makeFakeShipment(2, 'B2'),
|
||||
makeFakeShipment(3, 'C3'),
|
||||
]);
|
||||
|
||||
foreach ($shipments as $shipment) {
|
||||
$shipment->shouldNotReceive('update');
|
||||
}
|
||||
|
||||
$service = new DhlTrackingService;
|
||||
$stats = $service->updateTrackingBatch($shipments);
|
||||
|
||||
// Since Phase 13 the batch tracker fires one HTTP call per shipment and
|
||||
// aborts on the *first* auth error. So exactly one shipment is marked
|
||||
// failed (the one we tried), the other two are skipped entirely.
|
||||
expect($stats)
|
||||
->toHaveKey('updated', 0)
|
||||
->toHaveKey('failed', 1);
|
||||
|
||||
expect($stats['results'])->toHaveCount(1);
|
||||
expect($stats['results'][0])
|
||||
->toHaveKey('success', false)
|
||||
->toHaveKey('auth_error', true);
|
||||
|
||||
Http::assertSentCount(1);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue