123456, 'weight_kg' => 0.5, 'product_code' => $productCode, 'label_format' => 'PDF', 'shipper' => [ 'name' => 'mivita care gmbh', 'street' => 'Leinfeld', 'houseNumber' => '2', 'postalCode' => '87755', 'city' => 'Kirchhaslach', 'country' => 'DE', ], 'consignee' => [ 'name' => 'Max Mustermann', 'street' => 'Hauptstrasse', 'houseNumber' => '5', 'postalCode' => '10115', 'city' => 'Berlin', 'country' => $countryCode, ], 'reference' => 'ORDER-123456', ]; } it('accepts DHL Kleinpaket as a product code', function () { $service = makeDhlPhaseOneShippingService(); $method = new ReflectionMethod($service, 'validateOrderData'); $method->setAccessible(true); $validated = $method->invoke($service, validDhlPhaseOneOrderData('V62KP')); expect($validated['product_code'])->toBe('V62KP'); }); it('builds an international DHL parcel payload for Austria', function () { config([ 'dhl.account_numbers.V53PAK' => '63144073555301', 'dhl.legacy.sandbox' => false, 'dhl.legacy.test_mode' => false, ]); $service = makeDhlPhaseOneShippingService(); $method = new ReflectionMethod($service, 'buildShipmentPayload'); $method->setAccessible(true); $payload = $method->invoke($service, validDhlPhaseOneOrderData('V53PAK', 'AT')); expect($payload['shipments'][0]['product'])->toBe('V53PAK') ->and($payload['shipments'][0]['billingNumber'])->toBe('63144073555301') ->and($payload['shipments'][0]['consignee']['country'])->toBe('AUT'); }); it('rejects unsupported countries instead of falling back to Germany', function () { $service = makeDhlPhaseOneShippingService(); $method = new ReflectionMethod($service, 'buildShipmentPayload'); $method->setAccessible(true); $method->invoke($service, validDhlPhaseOneOrderData('V53PAK', 'FR')); })->throws(InvalidArgumentException::class); it('rejects legacy Warenpost for new labels', function () { $service = makeDhlPhaseOneShippingService(); $method = new ReflectionMethod($service, 'validateOrderData'); $method->setAccessible(true); $method->invoke($service, validDhlPhaseOneOrderData('V62WP')); })->throws(InvalidArgumentException::class); it('builds a DHL Kleinpaket payload', function () { config([ 'dhl.account_numbers.V62KP' => '63144073556201', 'dhl.legacy.sandbox' => false, 'dhl.legacy.test_mode' => false, ]); $service = makeDhlPhaseOneShippingService(); $method = new ReflectionMethod($service, 'buildShipmentPayload'); $method->setAccessible(true); $payload = $method->invoke($service, validDhlPhaseOneOrderData('V62KP')); expect($payload['shipments'][0]['product'])->toBe('V62KP') ->and($payload['shipments'][0]['billingNumber'])->toBe('63144073556201') ->and($payload['shipments'][0]['refNo'])->toBe('ORDER-123456'); }); it('uses mustEncode only for German consignee addresses', function () { $service = makeDhlPhaseOneShippingService(); $method = new ReflectionMethod($service, 'shouldUseMustEncode'); $method->setAccessible(true); expect($method->invoke($service, validDhlPhaseOneOrderData('V01PAK', 'DE') + ['print_only_if_codeable' => true]))->toBeTrue() ->and($method->invoke($service, validDhlPhaseOneOrderData('V53PAK', 'AT') + ['print_only_if_codeable' => true]))->toBeFalse() ->and($method->invoke($service, validDhlPhaseOneOrderData('V01PAK', 'DE') + ['print_only_if_codeable' => false]))->toBeFalse(); }); it('turns non-codeable DHL responses into address validation errors', function () { $service = makeDhlPhaseOneShippingService(); $method = new ReflectionMethod($service, 'assertSuccessfulShipmentResponse'); $method->setAccessible(true); $method->invoke($service, [ 'status' => [ 'statusCode' => 200, ], 'items' => [[ 'sstatus' => [ 'statusCode' => 400, 'detail' => 'Consignee address is not encodable.', ], 'validationMessages' => [[ 'validationMessage' => 'Address is not encodable.', ]], ]], ], true); })->throws(DhlAddressValidationException::class, 'DHL kann diese Adresse nicht leitcodieren.');