I'm trying to test my api that's made with JWT_auth: https://github.com/tymondesigns/jwt-auth
class UpdateTest extends TestCase
{
use DatabaseTransactions;
public $token;
public function signIn($data = ['email'=>'mail#gmail.com', 'password'=>'secret'])
{
$this->post('api/login', $data);
$content = json_decode($this->response->getContent());
$this->assertObjectHasAttribute('token', $content);
$this->token = $content->token;
return $this;
}
/** #test */
public function a_user_updates_his_account()
{
factory(User::class)->create([
'name' => 'Test',
'last_name' => 'Test',
'email' => 'mail#gmail.com',
'mobile' => '062348383',
'function' => 'ceo',
'about' => 'About me.....',
'corporation_id' => 1
]);
$user = User::first();
$user->active = 2;
$user->save();
$this->signIn();
$url = '/api/user/' . $user->slug . '?token=' . $this->token;
$result = $this->json('GET', $url);
dd($result);
}
}
Result is always:
The token could not be parsed from the request
How do I get this t work!?
Source (https://github.com/tymondesigns/jwt-auth/issues/206)
One way to test your API in this situation is to bypass the actual token verification, but still log your user in (if you need to identify the user). Here is a snippet of the helper method we used in our recent API-based application.
/**
* Simulate call api
*
* #param string $endpoint
* #param array $params
* #param string $asUser
*
* #return mixed
*/
protected function callApi($endpoint, $params = [], $asUser = 'user#example.org')
{
$endpoint = starts_with($endpoint, '/')
? $endpoint
: '/' . $endpoint;
$headers = [];
if (!is_null($asUser)) {
$token = auth()->guard('api')
->login(\Models\User::whereEmail($asUser)->first());
$headers['Authorization'] = 'Bearer ' . $token;
}
return $this->json(
'POST',
'http://api.dev/' . $endpoint,
$params,
$headers
);
}
And is used like this:
$this->callApi('orders/list', [
'type' => 'customers'
])
->seeStatusOk()
Basically, there is not really a way for now. The fake request that is created during testing and is passed to Laravel to handle, somehow drops the token data.
It has alredy been reported in an issue (https://github.com/tymondesigns/jwt-auth/issues/852) but as far as I know, there is no solution yet.
Related
I have this controller that is supposed to perform PayPal payments. The payment function is working well but on getting to success function I am getting an error Illegal string offset 'total' . I am passing $this->productData($request) as suggested in this question. I tried creating a variable $total = $response['AMT'] which is the response from setCheckoutDetails but I still got the same error. How do I go about it?
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Srmklive\PayPal\Services\ExpressCheckout;
class PayPalController extends Controller
{
private function projectData(Request $request){
// dd($request->all());
$item = [];
$datat = array_map(function($item){
return [
'name'=>$request->project_id,
'price'=>$request->budget,
'desc'=>'Deposit',
'qty'=>1
];
}, $item);
$data = [
'items'=>$datat,
'invoice_id' => uniqid(),
'invoice_description' => "Payment for Project No.".$request->project_id." Amount ".$request->budget,
'return_url' => route('payment.success'),
'cancel_url' => route('payment.cancel'),
'total'=>$request->budget
];
// dd($data);
return $data;
}
/**
* Responds with a welcome message with instructions
*
* #return \Illuminate\Http\Response
*/
public function payment(Request $request) {
$data = $this->projectData($request);
$provider = new ExpressCheckout;
$response = $provider->setExpressCheckout($data);
// dd($response);
// $response = $provider->setExpressCheckout($data, true);
return redirect($response['paypal_link']);
}
/**
* Responds with a welcome message with instructions
*
* #return \Illuminate\Http\Response
*/
public function cancel()
{
dd('Your payment is canceled. You can create cancel page here.');
}
/**
* Responds with a welcome message with instructions
*
* #return \Illuminate\Http\Response
*/
public function success(Request $request)
{
$provider = new ExpressCheckout;
$response = $provider->getExpressCheckoutDetails($request->token);
$token = $response['TOKEN'];
$payerId = $response['PAYERID'];
$total = $response['AMT'];
// dd($response);
if (in_array(strtoupper($response['ACK']), ['SUCCESS', 'SUCCESSWITHWARNING'])) {
// dd('Payment successful');
//Performing transaction
$payment_status = $provider->doExpressCheckoutPayment($token, $payerId, $this->projectData($request));
dd($payment_status);
}
dd('Something is wrong.');
}
}
You have to pass three parameters
data, token, PAYERID
Data can service information like
$data = array(
'total' => Total amount,
'invoice_id' => Invoicen number,
'invoice_description' => invoice descrption
);
And items as well which will contain name, price, desc and qty
I have the following 2 codes from library https://github.com/javiertelioz/mercadolibre to connect with MercadoLibre's API:
Class Meli.php:
<?php
namespace App\Sources;
use App\Sources\MercadoLibre\Utils;
class Meli extends Utils {
/**
* #version 1.0.0
*/
const VERSION = "1.0.0";
/**
* Configuration for urls
*/
protected $urls = array(
'API_ROOT_URL' => 'https://api.mercadolibre.com',
'AUTH_URL' => 'http://auth.mercadolibre.com.ar/authorization',
'OAUTH_URL' => '/oauth/token'
);
/**
* Configuration for CURL
*/
protected $curl_opts = array(
CURLOPT_USERAGENT => "MELI-PHP-SDK-1.0.0",
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_TIMEOUT => 60
);
protected $client_id;
protected $client_secret;
/**
* Constructor method. Set all variables to connect in Meli
*
* #param string $client_id
* #param string $client_secret
* #param string $access_token
*/
public function __construct($client_id, $client_secret, $urls = null, $curl_opts = null) {
$this->client_id = $client_id;
$this->client_secret = $client_secret;
$this->urls = $urls ? $urls : $this->urls;
$this->curl_opts = $curl_opts ? $curl_opts : $this->curl_opts;
}
/**
* Return an string with a complete Meli login url.
*
* #param string $redirect_uri
* #return string
*/
public function getAuthUrl($redirect_uri) {
$params = array("client_id" => $this->client_id, "response_type" => "code", "redirect_uri" => $redirect_uri);
$auth_uri = $this->urls['AUTH_URL'] . "?" . http_build_query($params);
return $auth_uri;
}
}
and the Controller MeliController.php with the following code:
class MeliController extends Controller
{
/**
* Login Page (Mercado Libre)
*/
public function login() {
session()->regenerate();
return view('auth/melilogin')->with('auth', [
'url' => meli::getAuthUrl(env('ML_AUTHENTICATION_URL', '')),
]);
}
public function logout() {
if(session('profile')) {
session()->forget('profile');
session()->flush();
}
return \Redirect::to('/auth/melilogin');
}
}
But Im receiving error:
Non-static method App\Sources\Meli::getAuthUrl() should not be called
statically
Three procedures I made with no success:
1- using facade (meli) as in the first example
meli::getAuthUrl
2- replacing code:
public function getAuthUrl($redirect_uri) {
$params = array("client_id" => $this->client_id, "response_type" => "code", "redirect_uri" => $redirect_uri);
$auth_uri = $this->urls['AUTH_URL'] . "?" . http_build_query($params);
return $auth_uri;
}
}
with public static function and $self instead of $this but with no success.
3- Making the call dynamic using:
'url' => (new \App\Sources\Meli)->getAuthUrl(env('ML_AUTHENTICATION_URL', '')),
But receiving error
Too few arguments to function App\Sources\Meli::__construct(), 0
passed in
/Applications/MAMP/htdocs/price2b/app/Http/Controllers/MeliController.php
any help appreciated.
The error tells you the problem: you are calling the method statically (meli::getAuthUrl(...)), but it's not a static method. You have to call it on an instance of the class. This means that your third approach:
'url' => (new \App\Sources\Meli)->getAuthUrl(env('ML_AUTHENTICATION_URL', '')),
is the right one.
But, as you pointed out, you get a "too few arguments" error. This is because you are passing no arguments when you instantiate the Meli class. That is, new \App\Sources\Meli is equivalent to new \App\Sources\Meli(), passing zero arguments to the constructor.
But the constructor for the Meli class, which you posted above, looks like this:
public function __construct($client_id, $client_secret, $urls = null, $curl_opts = null)
So, you need to pass at least 2 arguments, not zero. In other words, at a minimum, something like this:
'url' => (new \App\Sources\Meli($someClientId, $someClientSecret))->getAuthUrl(env('ML_AUTHENTICATION_URL', '')),
I wonder how to write proper unit test for my email sending method. It's a problem because inside method I get data from Auth object. Should I send id of user in Request?
public function sendGroupInvite(Request $request){
foreach ($request->get('data') as $item){
$invitations = new \App\Models\Invitations();
$invitations->user_id = Auth::id();
$invitations->name = $item["name"];
$invitations->email = $item["email"];
$invitations->status = 0;
$invitations->token = \UUID::getToken(20);
$invitations->username = Auth::user()->name;
$invitations->save();
$settings = UserSettings::where('user_id', Auth::id())->first();
$email = $item["email"];
$url = 'https://example.com/invite/accept/'.$invitations->token;
$urlreject = 'https://example.com/invite/reject/'.$invitations->token;
$mailproperties = ['token' => $invitations->token,
'name' => $invitations->name,
'url' => $url,
'email' => $email,
'urlreject' => $urlreject,
'userid' => Auth::id(),
'username' => Auth::user()->name,
'user_name' => $settings->name,
'user_lastname' => $settings->lastname,
'user_link' => $settings->user_link,
];
$this->dispatch(new SendMail(new Invitations($mailproperties)));
}
return json_encode(array('msg' => 'ok'));
}
I'm using Auth to get username and user id. When I testing it it's not works, because Auth it's null.
I would go with mocking the queue, something similar to this. Mock Documentation
class MailTester extends TestCase{
/**
* #test
*/
public function test_mail(){
Queue::fake();
// call your api or method
Queue::assertPushed(SendMail, function(SendMail $job) {
return $job->something = $yourProperties;
});
}
You could try "acting as" to deal with the Auth::user().
...
class MyControllerTest extends TestCase{
/**
* #test
*/
public function foo(){
$user = App\Users::find(env('TEST_USER_ID')); //from phpunit.xml
$route = route('foo-route');
$post = ['foo' => 'bar'];
$this->actingAs($user)//a second param is opitonal here for api
->post($route, $post)
->assertStatus(200);
}
}
Since twitter has switched to streaming his API, we have to collect the data by ourselves.
How we can do it using GNIP API in php?
Answering this, I’ve just wanted to ensure that I’ve done everything right and maybe to improve my TwitterGnipClient class with your help.
But if there are no answers, let it be FAQ style question.
Main methods see below:
/**
* Add rules to PowerTrack stream’s ruleset.
* #example ['id' => 'url', ...]
* #param array $data
*/
public function addRules(array $data)
{
$rules = [];
foreach ($data as $id => $url) {
$rules[] = $this->buildRuleForUrl($url, $id);
}
$this->httpClient->post($this->getRulesUrl(), [
'auth' => [$this->getUser(), $this->getPassword()],
'json' => ['rules' => $rules]
]);
}
/**
* Retrieves all existing rules for a stream.
* #return \Generator
*/
public function getRules()
{
$response = $this->httpClient->get($this->getRulesUrl(), [
'auth' => [$this->getUser(), $this->getPassword()]
]);
$batchStr = '';
$body = $response->getBody();
while (!$body->eof()) {
$batchStr .= $body->read(1024);
}
$batchArray = explode(PHP_EOL, $batchStr);
unset($batchStr);
foreach ($batchArray as $itemJson) {
yield $this->unpackJson($itemJson);
}
}
/**
* Removes the specified rules from the stream.
* #param $data
*/
public function deleteRules($data)
{
$rules = [];
foreach ($data as $id => $url) {
$rules[] = $this->buildRuleForUrl($url, $id);
}
$this->httpClient->delete($this->getRulesUrl(), [
'auth' => [$this->getUser(), $this->getPassword()],
'json' => ['rules' => array_values($rules)]
]);
}
/**
* Open stream through which the social data will be delivered.
* #return \Generator
*/
public function listenStream()
{
$response = $this->httpClient->get($this->getStreamUrl(), [
'auth' => [$this->getUser(), $this->getPassword()],
'stream' => true
]);
$batchStr = '';
$body = $response->getBody();
while (!$body->eof()) {
$batchStr .= $body->read(1024);
$batchArray = explode(PHP_EOL, $batchStr);
// leave the last piece of response as it can be incomplete
$batchStr = array_pop($batchArray);
foreach ($batchArray as $itemJson) {
yield $this->processBroadcastItem($this->unpackJson($itemJson));
}
}
$body->close();
}
/**
* Process broadcast item data
* #param $data
* #return array
*/
protected function processBroadcastItem($data)
{
if (is_array($data)) {
$url = str_replace('url_contains:', '', $data['gnip']['matching_rules'][0]['value']);
switch ($data['verb']) {
// Occurs when a user posts a new Tweet.
case 'post':
return $this->getMappedResponse($url, 'tweet', 1);
break;
// Occurs when a user Retweets another user's Tweet
case 'share':
return $this->getMappedResponse($url, 'retweet', $data['retweetCount']);
break;
}
}
return [];
}
All class I shared as Gist - here
P.S. If you see an evident issue - comment it please.
I'm receiving the below error when running my unit tests. Seems that it doesn't like passing in the Input::get to the constructor, however when running the script within the browser the action works fine so I know it's not the controller code. If I take out any of the 'task_update' code the test passes with just the find even with the Input - so not sure why it accepts the Input for one method.
ErrorException: Argument 1 passed to Illuminate\Database\Eloquent\Model::__construct() must be of the type array, null given, called
My Controller is:
public function store()
{
$task_update = new TaskUpdate(Input::get('tasks_updates'));
$task = $this->task->find(Input::get('tasks_updates')['task_id']);
$output = $task->taskUpdate()->save($task_update);
if (!!$output->id) {
return Redirect::route('tasks.show', $output->task_id)
->with('flash_task_update', 'Task has been updated');
}
}
And the test is - I'm setting the input for task_updates array but just isn't being picked up:
Input::replace(['tasks_updates' => array('description' => 'Hello')]);
$mockClass = $this->mock;
$mockClass->task_id = 1;
$this->mock->shouldReceive('save')
->once()
->andReturn($mockClass);
$response = $this->call('POST', 'tasksUpdates');
$this->assertRedirectedToRoute('tasks.show', 1);
$this->assertSessionHas('flash_task_update');
I believe the "call" function is blowing away the work done by Input::replace.
the call function can actually take a $parameters parameter which should fix your problem.
if you look in \Illuminate\Foundation\Testing\TestCase#call, you'll see the function:
/**
* Call the given URI and return the Response.
*
* #param string $method
* #param string $uri
* #param array $parameters
* #param array $files
* #param array $server
* #param string $content
* #param bool $changeHistory
* #return \Illuminate\Http\Response
*/
public function call()
{
call_user_func_array(array($this->client, 'request'), func_get_args());
return $this->client->getResponse();
}
If you do:
$response = $this->call('POST', 'tasksUpdates', array('your data here'));
I think it should work.
I do prefer to do both Input::replace($input) and $this->call('POST', 'path', $input).
Example AuthControllerTest.php:
public function testStoreSuccess()
{
$input = array(
'email' => 'email#gmail.com',
'password' => 'password',
'remember' => true
);
// Input::replace($input) can be used for testing any method which
// directly gets the parameters from Input class
Input::replace($input);
// Here the Auth::attempt gets the parameters from Input class
Auth::shouldReceive('attempt')
->with(
array(
'email' => Input::get('email'),
'password' => Input::get('password')
),
Input::get('remember'))
->once()
->andReturn(true);
// guarantee we have passed $input data via call this route
$response = $this->call('POST', 'api/v1/login', $input);
$content = $response->getContent();
$data = json_decode($response->getContent());
//... assertions
}