I'm pretty new into testing things, and I was wondering how I could test such code or even if it's valuable to test this.
this is what my code looks like :
public function convert(string $urlToConvert, string $path, array $options = []): void
{
try {
$result = $this->getClient()->post(
sprintf('%s/%s', $this->config->getEndpoint(), self::GOTENBERG_CHROMIUM_ENDPOINT),
[
'multipart' => [
['name' => 'url', 'contents' => $urlToConvert],
['name' => 'emulatedMediaType', 'contents' => 'screen'],
...$options
]
]
);
$directory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);
$directory->writeFile($path, $result->getBody());
} catch (Exception | GuzzleException $e) {
$this->logger->error($e, ['context' => 'm2-gotenberg']);
throw new GotenbergConvertException(__('Failed converting PDF'), $e);
}
}
The getClient() returns an instance of a GuzzleHttp.
The process is the following :
Do a request on an endpoint with the URL
Get the response body which is the converted PDF
Create a file with the content given by the response
I don't feel like I could test anything, or a really small amount of the code.
About the lines creating the files, this is the done by the framework I'm using
The only thing I see is to do an integration test, and fetching the endpoint to create a real pdf, then delete it.
Can someone clarify this for me or give me some advices? When using framework I have troubles on what to test to avoid testing the implementation of my code
Related
I have got trouble fetching the content of error responses (400/500) when using Symfony http client (RetryableHttpClient) and AsyncResponse:
The content is always empty, no matter if I do getContent() within a try/catch statement or use getContent(false) (false=dont throw an exception on error status code). The documentation states that getContent() will wait for the whole response to be fetched, even when using streamed/async Responses.
If I test the same request i.e. via Postman the response content is a json, as expected.
Example V1:
/* #var Symfony\Component\HttpClient\RetryableHttpClient $client */
$response = $client->request('POST', '/something' , [
'auth_bearer' => '******',
'body' => [
"some" => "thing"
]
]);
/* #var Symfony\Component\HttpClient\Response\AsyncResponse $response */
p_r($response->getContent(false)); // empty
Example V2 (try/catch):
try {
... see V1
$response->getContent(); // throws exception
} catch (ClientExceptionInterface|ServerExceptionInterface $exception) {
p_r($exception->getResponse()->getContent(false)); // empty
}
can you trying the toArray ? in the documentation for payload Json it is better to get the toArray link
$exception->getResponse()->toArray(false)
I am working on a process to upload a large number of files to S3, and for my smaller files I am building a list of commands using getCommand to upload them concurrently, like this:
$commands = array();
$commands[] = $s3Client->getCommand('PutObject', array(
'Bucket' => 'mybucket',
'Key' => 'filename.ext',
'Body' => fopen('filepath', 'r'),
));
$commands[] = $s3Client->getCommand('PutObject', array(
'Bucket' => 'mybucket',
'Key' => 'filename_2.ext',
'Body' => fopen('filepath_2', 'r'),
));
etc.
try {
$pool = new CommandPool($s3Client, $commands, [
'concurrency' => 5,
'before' => function (CommandInterface $cmd, $iterKey) {
//Do stuff before the file starts to upload
},
'fulfilled' => function (ResultInterface $result, $iterKey, PromiseInterface $aggregatePromise) {
//Do stuff after the file is finished uploading
},
'rejected' => function (AwsException $reason, $iterKey, PromiseInterface $aggregatePromise) {
//Do stuff if the file fails to upload
},
]);
// Initiate the pool transfers
$promise = $pool->promise();
// Force the pool to complete synchronously
$promise->wait();
$promise->then(function() { echo "All the files have finished uploading!"; });
} catch (Exception $e) {
echo "Exception Thrown: Failed to upload: ".$e->getMessage()."<br>\n";
}
This works fine for the smaller files, but some of my files are large enough that I'd like them to automatically be uploaded in multiple parts. So, instead of using getCommand('PutObject'), which uploads an entire file, I'd like to use something like getCommand('ObjectUploader') so that the larger files can be automatically broken up as needed. However, when I try to use getCommand('ObjectUploader') it throws an error and says that it doesn't know what to do with that. I'm guessing that perhaps the command has a different name, which is why it is throwing the error. But, it's also possible that it's not possible to do it like this.
If you've worked on something like this in the past, how have you done it? Or even if you haven't worked on it, I'm open to any ideas you might have.
Thanks!
References:
https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_commands.html#command-pool
https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-multipart-upload.html#object-uploader
I decided to go a different direction with this, and instead of using an array of concurrent commands I am now using a set of concurrent MultipartUploader promises, as shown in an example on this page: https://500.keboola.com/parallel-multipart-uploads-to-s3-in-php-61ff03ffc043
Here are the basics:
//Create an array of your file paths
$files = ['file1', 'file2', ...];
//Create an array to hold the promises
$promises = [];
//Create MultipartUploader objects, and add them to the promises array
foreach($files as $filePath) {
$uploader = new \Aws\S3\MultipartUploader($s3Client, $filePath, $uploaderOptions);
$promises[$filePath] = $uploader->promise();
}
//Process the promises once they are all complete
$results = \GuzzleHttp\Promise\unwrap($promises);
Wow talk about a mind bender. Ok, so whenever I run this
<?php
require_once "../vendor/autoload.php";
use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;
$public_key = "PUBLIC_KEY_REDACTED";
$private_key = "PRIVATE_KEY_REDACTED";
$auth = array(
'VAPID' => array(
'subject' => 'https://github.com/Minishlink/web-push-php-example/',
'publicKey' => $public_key, // don't forget that your public key also lives in app.js
'privateKey' => $private_key, // in the real world, this would be in a secret file
)
);
$json = json_decode(file_get_contents("php://input"));
file_put_contents("notification_subscription_info.txt", file_get_contents("php://input"));
try {
$subscription = Subscription::create(json_decode(file_get_contents('php://input'), true));
$notification = [
'subscription' => Subscription::create([ // this is the structure for the working draft from october 2018 (https://www.w3.org/TR/2018/WD-push-api-20181026/)
"endpoint" => "{$json->endpoint}",
"keys" => [
'p256dh' => "{$json->keys->p256dh}",
'auth' => "{$json->keys->auth}"
],
]),
'payload' => 'Hello!',
];
$webPush = new Minishlink\WebPush\WebPush($auth);
$webPush->sendNotification(
$subscription,
$notification['payload'] // optional (defaults null)
);
//version 1 (outside the foreach)
$webPush->flush();
//version 2 (inside the foreach)
foreach($webPush->flush() as $report) { } //This can be empty
} catch (Exception $e) {
echo $e->getMessage();
}
I get very weird behavior in my debugger phpstorm. If I run it with version 1 then I get no push notification and it calls a __destruct method.
If I run version 2 however I get normal behavior and the push notification sends successfully. It appears as if by merely being inside the foreach it changes the flow of the program. I have no idea why this would be and I'm left scratching my head. Does anyone know what is happening?
Edit: I was mistaken about the __destruct method. That's being called because it's the end of the function. In the debugger it's skipping over the flush() call as though it's a literal value. So if I do $report = $webPush->flush(); it will set $report without ever even calling the flush() function (at least that's what it seems like). I even have a breakpoint at the beginning of the flush() function and it isn't hitting it.
I am developing with the Twitter's API and I make several calls POST, GET, PUT, etc. Here an example of how I do that :
public function get(string $screenName): TwitterResponse
{
try {
$response = $this->client->getClient()->get(UserEnum::URI_GET, [
'query' => ['screen_name' => $screenName]
]);
} catch (ClientException $e) {
$this->logger->error($e->getMessage());
$response = $e->getResponse();
}
return new TwitterResponse($response);
}
I would like to know how to unit test that simple method ? I'm not sure if it's really useful to test my method because the "main" job is doing by Twitter. But I would like to reach 80% code coverage at least.
Thanks for reading this.
I am trying to set up SQS and after receiving the message, I need to delete it from the queue.
Creating Client -
$client = Aws\Sqs\SqsClient::factory(array(
'key' => '******',
'secret' => '******',
'region' => 'ap-southeast-1'
));
Sending Message
public static function SendMessage()
{
if(!isset(self::$queueUrl))
self::getQueueUrl();
$command = "This is a command";
$commandstring = json_encode($command);
self::$client->sendMessage(array(
'QueueUrl' => self::$queueUrl,
'MessageBody' => $commandstring,
));
}
Receiving Message
public static function RecieveMessage()
{
if(!isset(self::$queueUrl))
self::getQueueUrl();
$result = self::$client->receiveMessage(array(
'QueueUrl' => self::$queueUrl,
));
// echo "Message Recieved >> ";
print_r($result);
foreach ($result->getPath('Messages/*/Body') as $messageBody) {
// Do something with the message
echo $messageBody;
//print_r(json_decode($messageBody));
}
foreach ($result->getPath('Messages/*/ReceiptHandle') as $ReceiptHandle) {
self::$client->deleteMessage(self::$queueUrl, $ReceiptHandle);
}
}
When I try to delete the message using the Receipt Handle in the receive message code, I get error from Guzzle -
Catchable fatal error: Argument 2 passed to Guzzle\Service\Client::getCommand() must be an array, string given,
Now after searching a lot for it, I was able to find similar questions which state that they were using wrong SDK version. I am still not able to narrow it down though. I am using the zip version of the latest sdk 2.6.15
Why don't you give this a try this:
self::$client->deleteMessage(array(
'QueueUrl' => self::$queueUrl,
'ReceiptHandle' => $ReceiptHandle,
));
The Basic formatting example in the API docs for SqsClient::deleteMessage() (and other operations) should help. All of the methods that execute operations take exactly one parameter, which is an associative array of the operation's parameters. You should read through the SDK's Getting Started Guide (if you haven't already), which talks about how to perform operations in general.