Why users credentials are not working in my login unit testing? - laravel

I'm using PHPUnit for testing my Laravel project.
?php
namespace Tests\Feature;
use App\Models\User;
use Tests\TestCase;
class LoginTest extends TestCase
{
public function test_user_can_login():
{
$user = User::factory()->create();
$response = $this->postJson('/api/auth/login', [
'email' => $user->email,
'password' => $user->password,
]);
$response
->assertStatus(200)
->assertHeader('Authorization')
->assertJson([
'success' => 'Usuário logado com sucesso!',
]);
}
}
I want to create a new user for login test flow, but it returns a 400 status response.
Tests\Feature\LoginTest > user can login
Expected status code 200 but received 400.
Failed asserting that 200 is identical to 400.
I have no idea what is going wrong. Any suggestions?

Related

How set uploaded avatar in this.$page.props.user with inertiajs?

In Laravel 8 app with inertiajs/inertia-vue 0.7/vuejs2 with fortify (but without jetstream)
I use "spatie/laravel-medialibrary": "^9.9" for avatar uploading like
$loggedUser = auth()->user();
$avatar_file_path = $avatarImageUploadedFile->getPathName();
$loggedUser->addMedia( $avatar_file_path )->toMediaCollection('avatar');
and it works, but in which way can I use avatar in on client part when I use
this.$page.props.user
in vue components properties to check which user is logged and show his info?
Thanks!
You can do this with the 'Shared data' feature via the HandleInertiaRequests middleware.
For example, to share user info you can do the following:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
class HandleInertiaRequests extends Middleware
{
public function share(Request $request)
{
return array_merge(parent::share($request), [
'user' => function () {
return Auth::user() ? [
'id' => Auth::user()->id,
'name' => Auth::user()->name,
'email' => Auth::user()->email,
// get path to avatar
'avatar' => Storage::url('avatar-of-the-user.jpg'),
] : null;
},
]);
}
}
Client side you can then access the avatar's URL with this.$page.props.user.avatar.

How can I get api/register with Passport in laravel working?

Following a tutorial: https://www.twilio.com/blog/build-secure-api-php-laravel-passport
I manAged to get Laravel/Passport installed formy Laravel Api and Vue application.
I managed to create attoken with:
localhost:8000/oauth/token
get the login working in Postman:
localhost:8000/api/login?email=jennie05#example.com&password=password
Now when I try to register a user I get returned to the home-page.
I do get some "undefined method" errors from VS Code, but they show up in the login method,
and not in the failinf register method:
Here is the controller:
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
class AuthController extends Controller
{
public function register(Request $request)
{
$validatedData = $request->validate([
'name' => 'required|max:55',
'email' => 'email|required|unique:users',
'password' => 'required|confirmed'
]);
$validatedData['password'] = bcrypt($request->password);
$user = User::create($validatedData);
$accessToken = $user->createToken('authToken')->accessToken;
return response([ 'user' => $user, 'access_token' => $accessToken]);
}
public function login(Request $request)
{
$loginData = $request->validate([
'email' => 'email|required',
'password' => 'required'
]);
if (!auth()->attempt($loginData)) {
return response(['message' => 'Invalid Credentials']);
}
$accessToken = auth()->user()->createToken('authToken')->accessToken;
return response(['user' => auth()->user(), 'access_token' => $accessToken]);
}
}
In these are the routes in api.php:
Route::post( 'register', 'App\Http\Controllers\API\AuthController#register');
Route::post( 'login', 'App\Http\Controllers\API\AuthController#login');
// Route::prefix('v2')->group(function(){ // prefix voor versie 2
Route::apiResource('/cards', 'App\Http\Controllers\CardController');
Route::apiResource('/games', 'App\Http\Controllers\GameController');
//Route::get('games', 'App\Http\Controllers\GameController#index')->middleware('auth:api');
//Route::post('games', 'App\Http\Controllers\GameController#store')->middleware('auth:api');
//Route::get('games/{id}', 'App\Http\Controllers\GameController#show')->middleware('auth:api');
//Route::put('games/{id}', 'App\Http\Controllers\GameController#update')->middleware('auth:api');
//Route::delete('games/{id}', 'App\Http\Controllers\GameController#destroy')->middleware('auth:api');
Route::get('/gameByPin/{pin}', 'App\Http\Controllers\GameController#searchPin');
Route::apiResource('/speler', 'App\Http\Controllers\SpelerController');
//});
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
is there anyone who can help troubleshooting this?
Postman does not give me errors, just the redirect to homepage..
Wow,
Thanks Patrick, your the best.
Just checked all the steps in the documentation.
reran the installation command I ran when setting up everything. ( Apperently need to do this twice ?!):
php artisan passport:install
It returned:
Encryption keys already exist. Use the --force option to overwrite them.
Personal access client created successfully.
Client ID: 5
Client secret: ikJEfthxxxxxxxxxxxxxxxxxxxxxxxxxxxBjJ36
Password grant client created successfully.
Client ID: 6
Client secret: H42lpMxxxxxxxxxxxxxxxxxxxxxxxxxxxQGZgH
Now the post request results in a token!
The error VS Code shows d for "use HasApiToken" is:
Undefined type 'Laravel\Passport\HasApiTokens'.intelephense(1009)

Laravel Passport: How can I properly setup a feature test?

