How can i send auth header, when test codeception rest api?
What i have now:
Yii2 project
"codeception/module-yii2": "^1.0.0"
"codeception/module-rest": "^1.3"
Generated test class by command codecept generate:cest api TestName
My class with test
class CreateWorkspaceCest
{
public function _before(ApiTester $I)
{
}
public function successCreate(ApiTester $I)
{
$title = 'create test';
$description = 'test description';
$I->sendPost('/workspace/create', [
'title' => $title,
'description' => $description,
]);
$I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); // 200
$I->seeResponseIsJson();
$I->seeResponseContainsJson([
'title' => $title,
'description' => $description,
'status' => 'active',
]);
}
}
Now it fails with 403 code, because backend expects header JWT-Key: <TOKEN>
How can i send auth header in sendPost
And where it is better to store auth token in one place to avoid code duplication, during writing tests?
Codeception has a method called haveHttpHeader you can add any header using it.
This is documented half-way down this page. There is also a section on authorization on this other page.
There are a few built-in authorization methods, like amBearerAuthenticated, amAWSAuthenticated, but I believe that there isn't a specific method for JWT.
class CreateWorkspaceCest
{
public function _before(ApiTester $I)
{
}
public function successCreate(ApiTester $I)
{
$title = 'create test';
$description = 'test description';
// You can add any header like this:
$I->haveHttpHeader('Content-Type', 'application/json');
$I->haveHttpHeader('Authorization', 'Bearer user-one-access-token');
// To add the header that you show in the question, you can use:
$I->haveHttpHeader('JWT-Key', '<TOKEN>');
$I->sendPost('/workspace/create', [
'title' => $title,
'description' => $description,
]);
$I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); // 200
$I->seeResponseIsJson();
$I->seeResponseContainsJson([
'title' => $title,
'description' => $description,
'status' => 'active',
]);
}
}
Related
I'm trying to build a rest API for an existing database. I was suggested to use Rest API from Chris Kacerguis, but his latest build came up with some constant errors. I decided to then use a prior version that still works but have an "unsupported protocol" error. I copy-pasted the code from his basic GET example and still have the same unsupported protocol error.
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
use chriskacerguis\RestServer\RestController;
class Api extends RestController {
function __construct()
{
// Construct the parent class
parent::__construct();
}
public function users_get()
{
// Users from a data store e.g. database
$users = [
['id' => 0, 'name' => 'John', 'email' => 'john#example.com'],
['id' => 1, 'name' => 'Jim', 'email' => 'jim#example.com'],
];
$id = $this->get( 'id' );
if ( $id === null )
{
// Check if the users data store contains users
if ( $users )
{
// Set the response and exit
$this->response( $users, 200 );
}
else
{
// Set the response and exit
$this->response( [
'status' => false,
'message' => 'No users were found'
], 404 );
}
}
else
{
if ( array_key_exists( $id, $users ) )
{
$this->response( $users[$id], 200 );
}
else
{
$this->response( [
'status' => false,
'message' => 'No such user found'
], 404 );
}
}
}
}
https://github.com/chriskacerguis/codeigniter-restserver
Check this option in the REST config file
$config['force_https'] = TRUE;
I have a working api only application.
I am required to write a test decided to use laravel's phpunit test. This simple app allows only authenticated users can store, update or delete a book. Everyone else (authenticated or not) can retrieve a list of all books or view details of one book.
For my books test, I have written a test that first creates a user then a random token for the user. Then the token is passed using withHeaders when posting a new book record
class BooksTest extends TestCase
{
public function test_onlyAuthenticatedUserCanAddBookSuccessfully()
{
$user = factory(User::class)->create();
$token = str_random(10);
$book = factory(Book::class)->create();
$response = $this->withHeaders(['Authorization' => "Bearer $token"])
->json('POST', '/api/books', [
'title' => 'book post',
'author' => 'post author'
]);
$response->assertStatus(201);
}
}
Here I am using the default Laravel 5.6 UserFactory and my own BookFactory
$factory->define(Book::class, function (Faker $faker) {
return [
'title' => $faker->sentence,
'author' => $faker->name,
'user_id' => 1
];
});
$factory->define(Rating::class, function (Faker $faker) {
return [
'user_id' => 1,
'book_id' => mt_rand(1, 2),
'rating' => mt_rand(1, 5)
];
});
When I run the test, it fails and I get 401 instead of 200 which means the user is unauthorized.
I have a feeling that I have probably not set the $user in my test properly to be used during POST but I am not sure and really need help to get it right.
you can send headers in the fourth params of json() method as
$response = $this->json('POST', '/api/books', [
'title' => 'book post',
'author' => 'post author'
],['Authorization' => "Bearer $token"]);
since json method itself has provision to pass headers
or you can use post() method as
$response = $this->post('/api/books', [
'title' => 'book post',
'author' => 'post author'
],['Authorization' => "Bearer $token"]);
Try this instead hope this solves your issues
Not sure how authentication is hooked on your application, but you could try this:
...
$this->actingAs($user)
->jsonPost('/api/books', [
// ...
]);
$response->assertStatus(201);
I am trying to write a PHPUnit test that authenticates a user first before allowing the user to make a post request but got the error
1) Tests\Feature\BooksTest::test_onlyAuthenticatedUserCanAddBookSuccessfully
ErrorException: Trying to get property 'client' of non-object
C:\wamp64\www\bookstore\vendor\laravel\passport\src\ClientRepository.php:89
C:\wamp64\www\bookstore\vendor\laravel\passport\src\PersonalAccessTokenFactory.php:71
C:\wamp64\www\bookstore\vendor\laravel\passport\src\HasApiTokens.php:67
C:\wamp64\www\bookstore\tests\Feature\BooksTest.php:20
When I run my BooksTest
public function test_onlyAuthenticatedUserCanAddBookSuccessfully()
{
$user = factory(User::class)->create();
$token = $user->createToken('bookbook')->accessToken;
$response = $this->withHeaders(['Authorization' => 'Bearer '.$token])
->json('POST', '/api/books', [
'title' => 'new book post',
'author' => 'new author',
'user_id' => $user->id
]);
$response->assertStatus(201);
}
It's my first time working with PHPUnit test, and I have no idea why I'm getting this error. How do I make it work?
You can use Passport::actingAs to accomplish this.
For example:
public function test_onlyAuthenticatedUserCanAddBookSuccessfully()
{
$user = factory(User::class)->create();
Passport::actingAs($user);
$response = $this->json('POST', '/api/books', [
'title' => 'new book post',
'author' => 'new author',
'user_id' => $user->id
]);
$response->assertStatus(201);
}
See the documentation here - https://laravel.com/docs/5.7/passport#testing
Have you any idea how to control the queue of Mandrill send emails in PHP ?
Well, I'm using Symfony as a Framework, & this is my send function in the Mandrill class :
public function sendMandrill()
{
$listTo = array();
foreach ($this->contacts as $contact)
{
$listTo[] = [
'name' => $contact->getFname(),
'email' => $contact->getEmail()
];
}
var_dump($listTo);
$email = array(
'html' => $this->message->getBodyHtml(),
'text' => $this->message->getBodyText(),
'subject' => $this->message->getSubject(),
'from_email' => $this->apiDom,
'from_name' => $this->message->getFromName(),
'to' => $listTo,
"preserve_recipients"=> false,
);
$this->senderAPI = new \Mandrill("$this->apiKey");
return $this->senderAPI->messages->send($email);
}
Now, I want to create a function so I can pause the sending for a while so I can change such things, then resume it whenever I want or may be to stop it at all !
which mean there will be 3 functions as following :
public function pauseMandrill()
{
...
}
public function resumeMandrill()
{
...
}
public function stopMandrill()
{
...
}
I am trying to send automated mails via Mandrill in my Laravel 5.1 project. It was working but I was setting up my Mandrill Calls in my AuthController now I wanna have a class App\Marketing where all my functions for sending email will be stored. So in my controllers after an actions happens I can just call up the function with 1 line of code, but this line is giving me troubles I think.
my App\Marketing class looks like this now
class Marketing{
private $mandrill;
/**
* Via construct injection
*
*/
public function __construct(Mail $mandrill)
{
$this->mandrill = $mandrill;
}
public function sendRegistermail()
{
// In template content you write your dynamic content if you use <mc:edit> tags.
$template_content = [];
$message = array(
'subject' => 'Welkom bij SP*RK! - Jouw accountgegevens',
'from_email' => 'noreply#spark.com',
'from_name' => 'SP*RK',
'to' => array(
array(
'email' => $request->input('email'),
'name' => $request->input('name'),
'type' => 'to'
)
),
'merge_vars' => array(
array(
'rcpt' => $request->input('email'),
'vars' => array(
array(
'name' => 'NAME',
'content' => $request->input('name')
),
array(
'name' => 'EMAIL',
'content' => $request->input('email')
)
)
)
)
);
//email validation
if (str_contains($request['email'], "#kuleuven.be")) {
MandrillMail::messages()->sendTemplate('registration-mail', $template_content, $message);
} else {
MandrillMail::messages()->sendTemplate('registration-mail-notactive', $template_content, $message);
}
}
// ----- OR -------
/**
* Via method injection
*
*/
public function sendMail(Mail $mandrill, $data)
{
$mandrill->messages()->sendTemplate($data)
}
// ----- OR -------
/**
* Via the Facade
*
*/
public function sendMailByFacade($data)
{
\MandrillMail::messages()->sendTemplate($data);
}
}
This is how I try to call the function after registration in my postRegister function:
sendRegistermail();
return redirect($this->redirectPath());
sendRegistermail is a method of your Marketing class, you should call it on an instance of that object
So, first of all you have to create a Marketing object instance in your controller. A good way to do this it's by injecting the dependency in the constructor, like this:
//your controller class
class Controller
{
protected $marketing;
//Your controller's constructor
public function __construct(Marketing $marketing)
{
$this->marketing = $marketing;
}
}
Or you can use one of the other methods you have provided in your code to inject the instance.
Once you have an instance of the Marketing class, you only need to call the sendRegistermail method on that instance. In your controller method:
//call the method on the marketing instance
$this->marketing->sendRegistermail();