From 4a7ec2f7da5da58faefd63ce4b676769f1ab0ab9 Mon Sep 17 00:00:00 2001 From: Keith Brink Date: Thu, 18 Apr 2024 14:48:42 +0300 Subject: [PATCH] 100% coverage --- .github/workflows/ci.yml | 4 +- phpunit.xml | 6 +- src/Data/Base/DataBuilder.php | 14 +-- src/Data/Base/DataValidator.php | 22 ----- .../Validators/DataValidationException.php | 14 --- .../InboundShipmentInfoSchema.php | 2 - src/Resources/FulfillmentInboundResource.php | 14 --- src/Resources/OrdersResource.php | 5 -- tests/Setup/SetupAmznSPAConfig.php | 1 + tests/Unit/AmznSPAConfigTest.php | 1 + tests/Unit/AmznSPAHttpTest.php | 18 ++++ tests/Unit/Data/AuthTokensTest.php | 47 ---------- tests/Unit/Data/Base/DataBuilderTest.php | 72 ++++++++++++++++ tests/Unit/Data/Base/DataValidatorTest.php | 86 +++++++++++++++++++ .../Mappers/CarbonToDateStringMapperTest.php | 27 ++++++ tests/Unit/Data/Base/ToArrayObjectTest.php | 61 +++++++++++++ tests/Unit/Data/Base/TypedCollectionTest.php | 7 ++ .../PutTransportDetailsRequestTest.php | 4 +- tests/Unit/Data/TokensTest.php | 69 +++++++++++++++ 19 files changed, 358 insertions(+), 116 deletions(-) delete mode 100644 tests/Unit/Data/AuthTokensTest.php create mode 100644 tests/Unit/Data/Base/DataValidatorTest.php create mode 100644 tests/Unit/Data/Base/Mappers/CarbonToDateStringMapperTest.php create mode 100644 tests/Unit/Data/TokensTest.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1fa0665a..f289667a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,11 +11,11 @@ jobs: - name: Install dependencies uses: php-actions/composer@v6 with: - php_version: "8.3" + php_version: "8.2" - name: PHPUnit Tests uses: php-actions/phpunit@v3 with: - php_version: "8.3" + php_version: "8.2" php_extensions: pcov bootstrap: vendor/autoload.php configuration: phpunit.xml diff --git a/phpunit.xml b/phpunit.xml index 8c5e21ca..3062e833 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,5 +1,5 @@ - + @@ -22,7 +22,9 @@ ./src - ./src/Data + ./src/Data/Requests + ./src/Data/Responses + ./src/Data/Schemas diff --git a/src/Data/Base/DataBuilder.php b/src/Data/Base/DataBuilder.php index df8ee39a..2da32c50 100644 --- a/src/Data/Base/DataBuilder.php +++ b/src/Data/Base/DataBuilder.php @@ -72,19 +72,19 @@ private function getParameterValue( } if ($parameter->getType() instanceof \ReflectionUnionType) { - foreach ($parameter->getType()->getTypes() as $type) { - if ($type->getName() === 'null') { - continue; - } - + $types = array_filter( + $parameter->getType()->getTypes(), + fn ($type) => $type->getName() !== 'null', + ); + foreach ($types as $type) { try { return self::getValueFromNamedType($type, $payload_value); - } catch (\InvalidArgumentException) { + } catch (\Throwable) { continue; } } - throw new \InvalidArgumentException("Unsupported parameter union for: {$parameter->getName()}"); + throw new \InvalidArgumentException("Unsupported parameter union for: {$parameter->getName()}"); // @codeCoverageIgnore } return self::getValueFromNamedType($parameter->getType()->getName(), $payload_value); diff --git a/src/Data/Base/DataValidator.php b/src/Data/Base/DataValidator.php index 036c7a1f..099e0750 100644 --- a/src/Data/Base/DataValidator.php +++ b/src/Data/Base/DataValidator.php @@ -12,31 +12,9 @@ public function validate(object $data): void $reflection = new \ReflectionClass($data); foreach ($reflection->getProperties() as $property) { - if (! $property->isInitialized($data)) { - continue; - } - $property->setAccessible(true); $property_value = $property->getValue($data); - // If is iterable, validate all items that are data objects - if (is_iterable($property_value)) { - foreach ($property_value as $item) { - if ($item instanceof Data) { - $this->validate($item); - } - } - - continue; - } - - // If is data object, validate it recursively - if ($property_value instanceof Data) { - $this->validate($property_value); - - continue; - } - foreach ($this->getValidators($property) as $validator) { try { $validator->validate($property_value); diff --git a/src/Data/Base/Validators/DataValidationException.php b/src/Data/Base/Validators/DataValidationException.php index ab5e72b3..aafab023 100644 --- a/src/Data/Base/Validators/DataValidationException.php +++ b/src/Data/Base/Validators/DataValidationException.php @@ -4,18 +4,4 @@ class DataValidationException extends \Exception { - public static function create( - string $class, - string $property, - mixed $value, - ?string $message = null, - ): self { - $exception_message = json_encode($value)." is not a valid value for $class::$property."; - - if ($message) { - $exception_message .= " $message"; - } - - return new self($exception_message); - } } diff --git a/src/Data/Schemas/FulfillmentInbound/InboundShipmentInfoSchema.php b/src/Data/Schemas/FulfillmentInbound/InboundShipmentInfoSchema.php index 7cd86551..afc8ddab 100644 --- a/src/Data/Schemas/FulfillmentInbound/InboundShipmentInfoSchema.php +++ b/src/Data/Schemas/FulfillmentInbound/InboundShipmentInfoSchema.php @@ -5,7 +5,6 @@ use Carbon\CarbonImmutable; use Jasara\AmznSPA\Constants\AmazonEnums; use Jasara\AmznSPA\Data\Base\Casts\CarbonFromStringCaster; -use Jasara\AmznSPA\Data\Base\Casts\EmptyArrayToNullCaster; use Jasara\AmznSPA\Data\Base\Validators\StringEnumValidator; use Jasara\AmznSPA\Data\Schemas\AddressSchema; use Jasara\AmznSPA\Data\Schemas\BaseSchema; @@ -15,7 +14,6 @@ class InboundShipmentInfoSchema extends BaseSchema public function __construct( public ?string $shipment_id, public ?string $shipment_name, - #[EmptyArrayToNullCaster] public ?AddressSchema $ship_from_address, public ?string $destination_fulfillment_center_id, #[StringEnumValidator(AmazonEnums::SHIPMENT_STATUSES)] diff --git a/src/Resources/FulfillmentInboundResource.php b/src/Resources/FulfillmentInboundResource.php index 781c4393..6d29bdf8 100644 --- a/src/Resources/FulfillmentInboundResource.php +++ b/src/Resources/FulfillmentInboundResource.php @@ -3,7 +3,6 @@ namespace Jasara\AmznSPA\Resources; use Carbon\CarbonImmutable; -use Illuminate\Support\Arr; use Jasara\AmznSPA\AmznSPAHttp; use Jasara\AmznSPA\Constants\AmazonEnums; use Jasara\AmznSPA\Constants\MarketplacesList; @@ -239,19 +238,6 @@ public function getShipments( return $response; } - private function setEmptyShipFromAddressToNull(array $response): array - { - $response['payload']['shipment_data'] = array_map(function (array $shipment_data) { - if (Arr::get($shipment_data, 'ship_from_address') === []) { - $shipment_data['ship_from_address'] = null; - } - - return $shipment_data; - }, $response['payload']['shipment_data']); - - return $response; - } - public function getShipmentItemsByShipmentId(string $shipment_id, string $marketplace_id): GetShipmentItemsResponse { $this->validateStringEnum($marketplace_id, MarketplacesList::allIdentifiers()); diff --git a/src/Resources/OrdersResource.php b/src/Resources/OrdersResource.php index b78db33c..3e1178f8 100644 --- a/src/Resources/OrdersResource.php +++ b/src/Resources/OrdersResource.php @@ -3,7 +3,6 @@ namespace Jasara\AmznSPA\Resources; use Carbon\CarbonImmutable; -use Illuminate\Support\Arr; use Jasara\AmznSPA\AmznSPAHttp; use Jasara\AmznSPA\Constants\MarketplacesList; use Jasara\AmznSPA\Contracts\ResourceContract; @@ -91,10 +90,6 @@ public function getOrder(string $order_id): GetOrderResponse ->responseClass(GetOrderResponse::class) ->get($this->endpoint . self::BASE_PATH . 'orders/' . $order_id); - if (Arr::get($response, 'payload') === []) { - $response['payload'] = null; - } - return $response; } diff --git a/tests/Setup/SetupAmznSPAConfig.php b/tests/Setup/SetupAmznSPAConfig.php index b660a71a..667900a8 100644 --- a/tests/Setup/SetupAmznSPAConfig.php +++ b/tests/Setup/SetupAmznSPAConfig.php @@ -47,6 +47,7 @@ public function setupLiveConfig(): AmznSPAConfig return $config; } + /** @return array{0: AmznSPAConfig, 1: Factory} */ public function setupConfigWithFakeHttp(string | array $stubs, int $status_code = 200): array { if (is_string($stubs)) { diff --git a/tests/Unit/AmznSPAConfigTest.php b/tests/Unit/AmznSPAConfigTest.php index 4785cfda..eeb9ff55 100644 --- a/tests/Unit/AmznSPAConfigTest.php +++ b/tests/Unit/AmznSPAConfigTest.php @@ -22,6 +22,7 @@ use Symfony\Component\HttpKernel\Log\Logger; #[CoversClass(AmznSPAConfig::class)] +#[CoversClass(ApplicationKeys::class)] class AmznSPAConfigTest extends UnitTestCase { public function testGetNewConfig() diff --git a/tests/Unit/AmznSPAHttpTest.php b/tests/Unit/AmznSPAHttpTest.php index cd1d476b..68b75ba9 100644 --- a/tests/Unit/AmznSPAHttpTest.php +++ b/tests/Unit/AmznSPAHttpTest.php @@ -535,4 +535,22 @@ public function testResponseCallbackIsCalled() $amzn = new AmznSPA($config); $amzn->fulfillment_outbound->cancelFulfillmentOrder('some-order-id'); } + + public function testCannotSetResponseThatIsNotBaseResponse() + { + $this->expectExceptionMessage('Response class must extend BaseResponse'); + + $http = new AmznSPAHttp($this->setupMinimalConfig()); + $http->responseClass('stdClass'); + } + + public function testReturnsArrayWithNoResponseClassSet() + { + [$config] = $this->setupConfigWithFakeHttp('reports/get-report-document'); + + $http = new AmznSPAHttp($config); + $response = $http->get($config->getMarketplace()->getBaseUrl() . '/orders/v0/orders'); + + $this->assertIsArray($response); + } } diff --git a/tests/Unit/Data/AuthTokensTest.php b/tests/Unit/Data/AuthTokensTest.php deleted file mode 100644 index 66044f29..00000000 --- a/tests/Unit/Data/AuthTokensTest.php +++ /dev/null @@ -1,47 +0,0 @@ -assertEquals($access_token, $dto->access_token); - $this->assertEquals($refresh_token, $dto->refresh_token); - $this->assertEqualsWithDelta($expires_in, $dto->expires_at->diffInSeconds(CarbonImmutable::now()), 2); - } - - public function testSetupTokensWithExistingDate() - { - $access_token = Str::random(); - $refresh_token = Str::random(); - $expires_at = CarbonImmutable::now()->addSeconds(rand(100, 500)); - - $dto = new AuthTokens( - access_token: $access_token, - refresh_token: $refresh_token, - expires_at: $expires_at, - ); - - $this->assertEquals($access_token, $dto->access_token); - $this->assertEquals($refresh_token, $dto->refresh_token); - $this->assertEquals($expires_at, $dto->expires_at); - } -} diff --git a/tests/Unit/Data/Base/DataBuilderTest.php b/tests/Unit/Data/Base/DataBuilderTest.php index 1c70ca96..ab478f69 100644 --- a/tests/Unit/Data/Base/DataBuilderTest.php +++ b/tests/Unit/Data/Base/DataBuilderTest.php @@ -3,10 +3,14 @@ namespace Jasara\AmznSPA\Tests\Unit\Data\Base; use Carbon\CarbonImmutable; +use Jasara\AmznSPA\Contracts\IsFlatResponse; use Jasara\AmznSPA\Data\Base\Data; use Jasara\AmznSPA\Data\Base\DataBuilder; +use Jasara\AmznSPA\Data\Responses\CatalogItems\v20201201\GetCatalogItemResponse; +use Jasara\AmznSPA\Data\Responses\FulfillmentOutbound\CreateFulfillmentOrderResponse; use Jasara\AmznSPA\Data\Responses\Tokens\CreateRestrictedDataTokenResponse; use Jasara\AmznSPA\Data\Responses\Uploads\CreateUploadDestinationResponse; +use Jasara\AmznSPA\Data\Schemas\AddressSchema; use Jasara\AmznSPA\Data\Schemas\FulfillmentInbound\InboundShipmentInfoSchema; use Jasara\AmznSPA\Data\Schemas\FulfillmentInbound\NonPartneredSmallParcelDataOutputSchema; use Jasara\AmznSPA\Data\Schemas\FulfillmentInbound\NonPartneredSmallParcelPackageOutputListSchema; @@ -78,6 +82,15 @@ public function testBuildDataWithCollectionLikeSchema(): void $this->assertEquals('IN_TRANSIT', $data->package_list[1]->package_status); } + public function testBuildDataFailsIfInvalidDataSentToCollectionParameter(): void + { + $this->expectExceptionMessage('Expected iterable for collection parameter'); + + NonPartneredSmallParcelDataOutputSchema::from([ + 'package_list' => 'INVALID', + ]); + } + public function testBuildDataWithNestedData(): void { $data = CreateUploadDestinationResponse::from([ @@ -111,6 +124,17 @@ public function testBuildsDataWithUnionType(): void $this->assertLessThanOrEqual(3600, $data->expires_in->diffInSeconds()); } + public function testBuildsDataWithInvalidData(): void + { + $this->expectExceptionMessage('Missing required parameter: name'); + + AddressSchema::from([ + 'address_line_1' => 'address_line_1', + 'country_code' => 'CA', + 'postal_code' => 'postal_code', + ]); + } + public function testBuildsDataWithCarbonCaster(): void { $data = PartneredLtlDataInputSchema::from([ @@ -144,4 +168,52 @@ public function testBuildsDataWithEmptyArrayNotNullableDoesNotMapToNull(): void $this->assertInstanceOf(FeesEstimateErrorSchema::class, $data); $this->assertIsArray($data->detail); } + + public function testBuildDataWithNoParameters(): void + { + $data = CreateFulfillmentOrderResponse::from(); + + $this->assertInstanceOf(CreateFulfillmentOrderResponse::class, $data); + } + + public function testBuildDataWithFlatResponse(): void + { + $data = GetCatalogItemResponse::from([ + 'asin' => 'asin', + ]); + + $this->assertInstanceOf(GetCatalogItemResponse::class, $data); + $this->assertEquals('asin', $data->item->asin); + } + + public function testBuildDataWithEmptyFlatResponseIsIsError(): void + { + $data = GetCatalogItemResponse::from([ + 'errors' => [], + ]); + + $this->assertInstanceOf(GetCatalogItemResponse::class, $data); + $this->assertNull($data->item); + } + + public function testBuildDataWithFlatResponseFailsIfParameterDoesntExist(): void + { + $this->expectExceptionMessage('Missing required parameter: invalid'); + + $class = new class('test') extends Data implements IsFlatResponse { + public function __construct( + public string $not_matching, + ) { + } + + public static function mapResponseToParameter(): string + { + return 'invalid'; + } + }; + + $class::from([ + 'invalid' => 'invalid', + ]); + } } diff --git a/tests/Unit/Data/Base/DataValidatorTest.php b/tests/Unit/Data/Base/DataValidatorTest.php new file mode 100644 index 00000000..ef582238 --- /dev/null +++ b/tests/Unit/Data/Base/DataValidatorTest.php @@ -0,0 +1,86 @@ +expectException(DataValidationException::class); + $this->expectExceptionMessage('The length of the string'); + + AddressSchema::from([ + 'name' => Str::random(60), + 'address_line_1' => Str::random(), + 'country_code' => Str::random(2), + 'postal_code' => Str::random(), + ]); + } + + public function testValidatesDataRecursively(): void + { + $this->expectException(DataValidationException::class); + $this->expectExceptionMessage('The length of the string'); + + CreateInboundShipmentPlanRequest::from([ + 'ship_from_address' => new AddressSchema( + name: Str::random(60), + address_line_1: Str::random(), + address_line_2: null, + district_or_county: null, + city: null, + state_or_province_code: null, + country_code: Str::random(2), + postal_code: Str::random(), + email: null, + phone: null, + ), + 'label_prep_preference' => 'SELLER_LABEL', + 'inbound_shipment_plan_request_items' => [ + [ + 'seller_sku' => Str::random(), + 'asin' => Str::random(), + 'condition' => 'INVALID', + 'quantity' => 1, + ], + ], + ]); + } + + public function testValidatesCollectionsOfData(): void + { + $this->expectException(DataValidationException::class); + $this->expectExceptionMessage('Validation failed for property condition'); + + CreateInboundShipmentPlanRequest::from([ + 'ship_from_address' => [ + 'name' => Str::random(10), + 'address_line_1' => Str::random(), + 'country_code' => Str::random(2), + 'postal_code' => Str::random(), + ], + 'label_prep_preference' => 'SELLER_LABEL', + 'inbound_shipment_plan_request_items' => InboundShipmentPlanRequestItemListSchema::make([ + new InboundShipmentPlanRequestItemSchema( + seller_sku: Str::random(), + asin: Str::random(), + condition: 'INVALID', + quantity: 1, + quantity_in_case: null, + prep_details_list: null, + ), + ]), + ]); + } +} diff --git a/tests/Unit/Data/Base/Mappers/CarbonToDateStringMapperTest.php b/tests/Unit/Data/Base/Mappers/CarbonToDateStringMapperTest.php new file mode 100644 index 00000000..7c6fc53f --- /dev/null +++ b/tests/Unit/Data/Base/Mappers/CarbonToDateStringMapperTest.php @@ -0,0 +1,27 @@ +map(\Carbon\Carbon::parse('2021-01-01')); + + $this->assertEquals('2021-01-01', $result); + } + + public function testThrowsExceptionIfNotCarbon(): void + { + $this->expectException(\InvalidArgumentException::class); + + $mapper = new CarbonToDateStringMapper(); + $mapper->map('2021-01-01'); + } +} diff --git a/tests/Unit/Data/Base/ToArrayObjectTest.php b/tests/Unit/Data/Base/ToArrayObjectTest.php index a4183e23..9aa0cc0d 100644 --- a/tests/Unit/Data/Base/ToArrayObjectTest.php +++ b/tests/Unit/Data/Base/ToArrayObjectTest.php @@ -3,7 +3,11 @@ namespace Jasara\AmznSPA\Tests\Unit\Data\Base; use Jasara\AmznSPA\Data\Base\ToArrayObject; +use Jasara\AmznSPA\Data\Requests\FulfillmentInbound\PutTransportDetailsRequest; +use Jasara\AmznSPA\Data\Requests\ListingsItems\ListingsItemPatchRequest; use Jasara\AmznSPA\Data\Schemas\FulfillmentInbound\PartneredLtlDataInputSchema; +use Jasara\AmznSPA\Data\Schemas\MerchantFulfillment\AdditionalSellerInputSchema; +use Jasara\AmznSPA\Data\Schemas\Notifications\ProcessingDirectiveSchema; use Jasara\AmznSPA\Tests\Unit\UnitTestCase; use PHPUnit\Framework\Attributes\CoversClass; @@ -20,4 +24,61 @@ public function testReturnsDataUsingMappers(): void $this->assertEquals('2021-01-01', $array_object['freightReadyDate']); } + + public function testReturnsDataUsingCollection(): void + { + $data = ListingsItemPatchRequest::from([ + 'product_type' => 'test', + 'patches' => [ + [ + 'op' => 'add', + 'path' => '/path', + ], + ], + ]); + + $array_object = $data->toArrayObject(); + + $this->assertEquals('test', $array_object['productType']); + $this->assertCount(1, $array_object['patches']); + $this->assertEquals('add', $array_object['patches'][0]['op']); + $this->assertEquals('/path', $array_object['patches'][0]['path']); + } + + public function testReturnsDataUsingCarbonImmutable(): void + { + $data = AdditionalSellerInputSchema::from([ + 'value_as_timestamp' => '2021-01-01', + ]); + + $array_object = $data->toArrayObject(); + + $this->assertEquals('2021-01-01T00:00:00Z', $array_object['valueAsTimestamp']); + } + + public function testReturnsDataUsingSnakeCase(): void + { + $data = ProcessingDirectiveSchema::from([ + 'event_filter' => [ + 'event_filter_type' => 'ANY_OFFER_CHANGED', + ], + ]); + + $array_object = $data->toArrayObject(); + + $this->assertEquals('ANY_OFFER_CHANGED', $array_object['event_filter']['event_filter_type']); + } + + public function testReturnsDataUsingPascalCase(): void + { + $request = PutTransportDetailsRequest::from( + is_partnered: true, + shipment_type: 'LTL', + transport_details: [], + ); + + $array_object = $request->toArrayObject(); + + $this->assertEquals('LTL', $array_object['ShipmentType']); + } } diff --git a/tests/Unit/Data/Base/TypedCollectionTest.php b/tests/Unit/Data/Base/TypedCollectionTest.php index d6f4515e..b9138d90 100644 --- a/tests/Unit/Data/Base/TypedCollectionTest.php +++ b/tests/Unit/Data/Base/TypedCollectionTest.php @@ -53,6 +53,13 @@ public function testEnsureFailsWithMakeTypedCollection(): void ]); } + public function testThrowsExceptionIfItemClassIsInvalid(): void + { + $this->expectException(\InvalidArgumentException::class); + + TypedCollection::make(); + } + public function testSucceedsIfDataCanBeMappedToType(): void { $collection = RestrictedResourcesListSchema::make([ diff --git a/tests/Unit/Data/Requests/PutTransportDetailsRequestTest.php b/tests/Unit/Data/Requests/PutTransportDetailsRequestTest.php index dc992b75..2a14b77a 100644 --- a/tests/Unit/Data/Requests/PutTransportDetailsRequestTest.php +++ b/tests/Unit/Data/Requests/PutTransportDetailsRequestTest.php @@ -7,10 +7,12 @@ use Jasara\AmznSPA\Data\Schemas\FulfillmentInbound\PartneredLtlDataInputSchema; use Jasara\AmznSPA\Data\Schemas\FulfillmentInbound\TransportDetailInputSchema; use Jasara\AmznSPA\Tests\Unit\UnitTestCase; +use PHPUnit\Framework\Attributes\CoversClass; +#[CoversClass(PutTransportDetailsRequest::class)] class PutTransportDetailsRequestTest extends UnitTestCase { - public function testReadyDate() + public function testReadyDate(): void { $ready_date = CarbonImmutable::now()->addWeekdays(10); $request = PutTransportDetailsRequest::from( diff --git a/tests/Unit/Data/TokensTest.php b/tests/Unit/Data/TokensTest.php new file mode 100644 index 00000000..088af933 --- /dev/null +++ b/tests/Unit/Data/TokensTest.php @@ -0,0 +1,69 @@ +assertEquals($access_token, $auth_tokens->access_token); + $this->assertEquals($refresh_token, $auth_tokens->refresh_token); + $this->assertEqualsWithDelta($expires_in, $auth_tokens->expires_at->diffInSeconds(CarbonImmutable::now()), 2); + + $grantless_token = new GrantlessToken( + access_token: $access_token, + expires_at: $expires_in, + ); + + $this->assertEquals($access_token, $grantless_token->access_token); + $this->assertEqualsWithDelta($expires_in, $grantless_token->expires_at->diffInSeconds(CarbonImmutable::now()), 2); + + $restricted_data_token = new RestrictedDataToken( + access_token: $access_token, + path: '/bearer', + expires_at: $expires_in, + ); + + $this->assertEquals($access_token, $restricted_data_token->access_token); + $this->assertEquals('/bearer', $restricted_data_token->path); + $this->assertEqualsWithDelta($expires_in, $restricted_data_token->expires_at->diffInSeconds(CarbonImmutable::now()), 2); + } + + public function testSetupTokensWithExistingDate() + { + $access_token = Str::random(); + $refresh_token = Str::random(); + $expires_at = CarbonImmutable::now()->addSeconds(rand(100, 500)); + + $dto = new AuthTokens( + access_token: $access_token, + refresh_token: $refresh_token, + expires_at: $expires_at, + ); + + $this->assertEquals($access_token, $dto->access_token); + $this->assertEquals($refresh_token, $dto->refresh_token); + $this->assertEquals($expires_at, $dto->expires_at); + } +}