I am trying to create a Feature test that tests getting an OAuth access token. When using Postman, everything works great (actual code) so I am confident it is in my test code I am not implementing the setup correctly.
In the past, I have run the artisan command, and simply copy/paste the client_secret into my .env file. Because I am using CI/CD, I cannot do that. I need to either:
create an artisan command to append the .env
read the secret from the oauth_clients table (seems the easiest route)
I've followed this SO thread to help get where I'm at, but not able to make it work.
Here is what my test looks like:
MyFeatureTest.php
use RefreshDatabase, WithFaker, DatabaseMigrations;
public function setUp(): void
{
parent::setUp();
$this->artisan('passport:install');
}
public function a_user_can_get_a_token()
{
$credentials = [
'username' => 'username',
'password' => 'password',
];
$http = $this->postJson('api/v1/login', $credentials);
$response = json_decode($http->getContent());
// dd($response);
$http->assertStatus(200)
->assertJsonStructure([
'token_type', 'expires_in', 'access_token', 'refresh_token',
])
->assertJson([
'token_type' => $response->token_type,
'expires_in' => $response->expires_in,
'access_token' => $response->access_token,
'refresh_token' => $response->refresh_token,
]);
In my Controller that handles the login route:
public function __construct()
{
$this->client = DB::table('oauth_clients')
->where('id', 2)
->first();
}
...
public function getOauthAccessToken($credentials)
{
$response = Http::asForm()->post(env('APP_URL') . '/oauth/token', [
'grant_type' => 'password',
'client_id' => $this->client->id,
'client_secret' => $this->client->secret,
'username' => $credentials['username'],
'password' => $credentials['password'],
'scope' => '*',
]);
if ($response->successful()) {
return $response->json();
} else {
dd($response->body());
}
}
When using postman to test my routes/controller, everything works great. I get a Barer token back as expected.
When I run my Feature test, my controller returns null.
Trying to troubleshoot, I've tried dd($response->body()) above. Here is what I am getting:
"{"error":"invalid_client","error_description":"Client authentication failed","message":"Client authentication failed"}"
If I dd($this->client->secret), I am able to see the key. This is super confusing as it looks like everything is working...
How can I properly set up my test so that passport is ready to go/configured when I hit the login test(s)? Thank you for any suggestions!
Thank you everyone for your suggestions!
This is what ended up working for me. Since I am the only consumer of my api, I don't need to return the entire /oauth/token response (although it might be helpful in the future).
MyTest.php
use RefreshDatabase, WithFaker, DatabaseMigrations;
public function setUp(): void
{
parent::setUp();
$this->artisan('passport:install', ['--no-interaction' => true,]);
}
/** #test */
public function a_user_can_authenticate_using_email()
{
$credentials = [
'username' => 'test#email.com',
'password' => 'password',
];
$http = $this->postJson('api/v1/login', $credentials)
->assertStatus(200);
}
MyController.php
// authenticate the $credentials to get a $user object.
...
return $this->getOauthAccessToken($user);
...
private function getOauthAccessToken($user)
{
$token = $user->createToken('My Personal Access Token')->accessToken;
return response()->json($token);
}
Instead of validating I get the exact json back (would be ideal) I'm just verifying that I'm getting a 200 response back. Because my authentication is somewhat cumbersome (checking multiple db's) it was important to make sure that I was getting through everything with a 200.
Hope this helps someone!

Laravel test fails, but works on postman

I have a test for a user logging out and having their token deleted.
use RefreshDatabase;
public function setUp() :void {
parent::setUp();
\Artisan::call('migrate',['-vvv' => true]);
\Artisan::call('passport:install',['-vvv' => true]);
\Artisan::call('db:seed',['-vvv' => true]);
}
...
/**
* #test
*/
public function a_user_has_tokens_removed_when_logged_out()
{
// login
$this->withoutExceptionHandling();
$user = factory('App\User')->create();
$response = $this->post('/api/login', [
'username' => $user->email,
'password' => 'password'
]);
$token = json_decode($response->getContent())->access_token;
$this->assertTrue(!$user->tokens->isEmpty());
// logout
Passport::actingAs($user, ['*']);
$logout = $this->json('POST', 'api/logout')->withHeaders([
'Accept' => 'application/json',
'Authorization' => 'Bearer ' . $token
]);
$this->assertTrue($user->tokens->isEmpty());
}
First I'm creating a user and logging them in so a token is created and related to their user account.
I'm asserting that the token exists after hitting the login route, which passes.
Then I'm calling the logout route which will delete all the tokens the user has:
public function logout() {
auth()->user()->tokens()->each(function($token, $key) {
$token->delete();
});
return response()->json('Logged out successfully', 200);
}
routes/api.php
Route::middleware('auth:api')->post('logout', 'AuthController#logout');
This assertion on the test above is failing:
$this->assertTrue($user->tokens->isEmpty());
If I do a dd($user->tokens); before the assertion to check what's going on, the token shows up - it still exists.
But If I hit this api/logout route with Postman, which has everything stored in MySQL, all the tokens are being deleted successfully.
I don't understand what's going on and why this test is failing. Or rather, I don't understand why the $token->delete() doesn't work on the test, but does via Postman. What's different?
Before executing the assert, reload the user model relations via $user->fresh(), to ensure the deleted relations are reflected in the instance.
I don't know why, but within the testing context, this is not done automatically.

how to get data from External API url

I am using laravel v5.8.17 and i got this code from this side somewhere but my code is not working properly , i can only get status code 200 but cannot be return the response (body and header) , I tried like this : (at controller)
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use GuzzleHttp\Client;
class yourController extends Controller
{
public function saveApiData(){
if(isset($_res['https://my_api_url'])){
require './vendor/autoload.php';
$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://my_api_url', [
'formData' => [
'email' => 'myemail',
'password' => 'mypassword',
] ]);
echo $res->getStatusCode();
//200
echo $res->getHeader('content-type');
//'application/json; charset=utf8'
echo $res->getBody();
// "type":"User"...'
}
}
}
try to make it like this:
print_r(json_decode($response->getBody()->getContents()));exit(0);

Resources