diff --git a/.github/workflows/pint.yml b/.github/workflows/pint.yml new file mode 100644 index 0000000..b5498b2 --- /dev/null +++ b/.github/workflows/pint.yml @@ -0,0 +1,43 @@ +name: Laravel Pint + +on: + pull_request: + types: [ opened, synchronize, reopened ] + +jobs: + pint: + runs-on: ubuntu-latest + + steps: + - name: Check out repository code + uses: actions/checkout@v6 + with: + fetch-depth: 0 + ref: ${{ github.head_ref }} + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + + - name: Install Dependencies + run: composer install --prefer-dist --no-interaction --no-scripts --no-progress + + - name: Run Pint + run: ./vendor/bin/pint + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v8 + id: cpr + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: 'chore(linter): Laravel pint has beautified your code' + title: Pint + branch: pint-fixes-${{ github.head_ref }} + base: ${{ github.head_ref }} + delete-branch: true + branch-suffix: short-commit-hash + + - name: Exit if ECS made Changes + if: (steps.cpr.outputs.pull-request-operation == 'created') || (steps.cpr.outputs.pull-request-operation == 'updated') + run: exit 1 diff --git a/composer.json b/composer.json index e383c14..b730e8d 100644 --- a/composer.json +++ b/composer.json @@ -47,6 +47,7 @@ }, "require-dev": { "larastan/larastan": "^3.0", + "laravel/pint": "^1.0", "orchestra/testbench": "^10.0|^11.0", "pestphp/pest": "^3.0|^4.0", "pestphp/pest-plugin-laravel": "^3.0|^4.0" @@ -58,6 +59,8 @@ }, "scripts": { "test": "pest", - "stan": "phpstan analyse" + "stan": "phpstan analyse", + "lint": "pint", + "lint:test": "pint --test" } } diff --git a/config/addressable.php b/config/addressable.php index 2f00d04..e66f96a 100644 --- a/config/addressable.php +++ b/config/addressable.php @@ -1,7 +1,7 @@ $options * - * @throws \Byte5\Addressable\App\Exceptions\AddressLookupException When the provider is misconfigured (e.g. missing API key), cannot be reached, or responds with an error status. + * @throws AddressLookupException When the provider is misconfigured (e.g. missing API key), cannot be reached, or responds with an error status. */ public function validate(AddressInput $address, array $options = []): AddressValidation; } diff --git a/src/App/Data/AddressData.php b/src/App/Data/AddressData.php index 737a06d..fb01abf 100644 --- a/src/App/Data/AddressData.php +++ b/src/App/Data/AddressData.php @@ -14,15 +14,15 @@ public function __construct( public ?string $postal = null, public ?string $city = null, public ?string $region = null, - public ?float $latitude = null, - public ?float $longitude = null, + public ?float $latitude = null, + public ?float $longitude = null, public ?string $country = null, ) {} /** * Build from a loose attribute array (unknown keys ignored). * - * @param array $attributes + * @param array $attributes */ public static function fromArray(array $attributes): self { diff --git a/src/App/Data/AddressValidation.php b/src/App/Data/AddressValidation.php index d31b7b9..15b1222 100644 --- a/src/App/Data/AddressValidation.php +++ b/src/App/Data/AddressValidation.php @@ -11,7 +11,7 @@ * @param string $provider The driver that produced this result, e.g. `google`. * @param string|null $formattedAddress The provider's corrected/standardised address. * @param array $raw The provider's full response payload. Provider-specific fields - * (e.g. Google's `verdict`/`geocode`) live here. + * (e.g. Google's `verdict`/`geocode`) live here. */ public function __construct( public bool $valid, diff --git a/src/App/Enums/AddressType.php b/src/App/Enums/AddressType.php index 2d2b3f1..1b4a324 100644 --- a/src/App/Enums/AddressType.php +++ b/src/App/Enums/AddressType.php @@ -4,8 +4,8 @@ enum AddressType: string { - case Billing = 'billing'; + case Billing = 'billing'; case Shipping = 'shipping'; - case Home = 'home'; - case Work = 'work'; + case Home = 'home'; + case Work = 'work'; } diff --git a/src/App/Facades/AddressRules.php b/src/App/Facades/AddressRules.php index f007916..7ea7bd6 100644 --- a/src/App/Facades/AddressRules.php +++ b/src/App/Facades/AddressRules.php @@ -11,7 +11,7 @@ * @method static PostalFormat postalFormat(?string $country) * @method static Country country() * - * @see \Byte5\Addressable\App\Contracts\AddressRules + * @see AddressRulesContract */ class AddressRules extends Facade { diff --git a/src/App/Facades/Countries.php b/src/App/Facades/Countries.php index f7df15d..37781e7 100644 --- a/src/App/Facades/Countries.php +++ b/src/App/Facades/Countries.php @@ -2,8 +2,8 @@ namespace Byte5\Addressable\App\Facades; -use Illuminate\Support\Facades\Facade; use Byte5\Addressable\App\Contracts\Countries as CountryContract; +use Illuminate\Support\Facades\Facade; /** * @method static array list(?string $locale = null) diff --git a/src/App/Models/Address.php b/src/App/Models/Address.php index 793a555..a11c656 100644 --- a/src/App/Models/Address.php +++ b/src/App/Models/Address.php @@ -2,9 +2,9 @@ namespace Byte5\Addressable\App\Models; -use Byte5\Addressable\Database\Factories\AddressFactory; use Byte5\Addressable\App\Data\PostalAddress; use Byte5\Addressable\App\Support\Config; +use Byte5\Addressable\Database\Factories\AddressFactory; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -53,7 +53,7 @@ protected static function newFactory(): AddressFactory protected function country(): Attribute { return Attribute::make( - set: fn(?string $value) => $value === null ? null : strtoupper($value), + set: fn (?string $value) => $value === null ? null : strtoupper($value), ); } diff --git a/src/App/Rules/Country.php b/src/App/Rules/Country.php index da88c11..e83eeea 100644 --- a/src/App/Rules/Country.php +++ b/src/App/Rules/Country.php @@ -29,6 +29,6 @@ public function validate(string $attribute, mixed $value, Closure $fail): void private static function iso3166(): ISO3166 { - return self::$iso3166 ??= new ISO3166(); + return self::$iso3166 ??= new ISO3166; } } diff --git a/src/App/Rules/PostalFormat.php b/src/App/Rules/PostalFormat.php index a7d1ed7..5a31813 100644 --- a/src/App/Rules/PostalFormat.php +++ b/src/App/Rules/PostalFormat.php @@ -29,13 +29,13 @@ public function validate(string $attribute, mixed $value, Closure $fail): void // Mirror commerceguys/addressing: the pattern must match the value completely. preg_match('/'.$pattern.'/i', $value, $matches); - if (!isset($matches[0]) || $matches[0] !== $value) { + if (! isset($matches[0]) || $matches[0] !== $value) { $fail('byte5-addressable::validation.postal_format')->translate(['attribute' => $attribute]); } } private static function repository(): AddressFormatRepository { - return self::$repository ??= new AddressFormatRepository(); + return self::$repository ??= new AddressFormatRepository; } } diff --git a/src/App/Services/AddressRules.php b/src/App/Services/AddressRules.php index 1bc17d1..3d0a8d5 100644 --- a/src/App/Services/AddressRules.php +++ b/src/App/Services/AddressRules.php @@ -16,7 +16,7 @@ public function postalFormat(?string $country): PostalFormat public function country(): Country { - return new Country(); + return new Country; } /** diff --git a/src/App/Services/Countries.php b/src/App/Services/Countries.php index 3848e8c..06d61ae 100644 --- a/src/App/Services/Countries.php +++ b/src/App/Services/Countries.php @@ -28,7 +28,7 @@ public function list(?string $locale = null): array // Iterate the commerceguys list to keep its locale-specific ordering. return array_filter( $names, - fn(string $code): bool => isset(self::isoCodes()[$code]), + fn (string $code): bool => isset(self::isoCodes()[$code]), ARRAY_FILTER_USE_KEY, ); } @@ -51,11 +51,11 @@ private static function isoCodes(): array private static function repository(): CountryRepository { - return self::$repository ??= new CountryRepository(); + return self::$repository ??= new CountryRepository; } private static function iso3166(): ISO3166 { - return self::$iso3166 ??= new ISO3166(); + return self::$iso3166 ??= new ISO3166; } } diff --git a/src/lang/en/validation.php b/src/lang/en/validation.php index f53722c..10bae7b 100644 --- a/src/lang/en/validation.php +++ b/src/lang/en/validation.php @@ -4,4 +4,4 @@ 'country' => 'The :attribute is not a valid country.', 'postal_format' => 'The :attribute is not a valid postal code for the selected country.', 'address_exists' => 'The :attribute could not be verified as a deliverable address.', -]; \ No newline at end of file +]; diff --git a/tests/Feature/AddressCreationTest.php b/tests/Feature/AddressCreationTest.php index 667180c..1bab9eb 100644 --- a/tests/Feature/AddressCreationTest.php +++ b/tests/Feature/AddressCreationTest.php @@ -36,7 +36,7 @@ $address = $model->addAddress([ 'street' => 'Unter den Linden 1', - 'city' => 'Berlin', + 'city' => 'Berlin', 'postal' => '10117', 'country' => 'DE', ]); @@ -84,10 +84,11 @@ $model = TestModel::create(['name' => 'Acme']); $sentinel = $model->addresses()->create(['city' => 'Sentinel']); - $spy = (object) ['called' => false]; + $spy = (object) ['called' => false]; app()->bind(CreatesAddresses::class, function () use ($sentinel, $spy): CreatesAddresses { - return new class ($sentinel, $spy) implements CreatesAddresses { + return new class($sentinel, $spy) implements CreatesAddresses + { public function __construct( private readonly Address $sentinel, private readonly object $spy, diff --git a/tests/Feature/AddressLookupPersistenceTest.php b/tests/Feature/AddressLookupPersistenceTest.php index 6c895fc..ef58ca7 100644 --- a/tests/Feature/AddressLookupPersistenceTest.php +++ b/tests/Feature/AddressLookupPersistenceTest.php @@ -1,7 +1,7 @@ toBeInstanceOf(PostalFormat::class); }); -it('builds a Country rule through the facade', function() { +it('builds a Country rule through the facade', function () { expect(AddressRules::country())->toBeInstanceOf(Country::class); }); -it('the facade-built rule validates the same as a new instance', function() { - $collect = function($rule) { +it('the facade-built rule validates the same as a new instance', function () { + $collect = function ($rule) { $failures = []; - $rule->validate('postal', '123', function(string $message) use (&$failures) { + $rule->validate('postal', '123', function (string $message) use (&$failures) { $failures[] = $string = new PotentiallyTranslatedString($message, app('translator')); return $string; diff --git a/tests/Feature/AddressableTest.php b/tests/Feature/AddressableTest.php index 1ecf995..91b871f 100644 --- a/tests/Feature/AddressableTest.php +++ b/tests/Feature/AddressableTest.php @@ -1,14 +1,14 @@ 'Acme']); $model->addresses()->create([ @@ -21,7 +21,7 @@ ->and($model->addresses->first()->city)->toBe('Berlin'); }); -it('exposes a single address via the morphOne relation', function() { +it('exposes a single address via the morphOne relation', function () { $model = TestModel::create(['name' => 'Acme']); $model->addresses()->create(['city' => 'Berlin', 'type' => 'billing']); @@ -31,7 +31,7 @@ ->and($model->latestAddress->is($latest))->toBeTrue(); }); -it('resolves the owning model through the addressable morphTo relation', function() { +it('resolves the owning model through the addressable morphTo relation', function () { $model = TestModel::create(['name' => 'Acme']); $address = $model->addresses()->create(['city' => 'Berlin']); @@ -39,7 +39,7 @@ ->and($address->addressable->is($model))->toBeTrue(); }); -it('casts latitude and longitude to 8-decimal precision', function() { +it('casts latitude and longitude to 8-decimal precision', function () { $address = Address::factory()->create([ 'latitude' => 52.52, 'longitude' => 13.405, @@ -49,11 +49,11 @@ ->and($address->fresh()->longitude)->toBe('13.40500000'); }); -it('uses a big integer addressable key by default', function() { +it('uses a big integer addressable key by default', function () { expect(Schema::getColumnType('addresses', 'addressable_id'))->toBe('integer'); }); -it('uses a configurable table name', function() { +it('uses a configurable table name', function () { config()->set('byte5-addressable.table_names.addresses', 'postal_addresses'); Schema::dropIfExists('addresses'); @@ -61,10 +61,10 @@ $migration->up(); expect(Schema::hasTable('postal_addresses'))->toBeTrue() - ->and((new Address())->getTable())->toBe('postal_addresses'); + ->and((new Address)->getTable())->toBe('postal_addresses'); }); -it('resolves the address model from config', function() { +it('resolves the address model from config', function () { config()->set('byte5-addressable.models.address', CustomAddress::class); $model = TestModel::create(['name' => 'Acme']); @@ -74,7 +74,7 @@ ->and($model->addresses->first())->toBeInstanceOf(CustomAddress::class); }); -it('honours a configurable morph key column name', function() { +it('honours a configurable morph key column name', function () { config()->set('byte5-addressable.column_names.model_morph_key', 'owner_id'); Schema::dropIfExists('addresses'); @@ -90,13 +90,13 @@ ->and($address->addressable->is($model))->toBeTrue(); }); -it('casts type to the default AddressType enum', function() { +it('casts type to the default AddressType enum', function () { $address = Address::factory()->create(['type' => 'billing']); expect($address->fresh()->type)->toBe(AddressType::Billing); }); -it('casts type to a custom enum provided via config', function() { +it('casts type to a custom enum provided via config', function () { config()->set('byte5-addressable.type_enum', CustomAddressType::class); $address = Address::factory()->create(['type' => 'home']); @@ -104,20 +104,20 @@ expect($address->fresh()->type)->toBe(CustomAddressType::Home); }); -it('populates the extension line and an address type via the factory', function() { +it('populates the extension line and an address type via the factory', function () { $address = Address::factory()->make(); expect($address->extra)->toBeString()->not->toBeEmpty() ->and($address->type)->toBeInstanceOf(AddressType::class); }); -it('normalises the country to an uppercase ISO code', function() { +it('normalises the country to an uppercase ISO code', function () { $address = Address::factory()->create(['country' => 'de']); expect($address->fresh()->country)->toBe('DE'); }); -it('keeps type as a plain string when the enum is disabled', function() { +it('keeps type as a plain string when the enum is disabled', function () { config()->set('byte5-addressable.type_enum', ''); $address = Address::factory()->create(['type' => 'anything']); @@ -125,7 +125,7 @@ expect($address->fresh()->type)->toBe('anything'); }); -it('builds a schema.org PostalAddress from the model', function() { +it('builds a schema.org PostalAddress from the model', function () { $address = new Address([ 'street' => 'Pariser Platz 1', 'extra' => 'Apt. 5', diff --git a/tests/Feature/CountriesFacadeTest.php b/tests/Feature/CountriesFacadeTest.php index ae34538..c434e72 100644 --- a/tests/Feature/CountriesFacadeTest.php +++ b/tests/Feature/CountriesFacadeTest.php @@ -2,6 +2,6 @@ use Byte5\Addressable\App\Facades\Countries; -it('exposes the country list through the facade', function() { +it('exposes the country list through the facade', function () { expect(Countries::list())->toHaveKey('DE', 'Germany'); }); diff --git a/tests/Feature/GoogleLookupDriverTest.php b/tests/Feature/GoogleLookupDriverTest.php index 9f50763..bd29a46 100644 --- a/tests/Feature/GoogleLookupDriverTest.php +++ b/tests/Feature/GoogleLookupDriverTest.php @@ -24,7 +24,7 @@ function googleDriver(array $config = []): GoogleLookupDriver 'mainText' => ['text' => 'Brandenburger Tor'], 'secondaryText' => ['text' => 'Berlin, Germany'], ], - ] + ], ], ], ]), diff --git a/tests/TestCase.php b/tests/TestCase.php index d3836d9..84c7616 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -41,7 +41,7 @@ protected function defineDatabaseMigrations(): void { (include __DIR__.'/../database/migrations/create_addresses_table.php.stub')->up(); - Schema::create('test_models', function(Blueprint $table): void { + Schema::create('test_models', function (Blueprint $table): void { $table->id(); $table->string('name')->nullable(); $table->timestamps(); diff --git a/tests/Unit/AddressDataTest.php b/tests/Unit/AddressDataTest.php index aec07f5..e4adfe1 100644 --- a/tests/Unit/AddressDataTest.php +++ b/tests/Unit/AddressDataTest.php @@ -7,15 +7,15 @@ it('fromArray() populates all known fields', function () { $data = AddressData::fromArray([ - 'type' => AddressType::Billing, - 'street' => 'Pariser Platz 1', - 'extra' => 'Apt. 5', - 'postal' => '10117', - 'city' => 'Berlin', - 'region' => 'Berlin', - 'latitude' => 52.5163, + 'type' => AddressType::Billing, + 'street' => 'Pariser Platz 1', + 'extra' => 'Apt. 5', + 'postal' => '10117', + 'city' => 'Berlin', + 'region' => 'Berlin', + 'latitude' => 52.5163, 'longitude' => 13.3777, - 'country' => 'DE', + 'country' => 'DE', ]); expect($data->type)->toBe(AddressType::Billing) @@ -31,7 +31,7 @@ it('fromArray() ignores unknown keys', function () { $data = AddressData::fromArray([ - 'city' => 'Berlin', + 'city' => 'Berlin', 'unknown' => 'ignored', 'extra_field' => 'also ignored', ]); @@ -71,7 +71,7 @@ }); it('toArray() keeps null type as null', function () { - $data = new AddressData(); + $data = new AddressData; expect($data->toArray()['type'])->toBeNull(); }); diff --git a/tests/Unit/CountriesTest.php b/tests/Unit/CountriesTest.php index 82150bb..d0ab33e 100644 --- a/tests/Unit/CountriesTest.php +++ b/tests/Unit/CountriesTest.php @@ -4,26 +4,26 @@ use Byte5\Addressable\App\Services\Countries; use Illuminate\Translation\PotentiallyTranslatedString; -it('lists country codes mapped to names', function() { - $list = (new Countries())->list(); +it('lists country codes mapped to names', function () { + $list = (new Countries)->list(); expect($list)->toHaveKey('DE', 'Germany') ->and($list)->toHaveKey('US', 'United States'); }); -it('localises the country names', function() { - expect((new Countries())->list('de'))->toHaveKey('DE', 'Deutschland'); +it('localises the country names', function () { + expect((new Countries)->list('de'))->toHaveKey('DE', 'Deutschland'); }); -it('preserves the locale-specific ordering', function() { - $de = array_keys((new Countries())->list('de')); +it('preserves the locale-specific ordering', function () { + $de = array_keys((new Countries)->list('de')); // "Ägypten" (EG) sorts before "Albanien" (AL) in German; the reverse in English. expect(array_search('EG', $de, true))->toBeLessThan(array_search('AL', $de, true)); }); -it('excludes codes that are not official ISO 3166-1 alpha-2', function() { - $list = (new Countries())->list(); +it('excludes codes that are not official ISO 3166-1 alpha-2', function () { + $list = (new Countries)->list(); expect($list)->not->toHaveKey('AC') // Ascension Island ->and($list)->not->toHaveKey('IC') // Canary Islands @@ -34,12 +34,12 @@ ->and($list)->toHaveCount(250); }); -it('only lists codes that the Country rule accepts', function() { - $rule = new Country(); +it('only lists codes that the Country rule accepts', function () { + $rule = new Country; - foreach (array_keys((new Countries())->list()) as $code) { + foreach (array_keys((new Countries)->list()) as $code) { $failures = []; - $rule->validate('country', $code, function(string $message) use (&$failures) { + $rule->validate('country', $code, function (string $message) use (&$failures) { $failures[] = $string = new PotentiallyTranslatedString($message, app('translator')); return $string; diff --git a/tests/Unit/CountryTest.php b/tests/Unit/CountryTest.php index 8d72f53..202f65d 100644 --- a/tests/Unit/CountryTest.php +++ b/tests/Unit/CountryTest.php @@ -15,10 +15,10 @@ function countryFailures(mixed $value): array { $failures = []; - (new Country())->validate( + (new Country)->validate( 'country', $value, - function(string $message) use (&$failures) { + function (string $message) use (&$failures) { $failures[] = $string = new PotentiallyTranslatedString($message, app('translator')); return $string; @@ -28,28 +28,28 @@ function(string $message) use (&$failures) { return array_map('strval', $failures); } -it('passes a valid ISO 3166-1 alpha-2 country code', function() { +it('passes a valid ISO 3166-1 alpha-2 country code', function () { expect(countryFailures('US'))->toBeEmpty() ->and(countryFailures('DE'))->toBeEmpty() ->and(countryFailures('NL'))->toBeEmpty(); }); -it('is case-insensitive about the country code', function() { +it('is case-insensitive about the country code', function () { expect(countryFailures('de'))->toBeEmpty(); }); -it('fails an unknown country code', function() { +it('fails an unknown country code', function () { expect(countryFailures('ZZ'))->not->toBeEmpty() ->and(countryFailures('XX'))->not->toBeEmpty(); }); -it('fails a malformed country code', function() { +it('fails a malformed country code', function () { expect(countryFailures('1'))->not->toBeEmpty() ->and(countryFailures('U'))->not->toBeEmpty() ->and(countryFailures('USA'))->not->toBeEmpty(); }); -it('skips empty values so required/nullable can own emptiness', function() { +it('skips empty values so required/nullable can own emptiness', function () { expect(countryFailures(''))->toBeEmpty() ->and(countryFailures(null))->toBeEmpty(); }); diff --git a/tests/Unit/PlaceDetailsTest.php b/tests/Unit/PlaceDetailsTest.php index 45a5e17..5fb5467 100644 --- a/tests/Unit/PlaceDetailsTest.php +++ b/tests/Unit/PlaceDetailsTest.php @@ -38,7 +38,7 @@ }); it('defaults every field to null', function () { - $details = new PlaceDetails(); + $details = new PlaceDetails; expect($details->toArray())->toBe([ 'street' => null, diff --git a/tests/Unit/PostalFormatTest.php b/tests/Unit/PostalFormatTest.php index c9582bd..5815eaa 100644 --- a/tests/Unit/PostalFormatTest.php +++ b/tests/Unit/PostalFormatTest.php @@ -18,7 +18,7 @@ function postalFailures(?string $country, mixed $value): array (new PostalFormat($country))->validate( 'postal', $value, - function(string $message) use (&$failures) { + function (string $message) use (&$failures) { $failures[] = $string = new PotentiallyTranslatedString($message, app('translator')); return $string; @@ -28,38 +28,38 @@ function(string $message) use (&$failures) { return array_map('strval', $failures); } -it('passes a valid postal code for the country', function() { +it('passes a valid postal code for the country', function () { expect(postalFailures('DE', '10115'))->toBeEmpty() ->and(postalFailures('AT', '1010'))->toBeEmpty() ->and(postalFailures('NL', '1011 AB'))->toBeEmpty(); }); -it('fails an invalid postal code for the country', function() { +it('fails an invalid postal code for the country', function () { expect(postalFailures('DE', '123'))->not->toBeEmpty() ->and(postalFailures('DE', 'ABCDE'))->not->toBeEmpty() ->and(postalFailures('AT', '10115'))->not->toBeEmpty(); }); -it('is case-insensitive about the country code', function() { +it('is case-insensitive about the country code', function () { expect(postalFailures('de', '10115'))->toBeEmpty(); }); -it('does not validate countries without a known pattern', function() { +it('does not validate countries without a known pattern', function () { expect(postalFailures('ZZ', 'anything-goes'))->toBeEmpty() ->and(postalFailures(null, 'anything-goes'))->toBeEmpty(); }); -it('skips empty values so required/nullable can own emptiness', function() { +it('skips empty values so required/nullable can own emptiness', function () { expect(postalFailures('DE', ''))->toBeEmpty() ->and(postalFailures('DE', null))->toBeEmpty(); }); -it('validates countries via commerceguys/addressing data', function() { +it('validates countries via commerceguys/addressing data', function () { expect(postalFailures('CA', 'K1A 0B1'))->toBeEmpty() ->and(postalFailures('CA', '12345'))->not->toBeEmpty(); }); -it('does not validate countries that have no postal codes', function() { +it('does not validate countries that have no postal codes', function () { expect(postalFailures('AE', 'whatever'))->toBeEmpty() ->and(postalFailures('HK', 'whatever'))->toBeEmpty(); });