Symfony - deep cloning functional test - php

I am trying to write I functional test for my deep cloning entity method __clone() on my Symfony project, which is defined in my method class..
I am new at writing tests and I can not find any source where I can get an info about this specific operation. This is what I got so far:
public function testDuplicate()
{
$userInProgress = $this->fixtureLoader->getReferenceRepository()->getReference(UserFixtures::USER_1);
$data = [
"name" => "John Doe"
];
$expected = [
"name" => "John Doe",
"status" => "IN_PROGRESS",
"assignee" => [
"uuid" => "4e7c77d5-8e36-4319-b250-050d241621dc",
]
"archived" => FALSE
];
$client->request('POST',
self::API_URL . self::USER_ENDPOINT . 'copy/' . $userInProgress->getId(),
[],
[],
[
'CONTENT_TYPE' => 'application/ld+json',
'HTTP_ACCEPT' => 'application/ld+json'
],
json_encode($data)
);
//assert
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$response = $client->getResponse()->getContent();
$this->assertArraySubset($expected, json_decode($response, true));
}
This will run my test but I am not sure if it will actually test if deep cloning is set as it should?
Can anybody help?

Related

Unable to POST request using guzzle with the Amadeus API

Description
I am trying to integrate Amadeus Self-Service API within the Laravel Environment. I am successfully able to get content by GET request, but I am not able to get content by the POST request. I have set the exceptions to display the errors thrown by the guzzle in specific.
Here is the api reference, which has the data and the endpoint which I want to post to.
https://developers.amadeus.com/self-service/category/air/api-doc/flight-offers-search/api-reference
How to reproduce
This is the method which I call from my Client.php and pass the data through by calling the POST method.
public function __construct() {
throw_if(static::$instance, 'There should be only one instance of this class');
static::$instance = $this;
$this->client = new Client([
'base_uri' => 'https://test.api.amadeus.com/',
]);
}
public function get($uri, array $options = []) {
$this->authenticate();
return $this->client->request('GET', $uri, [
$options,
'headers' => [
'Authorization' => 'Bearer '.$this->access_token,
],
]);
}
public function post($uri, array $options = []) {
$this->authenticate();
return $this->client->request('POST', $uri, [
$options,
'headers' => [
'Authorization' => 'Bearer '.$this->access_token,
],
]);
}
After calling the POST method, I pass the 'X-HTTP-Method-Override' as 'GET', and pass the data as body.
$requests_response = $client->post('v2/shopping/flight-offers', [
'headers' => [
'X-HTTP-Method-Override' => 'GET',
],
'body' => [
[
"currencyCode" => "USD",
"originDestinations" => [
[
"id" => "1",
"originLocationCode" => "RIO",
"destinationLocationCode" => "MAD",
"departureDateTimeRange" => [
"date" => "2022-11-01",
"time" => "10:00:00",
],
],
[
"id" => "2",
"originLocationCode" => "MAD",
"destinationLocationCode" => "RIO",
"departureDateTimeRange" => [
"date" => "2022-11-05",
"time" => "17:00:00",
],
],
],
"travelers" => [
["id" => "1", "travelerType" => "ADULT"],
["id" => "2", "travelerType" => "CHILD"],
],
"sources" => ["GDS"],
"searchCriteria" => [
"maxFlightOffers" => 2,
"flightFilters" => [
"cabinRestrictions" => [
[
"cabin" => "BUSINESS",
"coverage" => "MOST_SEGMENTS",
"originDestinationIds" => ["1"],
],
],
"carrierRestrictions" => [
"excludedCarrierCodes" => ["AA", "TP", "AZ"],
],
],
],
],
],
]);
Additional context
Here is the error, which I caught in the log.
local.ERROR: Guzzle error {"response":{"GuzzleHttp\\Psr7\\Stream":"
{
\"errors\": [
{
\"code\": 38189,
\"title\": \"Internal error\",
\"detail\": \"An internal error occurred, please contact your administrator\",
\"status\": 500
}
]
}
"}}
local.ERROR: Server error: POST https://test.api.amadeus.com/v2/shopping/flight-offers resulted in a 500 Internal Server Error response:
{
"errors": [
"code": 38189,
(truncated...)
"exception":"[object] (GuzzleHttp\\Exception\\ServerException(code: 500): Server error: POST https://test.api.amadeus.com/v2/shopping/flight-offers resulted in a 500 Internal Server Error response:
"errors": [
"code": 38189,
(truncated...)
at C:\\xampp\\htdocs\\Application\\vendor\\guzzlehttp\\guzzle\\src\\Exception\\RequestException.php:113)
Please spare some time to have a look, help is really appreciated.
Do the POST calls actually work using a HTTP client such as Postman or Insomnia ?
I am noticing is that you are passing an array of $options and are nesting it inside the Guzzle options. The resulting call will look something like this:
$this->client->request('POST', $uri, [
['headers' => '...', 'body' => ['...']],
'headers' => ['...']
]);
That won't work, you are going to need to unpack them this way:
public function post($uri, array $options = []) {
$this->authenticate();
return $this->client->request('POST', $uri, [
...$options,
'headers' => [
'Authorization' => 'Bearer '.$this->access_token,
],
]);
}
Notice the dots ... to unpack the options array. Also notice that you are setting the headers key twice (once in your post method definition and once in the options parameter), so only one will actually be used (by the way why exactly are you using the X-HTTP-Method-Override header ?).
Another solution if you want to pass all header and body in the POST function parameters is this:
public function post($uri, array $options = []) {
$this->authenticate();
return $this->client->request('POST', $uri, [
'json' => $options['json'], // I would suggest 'json' because it looks like the API is looking for a JSON body, if that doesn't work go with 'body'
'headers' => [
'Authorization' => 'Bearer '.$this->access_token,
...$options['headers']
],
]);
}
Another thing you might try if this doesn't do it is using the Guzzle json option instead of body for the POST request.
when you are exploring any Amadeus Self Service API, I recommend to review the portal, because it will help you with one idea about how to make the http calls.
In your case:
https://developers.amadeus.com/self-service/category/air/api-doc/flight-offers-search/api-reference
Another help could be to review the coding examples:
https://github.com/amadeus4dev/amadeus-code-examples/blob/master/flight_offers_search/v2/post/curl/
https://github.com/amadeus4dev/amadeus-code-examples/tree/master/flight_offers_search/v2/get/curl
Maybe it's a little late but this example work for me:
$options = [
'headers' => [
'Authorization' => sprintf('Bearer %s', $this->getApiToken()),
'content-type' => 'application/vnd.amadeus+json',
'X-HTTP-Method-Override' => 'GET',
],
'body' => '{
"currencyCode": "XPF",
"originDestinations": [
{
"id": 1,
"originLocationCode": "PPT",
"originRadius": null,
"alternativeOriginsCodes": [],
"destinationLocationCode": "CDG",
"alternativeDestinationsCodes": [],
"departureDateTimeRange": {
"date": "2022-12-22",
"dateWindow": "I2D"
},
"includedConnectionPoints": [],
"excludedConnectionPoints": []
}
],
"travelers": [
{
"id": "1",
"travelerType": "ADULT",
"associatedAdultId": null
}
],
"sources": [
"GDS"
]
}'
];
try {
...
$response = $this->httpClient->post(self::AMADEUS_API_URL_FLIGHT_OFFER, $options);
$body = $response->getBody();
...
Note: don't forget the content-type, it's not very obvious at first sight in the documentation but without it doesnt work with Guzzle for me (but with insomnia no problem)
consts of the class:
private const AMADEUS_API_CLIENT_GRANT_TYPE = 'client_credentials';
private const AMADEUS_API_URL_AUTH = '/v1/security/oauth2/token';
private const AMADEUS_API_URL_FLIGHT_OFFER = '/v2/shopping/flight-offers';
Authentication:
/**
*
*/
public function authenticate()
{
if (!is_null($this->getApiToken())) {
return $this->getApiToken();
}
$options = [
'form_params' => [
'client_id' => $this->apiId, //setted in the parent construct
'client_secret' => $this->apiKey, //setted in the parent construct
'grant_type' => self::AMADEUS_API_CLIENT_GRANT_TYPE,
]
];
try {
$response = $this->httpClient->post(self::AMADEUS_API_URL_AUTH, $options);
} catch (ClientExceptionInterface $exception) {
...
}
if ($response->getStatusCode() != Response::HTTP_OK) {
throw new ApiException($errorMessage, [$response->getReasonPhrase()], $response->getStatusCode());
}
$body = $response->getBody();
//custom serializer, AmadeusAuthenticationResponse is a mapping based on Amadeus authentication response
$authenticationResponse = $this->serializer->convertSerializationToData($body->getContents(), AmadeusAuthenticationResponse::class);
$this->setApiToken($authenticationResponse->getAccessToken());
return $this->getApiToken();
}';

How to pass an object as parameter in unit test? (laravel)

I have a data object below that I tried to test using postman. I just don't know how to make this possible also in laravel's unit testing. I have visited this link but it doesn't helped in my case.
{
"user_id": 1,
"name": "juan",
"address": [
"state": "Auckland",
"country": "New Zealand",
],
"school": [
{
"school_name": "Kristin School",
"year": 2016
},
{
"school_name": "ACG Parnell College",
"year": 2018
},
],
// and more...
}
How can we pass such an object above in unit testing? Is there any way to achieve this?
$response = $this->post('/logbooks/user', ['Authorization': 'Bearer' + $token]);
First to know that such tests are not a "unit test" type.
Note that the code will run and you should be alert to changes that running the code may entail (updating data in the database, contacting external providers).
Of course, all of this can be mocked, and it is recommended that you read about it carefully.
class FooTest extends TestCase {
public function testBar()
{
$response = $this->post('/api/v1/yourapi',
[
"user_id" => 1,
"name" => "juan",
"address" => [
"state" => "Auckland",
"country" => "New Zealand"
],
"school" => [
[
"school_name" => "Kristin School",
"year" => 2016
],
[
"school_name" => "ACG Parnell College",
"year" => 2018
]
]
]);
$response->assertStatus(200);
$response->assertJsonStructure([...]);
$response->assert....();
}
}
As I wrote in the comment below, the JSON you attached was invalid.
You treated the array as an object, adding unnecessary commas.
It is recommended to use an array that the IDE knows to recognize
As you can find in the Laravel docs for HTTP tests, you can send a php array as follows:
$payload = [
"user_id" => 1,
"name" => "juan",
"address" => [
"state" => "Auckland",
"country" => "New Zealand",
],
];
$response = $this->postJson(
'/logbooks/user', $payload, ['Authorization': 'Bearer' + $token]
);
It takes your php $payload array and encodes it as JSON and sends it.
If you have an object you can cast it with $myArray = (array) $myObject.

Lumen / Laravel: Can't figure out how unittesting a json response works

I can't figure out how unit testing works.
I have controller which returns a JSON response
Controller.php
public function getDiscount(Request $request)
{
if (isset($request) && !empty($request)) {
return response()->json($this->discount->calculateDiscount($request->json()->all()))->getOriginalContent();
}
}
With postman, this is the result of this route:
Post by client:
{
"customer-id": "3",
"items": [
{
"product-id": "A101",
"quantity": "2",
"unit-price": "9.75",
"total": "19.50"
},
{
"product-id": "A102",
"quantity": "1",
"unit-price": "49.50",
"total": "49.50"
}
],
"total": "69.00"
}
Response by API
{
"applied_discounts": [
{
"id": 3,
"name": "Tools Discount",
"description": "Seems like you really like Tools, here's one for free!"
}
],
"discounted_items": [
{
"product-id": "A101",
"quantity": "2",
"unit-price": "9.75",
"total": "19.50",
"discounted_price": 15.6
}
],
"discounted_price": 65.1,
"original_price": "69.00"
}
Now when I try to make unit test, this is what I came up with:
public function testToolsDiscount()
{
$this->json('POST', '/discount',
[
'customer-id' => '3',
'items' => [
[
'product-id' => 'A101',
'quantity' => '2',
'unit-price' => '9.75',
'total' => '19.50'
],
[
'product-id' => 'A102',
'quantity' => '1',
'unit-price' => '49.50',
'total' => '49.50'
]
],
'total' => '69.00'
])
->seeJson(
[
'applied_discounts' => [
[
'id' => 3,
]
],
]);
}
However when I run it, this is the error I get
DiscountTest::testToolsDiscount Invalid JSON was returned from the
route. Perhaps an exception was thrown?
What am I doing wrong?
Make sure your route matches the specified '/discount' with any prefix it may have.
Define the proper route,
I recommend using action() Helper function to add url, the main benefits of this function is when you change some text or prefix in route,
Let say you change from /discount to /discounts in these case you don't need to change the route everywhere.
action('ControllerName#actionName');
It's possible your post-body needs to be an actual JSON string, rather than an associative array. It's also possible that the json() method requires a fully-qualified URL instead of a relative path. If either is the case, this solution may not actually expose the problem, you'll just have to try them and see. Otherwise, try this, it should at least offer some clues as to what's going wrong. Add the following to your unit-test class and dd() the results.
/**
* #param string $uri
* #param string $method
* #param array $body
* #param array $headers
* #param array $files
* #param array $cookies
* #return array
*/
public function callRoute(
$uri,
$method = 'GET',
array $body = [],
array $headers = [],
array $files = [],
array $cookies = []
) {
foreach ($cookies as $name => $cookie) {
$this->app->resolving(\App\Http\Middleware\EncryptCookies::class, function (\App\Http\Middleware\EncryptCookies $cookie) use ($name) {
$cookie->disableFor($name);
});
}
$uri = trim($uri, '/');
$uriParts = parse_url($uri);
//This value may be based on the APP_URL value in your .env, I'm not really sure.
$root = !array_get($uriParts, 'host') ? trim(app(\Illuminate\Http\Request::class)->root(), '/').'/' : '';
$uri = "$root$uri";
$server = $this->transformHeadersToServerVars($headers);
$response = $this->call($method, $uri, $body, $cookies, $files, $server);
$headers = $response->headers->all();
$code = $response->getStatusCode();
$json = $content = $response->getContent();
$json = json_decode($json, true);
$content = ($json && json_last_error() == JSON_ERROR_NONE) ? $json : $content;
return compact('code', 'headers', 'content');
}
I'd be willing to bet the following will expose an error message and stack trace. You'll likely have to follow it up with some other dd() statements in your target controller, or wherever else your logic lives after that error points you in the right direction:
$body = [
'customer-id' => '3',
'items' => [
[
'product-id' => 'A101',
'quantity' => '2',
'unit-price' => '9.75',
'total' => '19.50'
],
[
'product-id' => 'A102',
'quantity' => '1',
'unit-price' => '49.50',
'total' => '49.50'
]
],
'total' => '69.00'
];
$response = $this->callRoute('POST', '/discount', $body)['content'];
dd($response);

Different output in AWS PHP SDK than in AWSCLI

The primary goal that I'm trying to achieve is to iterate over my running EC2 instances in PHP.
It's really easy to get the data using a bash script, as shown below:
Bash script:
#!/bin/bash
export AWS_ACCESS_KEY_ID="AKIDEXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"
aws ec2 describe-instances --region="eu-west-1" --filter "Name=instance-state-name,Values=running"
Bash output:
{
"Reservations": [
{
"OwnerId": "58728357357",
"ReservationId": "r-0e0283649826935",
"Instances": [
{
"SecurityGroups": [
{
"GroupId": "sg-2fe333148",
"GroupName": "WEB"
}
],
"PublicDnsName": "ec2-53-13-121-72.eu-west-1.compute.amazonaws.com",
"Architecture": "x86_64",
"LaunchTime": "2016-07-11T08:28:23.000Z",
"RootDeviceName": "/dev/sda1",
"BlockDeviceMappings": [
{
"Ebs": {
// ...
}
]
}
However, when I try the following example, using the same keys, I am presented with what seems to be an unusable object - or at least the object looks like it is representing an empty data structure.
PHP File:
<?php
require __DIR__ . "/vendor/autoload.php";
$settings = [
"version" => "latest",
"region" => "eu-west-1",
"credentials" => [
"key" => "AKIDEXAMPLE",
"secret" => "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
],
];
$client = new \Aws\Ec2\Ec2Client($settings);
$result = $client->describeInstances([
"Filters" => [
[
"Name" => "instance-state-name",
"Value" => "running",
]
],
]);
var_dump($result);
PHP Output:
What the hell am I meant to do with this AWS\Result?
class Aws\Result#82 (1) {
private $data =>
array(2) {
'Reservations' =>
array(0) {
}
'#metadata' =>
array(4) {
'statusCode' =>
int(200)
'effectiveUri' =>
string(35) "https://ec2.eu-west-1.amazonaws.com"
'headers' =>
array(5) {
...
}
'transferStats' =>
array(1) {
...
}
}
}
}
Am I missing something in the PHP configuration? Please can someone help point me in the right direction?
P.S. I've masked the API keys in the above examples.
EC2::DescribeInstances takes an array of filters, each of which has a string Name and an array of string Values. In your CLI example, you've supplied something for Values, whereas in your PHP example you've supplied a Value instead. This field is not recognized by the SDK and will be ignored. See the SDK API docs for more information.
Your PHP should be updated to read:
<?php
require __DIR__ . "/vendor/autoload.php";
$settings = [
"version" => "latest",
"region" => "eu-west-1",
"credentials" => [
"key" => "AKIDEXAMPLE",
"secret" => "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
],
];
$client = new \Aws\Ec2\Ec2Client($settings);
$result = $client->describeInstances([
"Filters" => [
[
"Name" => "instance-state-name",
"Values" => ["running"],
]
],
]);
var_dump($result);

Neo4j Spatial 3.0.2: No index provider 'spatial' found

I am trying to create a spatial database with neo4j 3.0.2 and neo4j-spatial for 3.0.2. I have installed the plugin and I have checked that the plugin is running with cURL curl -v http://neo4j:neo4j#localhost:7474/db/data/ which outputs following:
{
"extensions" : {
"SpatialPlugin" : {
"addSimplePointLayer" : "http://localhost:7474/db/data/ext/SpatialPlugin/graphdb/addSimplePointLayer",
"addNodesToLayer" : "http://localhost:7474/db/data/ext/SpatialPlugin/graphdb/addNodesToLayer",
"findClosestGeometries" : "http://localhost:7474/db/data/ext/SpatialPlugin/graphdb/findClosestGeometries",
"addGeometryWKTToLayer" : "http://localhost:7474/db/data/ext/SpatialPlugin/graphdb/addGeometryWKTToLayer",
"findGeometriesWithinDistance" : "http://localhost:7474/db/data/ext/SpatialPlugin/graphdb/findGeometriesWithinDistance",
"addEditableLayer" : "http://localhost:7474/db/data/ext/SpatialPlugin/graphdb/addEditableLayer",
"addCQLDynamicLayer" : "http://localhost:7474/db/data/ext/SpatialPlugin/graphdb/addCQLDynamicLayer",
"addNodeToLayer" : "http://localhost:7474/db/data/ext/SpatialPlugin/graphdb/addNodeToLayer",
"getLayer" : "http://localhost:7474/db/data/ext/SpatialPlugin/graphdb/getLayer",
"findGeometriesInBBox" : "http://localhost:7474/db/data/ext/SpatialPlugin/graphdb/findGeometriesInBBox",
"updateGeometryFromWKT" : "http://localhost:7474/db/data/ext/SpatialPlugin/graphdb/updateGeometryFromWKT",
"findGeometriesIntersectingBBox" : "http://localhost:7474/db/data/ext/SpatialPlugin/graphdb/findGeometriesIntersectingBBox"
}
},
"node" : "http://localhost:7474/db/data/node",
"relationship" : "http://localhost:7474/db/data/relationship",
"node_index" : "http://localhost:7474/db/data/index/node",
"relationship_index" : "http://localhost:7474/db/data/index/relationship",
"extensions_info" : "http://localhost:7474/db/data/ext",
"relationship_types" : "http://localhost:7474/db/data/relationship/types",
"batch" : "http://localhost:7474/db/data/batch",
"cypher" : "http://localhost:7474/db/data/cypher",
"indexes" : "http://localhost:7474/db/data/schema/index",
"constraints" : "http://localhost:7474/db/data/schema/constraint",
"transaction" : "http://localhost:7474/db/data/transaction",
"node_labels" : "http://localhost:7474/db/data/labels",
"neo4j_version" : "3.0.2"
* Connection #0 to host localhost left intact
}* Closing connection #0
Now I can create a new simplePointLayer:
// define entity manager
$client = $this->get('neo4j.spatial_manager')->getClient();
// 1. Create a pointlayer
$request = $client->request('POST',
'/db/data/ext/SpatialPlugin/graphdb/addSimplePointLayer',
[
'json' => [
'layer' => 'geom',
'lat' => 'lat',
'lon' => 'lon',
],
]
);
var_dump($request);
This creates the spatial root node with the rTree. But when I now want to create a spatial index with following:
// 2. Create a spatial index
$request = $client->request('POST',
'/db/data/index/node/',
[
'json' => [
'name' => 'geom',
'config' => [
'provider' => 'spatial',
'geometry_type' => 'point',
'lat' => 'lat',
'lon' => 'lon',
],
],
]
);
var_dump($request);
I am confronted with the error-message:
"message" : "No index provider 'spatial' found.
What am I doing wrong? I have checked a lot of forums and so on, but the answer always seems to be to install the spatial plugin, which I have and it seems to be working according to the first output.
EDIT 15.06.2016
Whats weird is that I can add nodes to the rTree:
// Create a node with spatial data
$json = [
'team' => 'REDBLUE',
'name' => 'TEST',
'lat' => 25.121075,
'lon' => 89.990630,
];
$response = $client->request('POST',
'/db/data/node',
[
'json' => $json
]
);
$node = json_decode($response->getBody(), true)['self'];
// Add the node to the layer
$response = $client->request('POST',
'/db/data/ext/SpatialPlugin/graphdb/addNodeToLayer',
[
'json' => [
'layer' => 'geom',
'node' => $node,
],
]
);
$data = json_decode($response->getBody(), true);
var_dump($data);
And I can query the nodes through REST:
$request = $client->request('POST',
'/db/data/ext/SpatialPlugin/graphdb/findGeometriesWithinDistance',
[
'json' => [
'layer' => 'geom',
'pointX' => 89.99506,
'pointY' => 25.121260,
'distanceInKm' => 10,
],
]
);
$data = json_decode($request->getBody(), true);
var_dump($data);
But why is it not letting me create an index? Or does it do the indexing automatically? And if so, how can I query using CYPHER (in the web console for example)?
Any help would be appreciated! Cheers
The index provider was removed (back then it was the only means to provide integration with Cypher), in favor of user defined procedures for Spatial.
see: https://github.com/neo4j-contrib/spatial/blob/master/src/test/java/org/neo4j/gis/spatial/procedures/SpatialProceduresTest.java#L113
As this is a new major release (for 3.0) we found it sensible to remove the index provider.

Categories