Starting from version 5.7 Laravel suggests to use the array driver for Mail during testing:
Unfortunately, the documentation tells nothing about this driver. According to the source code, the driver stores all the messages in memory without actually sending them. How to get the stored "sent" messages during unit testing (in order to check them)?
EDIT: With Laravel 9+, use:
$emails = app()->make('mailer')->getSymfonyTransport()->messages();
dd($emails);
Be sure your mail driver is set to array in your .env or phpunit.xml file.
With Laravel 7+ or if you get error Target class [swift.transport] does not exist use this to get the list of emails sent with the array driver:
$emails = app()->make('mailer')->getSwiftMailer()->getTransport()->messages();
$count = $emails->count();
$subject = $emails->first()->getSubject();
$to = $emails->first()->getTo();
$body = $emails->first()->getBody();
Call app()->make('swift.transport')->driver()->messages(). The return value is a collection of Swift_Mime_SimpleMessage objects.
An example of a full PHPUnit test:
public function testEmail()
{
Mail::to('user#example.com')->send(new MyMail);
$emails = app()->make('swift.transport')->driver()->messages();
$this->assertCount(1, $emails);
$this->assertEquals(['user#example.com'], array_keys($emails[0]->getTo()));
}
My custom assertion based on Finesse's answer.
protected function assertMailSentTo($user, $expected = 1)
{
$messages = app('swift.transport')->messages();
$filtered = $messages->filter(function ($message) use ($user) {
return array_key_exists($user->email, $message->getTo());
});
$actual = $filtered->count();
$this->assertTrue(
$expected === $actual,
"Sent {$actual} messages instead of {$expected}."
);
}
Related
I am using PHP, PHP Legacy Router and PHP Legacy Container.
I am using also PHPUnit test
I am trying to make a correct PHPUnit test, however, I am a begginer and I don`t know whether my code is ok or it is wrong.
In my NewUserService class I write 2 functions to get all emails and if the email is already existed in the db to return an error that user is created already.
My problem is how to test this functionality trough PHPUnit tests in my NewUserServiceTest
This is my functionality to test whether the user is already created
public function getAllUsersEmails()
{
$sql ="SELECT `email` FROM users";
$stmt = $this->container->get('db')->prepare($sql);
$stmt->execute();
if ($stmt->errorCode() != '00000') throw new Exception(sprintf(("Database error (%s): %s"), $stmt->errorCode(), $stmt->errorInfo()[2]));
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* #throws Exception
*/
private function repeatingEmailAddress($requestObj){
$allUsersEmails = NewUserService::getAllUsersEmails();
$allUsersEmailsValues = [];
foreach ($allUsersEmails as $value){
$allUsersEmailsValues[]=$value['email'];
}
if(in_array($requestObj['email'], $allUsersEmailsValues)){
throw new Exception("A user with that email is already created.", 400);
}
This is the unit test:
private function repeatingUserEmail(): array
{
$request = $this->createStub(ServerRequestInterface::class);
$requestJsonPayload = '{"email": "mj#hph.io", "role": 2, "first": "Griffin", "last": "Mitchell"}';
$requestStream = fopen('php://memory', 'r+');
fwrite($requestStream, $requestJsonPayload);
rewind($requestStream);
$request->method('getBody')->willReturn(new Stream($requestStream));
$routeArgs = ['id' => 4];
/* <container with user> */
$container = $this->getStandardContainer();
$user = $this->createMock(User::class);
$user->id = '1';
$user->role = '1';
$user->parent_entity = '11';
$user->email='mj#hph.io';
$container->add('current_user', $user);
$response = ['error' => 'A user with that email is already created.'];
$responseJsonPayload = json_encode($response);
$responseStream = fopen('php://memory', 'r+');
fwrite($responseStream, $responseJsonPayload);
rewind($responseStream);
$expectedResponse = $this->createMock(ResponseInterface::class);
$expectedResponse->method('getStatusCode')->willReturn(400);
$expectedResponse->method('getBody')->willReturn(new Stream($responseStream));
return [$request, $routeArgs, $container, $expectedResponse];
}
Is it correct? How should be done in a correct way?
First of all i suggest to make a isEmailExists($email) method that will search this specific email in the database and return bollean response (true/false). So you don't have to pull millions of emails from database and then loop through millions of rows and in the end you just throw an error.
Back to the test. I suggest to use the expectException assertion that you can find in the documentation
You can 1)add one row in you database and then try to pass the same email that will throw this error or 2)partial mock the
isEmailExists($email) to return true and throw your exception (without hit the database at all)
I have been working around Bit-Wasp/bitcoin-php library for a while now and I encountered problems with it that I cannot resolve.
I have this as my code:
public function bitcoinWalletFromPublicKey($key, $index) {
$adapter = Bitcoin::getEcAdapter();
if (config('market.btc_network') == "mainnet") {
$btc = NetworkFactory::bitcoin();
$bitcoinPrefixes = new BitcoinRegistry();
} else {
$btc = NetworkFactory::bitcoinTestnet();
$bitcoinPrefixes = new BitcoinTestnetRegistry();
}
$slip132 = new Slip132(new KeyToScriptHelper($adapter));
$pubkeytype=substr($key, 0, 4);
if ($pubkeytype=='xpub' || $pubkeytype =='tpub') $pubPrefix = $slip132->p2pkh($bitcoinPrefixes);
if ($pubkeytype=='ypub') $pubPrefix = $slip132->p2shP2wpkh($bitcoinPrefixes);
if ($pubkeytype=='zpub' || $pubkeytype =='vpub') $pubPrefix = $slip132->p2wpkh($bitcoinPrefixes);
$config = new GlobalPrefixConfig([
new NetworkConfig($btc, [$pubPrefix])
]);
$serializer = new Base58ExtendedKeySerializer(
new ExtendedKeySerializer($adapter, $config)
);
$path = '0/' . $index;
$fkey = $serializer->parse($btc, $key);
$child_key = $fkey->derivePath($path);
#$account0Key = $child_key->derivePath("84'/0'/0'");
#$child_key = $fkey->derivePath("0/1");
//dd($child_key->getAddress(new AddressCreator())->getAddress());
return $child_key->getAddress(new AddressCreator())->getAddress();
}
I have two problems with this code:
Problem #1
On the first few lines of the code you will see that I used an If statement to check what network should it use. On my test im using testnet network and I'm sure as well that the code on my If / else { # code } works and it uses NetworkFactory::bitcoinTestnet() and new BitcoinTestnetRegistry() properly;
$key variable represents the Master Public Key of my user from Electrum wallet or whatever with a format of (xpub#########################/vpub#########################) or in my case since its on testnet it uses tpub######################### format. However, it returns an address with a format of bc#########, this means that its passing on mainnet network wherein it should be on testnet network.
Problem #2
On lower part of my code, I'm using $fkey = $serializer->parse($btc, $key); and $child_key = $fkey->derivePath($path) wherein $path = '0/' $index. $index here are just random numbers. It can be 0/1 or 0/99 or whatever 0/random.
Problem here is that somehow related to Problem #1, after it generates the wrong address, when I try to use this address for transaction my rpc returns an invalid address Error. As you can see as well I have a commented code $account0Key = $child_key->derivePath("84'/0'/0'"); wherein i got an error that it needs a private key instead of a public one. Now, my concern is that I do not want the users of the system i'm making to put their private keys whatsoever as it will might just compromise their wallets.
Basically, What I want to achieve using with this library from BitWasp is when a user put in their master public key from their wallet, my system would be able to then generate an address to be used for a btc transaction. Please help.
Passing the network inside the getAddress() method works
return $child_key->getAddress(new AddressCreator())->getAddress($btc);
Before I start, I want to let you know I'm really a noob in PHP and this is the first API I'm making.
It works pretty good if I want to echo one array of information (for example food details), but when I try to do the same with multiple items it returns empty.
I've checked the variable values in debug. It's fine in debug and I see an array which contains multiple sub arrays.
My code
$app->get('/allfoods', 'authenticate', function () use ($app) {
global $user_id;
$db = new FoodHandler();
// In here i get foods with their details via mysql
$result = $db->GetAllFoods();
$response = array();
$response["error"] = false;
$response["foods"] = array();
// looping through result and preparing food array
while ($row = $result->fetch_assoc()) {
$tmp = array();
$tmp['food_id'] = $row['food_id'];
$tmp['food_name'] = $row['food_name'];
$tmp['food_desc'] = $row['food_desc'];
$tmp['food_category'] = $row['food_category'];
$tmp['food_creationDate'] = $row['food_creationDate'];
array_push($response["foods"], $tmp);
}
echoRespnse(200, $response);});
My output function (which works great if there is no array in my array)
function echoRespnse($status_code, $response) {
$app = \Slim\Slim::getInstance();
// Http response code
$app->status($status_code);
// setting response content type to json
$app->contentType('application/json');
echo json_encode($response);
}
$app->run();?>
What is my setup?
Localhost wamp with php 7.2.4
Apache 2.4.33
Mysql 5.7.21
I'm also using Postman to send my request (I also tried it in C#, both give back empty content)
I see several issues with your code. First, there is a problem with your route definition. When defining a route, you should pass two arguments to the get method: a pattern (a string,/allfoods in your case) and an instance of Clousure (a callable, your route callback, the anonymous function in your case.) More details in official docs.
So, first thing is to remove the authenticate string from method parameters and change your route definition to this:
$app->get('/allfoods', function ($request, $response, $args) {
// Body of the function goes here
});
Please note I also removed the use ($app) as you have access to application instance uising $this keyword, so no need for that (described in official docs as well).
Second thing is about generating the response. When using Slim framework it is always a good idea to return the $response object instead of echoing response (read more in official docs). Thisgives you some advantages, for example the helper method whitJson helps you whit generating JSON output.
To refine your whole code in a more Slim-ish way:
$app->get('/allfoods', function ($request, $response, $args) {
global $user_id;
$db = new FoodHandler();
// In here i get foods with their details via mysql
$result = $db->GetAllFoods();
$data= array();
$data["error"] = false;
$data["foods"] = array();
// looping through result and preparing food array
while ($row = $result->fetch_assoc()) {
$tmp = array();
$tmp['food_id'] = $row['food_id'];
$tmp['food_name'] = $row['food_name'];
$tmp['food_desc'] = $row['food_desc'];
$tmp['food_category'] = $row['food_category'];
$tmp['food_creationDate'] = $row['food_creationDate'];
array_push($data["foods"], $tmp);
}
// Return JSON data using helper method
return $response->withJson($data);
}
And you won't need the echoResponse function anymore.
this is my scenario: I have a Laravel queued jod, here the handle method content:
$invoiceContract = app('AsteBolaffi\Repositories\Backend\Invoice\InvoiceContract');
$error = $invoiceContract->recordInvoice($this->invoiceId);
d($error);
if (!empty($error) && !empty($this->userEmail)) {
$userEmail = $this->userEmail;
$invoice = $invoiceContract->findOrThrowException($this->invoiceId);
$invoice->load('customer');
d("Going to send mail to " . $userEmail);
d($error);
d($invoice->customer->business_name);
$data["error"] = $error;
$data["business_name"] = $invoice->customer->business_name;
$data["document_number"] = $invoice->document_number;
d($data);
\Mail::queueOn('mail', "emails.record_invoice", $data, function ($message) use ($userEmail) {
$message->from('admin#astebolaffi.it', 'AsteBolaffi');
$message->to($userEmail);
$message->subject('Errori contabilizzazione fattura');
d("HERE I AM");
});
d("Completed");
}
return;
If the recordInvoice method returns a value (e.g. "Item not found") the if clause is satisfied and it has to add a mail queue. But the mail queue is not created on db and the current job is not deleted even if the console prints the last d method value ("Completed").
If recordInvoice does not returns any error the job is deleted.
Non-sense thing (at least for me)
If I comment the recordInvoice method and set a value to error, for example:
$error = "test";
It works properly, adds the mail queue and deletes the current one.
Any tip about it?
I don't know why, but moving the project from php 5.6 to 7.0 it works correctly.
I just created a mailer class for Zend Framework 2. It uses the Sendmail class.
The problem is that I set the subject of the email with multiple words. Before sending I dump the subject and all the spaces are ok. After sending the email I check my gmail and all the spaces are stripped out of the subject.
When I run the script I get "testemail" as the subject.
Below a part of the class I created :
public function addFile($p_sPath, $p_sMimetype, $p_sFilename){
$rFile = fopen($p_sPath,'rb');
$this->_m_oAttachment = new Mimepart(fread($rFile,filesize($p_sPath)));
$this->_m_oAttachment->type = $p_sMimetype;
$this->_m_oAttachment->filename = $p_sFilename;
$this->_m_oAttachment->disposition = 'attachment';
$this->_m_oAttachment->encoding = Mime::ENCODING_BASE64;
}
public function sendEmail()
{
$aParts = (!is_null($this->_m_oAttachment))
? array($this->_m_oBodymessage, $this->_m_oAttachment)
: array($this->_m_oBodymessage);
$this->_m_oBodypart->setParts($aParts);
$this->_m_oMessage->setEncoding('utf-8')
->setBody($this->_m_oBodypart)
->addFrom($this->_fromAddress, $this->_fromName)
->addReplyTo($this->_fromAddress, $this->_fromName)
->setSubject($this->_subject);
// even here the spaces are still intact.
$this->send($this->_m_oMessage);
}
$oMailer = $this->getLocator()->get('Core\Mailer');
$oMailer->setBodyHtml('mail/mail.phtml', array('aData' => $aData));
$oMailer->setSubject('test email');
$oMailer->setRecipient('jacob#myemail.com', 'jacob');
$oMailer->addFile(realpath(dirname(__file__). '/../../../../../'.$sPath.$sSubfolder.'/'.$sFilename), 'application/pdf', $aData['data']['eventID'].'_'.$aDeclaratie['data']['userID'].'.pdf');
$oMailer->sendEmail();
This is fixed with Zend Framework 2 stable
http://framework.zend.com/issues/browse/ZF2-258