Symfony2 functional test with Session and Post - ajax

I am trying to write some Symfony2 functional tests, using PHPUnit, that simulate a logged-in user requesting some resource via an AJAX call.
The test first simulates a user logging-in using the standard FOSUserBundle log-in form;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class MyWebTestCase extends WebTestCase {
public function login($username, $password = 'password')
{
$client = static::createClient();
$client->followRedirects(true);
$crawler = $client->request('GET', '/login');
$form = $crawler->selectButton('Login')->form(array(
'_username' => $username,
'_password' => $password,
));
$crawler = $client->submit($form);
return $client;
}
}
Then, the test gets an entity from the database and sets it in a session variable, then sends a POST request to retrieve the requested resource, using some JSON which includes extra information about what is being requested (in the actual application, some JavaScript composes the JSON in response to the user's actions which is then submitted via AJAX);
$this->client = $this->login('user#domain.com', 'password');
// we want element number 6 from the fixtures
$chart = $this->em->getRepository('MyBundle:Element')->find(6);
$guid = $chart->getGuid();
$this->client->getContainer()->get('session')->set($guid, $chart);
$link = sprintf('/viewer/data/%d/%s', 1, $guid);
$crawler = $this->client->request('POST', $link, array(), array(),
array('CONTENT_TYPE' => 'application/json'),
'{"filters":[]}');
When the tests reach this point, an error is generated;
ini_set(): A session is active. You cannot change the session module's ini settings at this time
I'm using Symfony 2.6.5 and PHPUnit 4.5.0

Do you use mock sessions for your test? If not try this.
Config_test.yml
framework:
test: ~
session:
storage_id: session.storage.mock_file
The native session storage is meant to handle a single request per process which may lead to your bug. if its your case.

Related

How do I mock guzzle request

How do I make a mock of third party api call . That is happening from controller . I have this line of code in controller .
public function store(){
$response = $request->post('http://thirdpaty.app/rmis/api/ebp/requests', [
"headers" => [
'Content-Type' => 'application/json',
],
"json" => [
"data"=>1
]
]);
$data = json_decode($response->getBody()->getContents());
$token = $data->token;
// Saving that token to database
}
And from the test I am doing
$response = $this->post('/to-store-method');
How do I mock the api request . So that in testing I don't have to call the third api request .
Right now I am doing
if(app()->get('env') == 'testing'){
$token = 123;
}else{
//Api call here
}
Is there any better alternative of doing this test
You'll need some way of injecting a mock handler into the Guzzle Client your controller is using. Traditionally, you'd either leverage dependency injection by passing the Guzzle Client via the constructor or through some referenced service in that code that you can mock (using Mockery) behind the scenes.
After that, check out the Guzzle documentation for a peak on how to mock requests in the HTTP Client:
http://docs.guzzlephp.org/en/stable/testing.html
You'd use a MockHandler to do something resembling the following code by building a stack of fake requests and responses.
// Create a mock and queue two responses.
$mock = new MockHandler([
new Response(200, ['X-Foo' => 'Bar'], 'Hello, World'),
new Response(202, ['Content-Length' => 0]),
new RequestException('Error Communicating with Server', new Request('GET', 'test'))
]);
$handlerStack = HandlerStack::create($mock);
$client = new Client(['handler' => $handlerStack]);
// The first request is intercepted with the first response.
$response = $client->request('GET', '/');
Actually, it is a bad practice to mock the network libraries. What I would recommend is to wrap the network request by the httpService and mock the httpService instead to return the required response.
public function store(){
$response = httpService.postData();
$data = json_decode($response->getBody()->getContents());
$token = $data->token;
// Saving that token to database
}
So, the you would get the response as return from the httpService.postData function and you can mock the postData instead of the network library.

Laravel Stormpath not able to access User Object

I am using Laravel and Stormpath for User Management. I am able to register and login user successfully using AJAX.
After successful login only the url is returned to AJAX, but after login when I go to User specific pages I am not able to fetch User Data.
Registration and Login happens in RegisterController
User Pages are rendered using UserController
I've tried to get User data using
$user = app('stormpath.user');
in UserController, but when I do dd($user) null is returned.
How to persist or get User Data after successful login or sign-up in other Controllers?
Any help appreciated! Thanks in advance!
For the Stormpath Laravel integration, when you run AJAX calls, we do not set any cookies. We provide you with the JWT in the header response that you will need to look at and then store them youself. The JWT will then need to be attached to all other requests as a Bearer token which will allow you to use the `$user = app('stormpath.user') method to get the user information out of the JWT.
I finally got everything working. Thank you #bretterer
// Stormpath user account creation
\Stormpath\Client::$apiKeyProperties = "apiKey.id="
.env('STORMPATH_CLIENT_APIKEY_ID').
"\napiKey.secret=".env('STORMPATH_CLIENT_APIKEY_SECRET');
$client = \Stormpath\Client::getInstance();
$apps = $client->tenant->applications;
$apps->search = array('name' => 'My Application');
$application = $apps->getIterator()->current();
$account = \Stormpath\Resource\Account::instantiate(
[
'givenName' => $request->input('username'),
'middleName' => '',
'surname' => 'StromTrooper',
'username' => $request->input('username'),
'email' => $request->input('user_mail'),
'password' => $request->input('user_pass'),
'confirmPassword' => $request->input('user_pass')
]
);
// Create User Account and Log-in the User
try
{
$response = $application->createAccount($account);
$passwordGrant = new \Stormpath\Oauth\PasswordGrantRequest(
$request->input('user_mail'),
$request->input('user_pass')
);
$auth = new \Stormpath\Oauth\PasswordGrantAuthenticator($application);
$result = $auth->authenticate($passwordGrant);
$atoken = cookie("access_token",
$result->getAccessTokenString(),
$result->getExpiresIn()
);
$rtoken = cookie("refresh_token",
$result->getRefreshTokenString(),
$result->getExpiresIn()
);
$response_bag['success'] = url('userprofile');
}
catch (\Stormpath\Resource\ResourceError $re)
{
$response_bag['error'] = $re->getMessage();
$atoken = 'null';
$rtoken = 'null';
}
return response()
->json($response_bag)
->withCookie($atoken)
->withCookie($rtoken);
and in the User controller I am able to access the user details using app('stormpath.user');
and since I was using Laravel 5.1
I had to comment out $token = $request->bearerToken(); from vendor/stormpath/laravel/src/Http/Middleware/Authenticate.php from function public function isAuthenticated(Request $request)

Returning Token to Store in Local Storage / Cookie On Login Page Reroute - Laravel 4.2

I have a Laravel app that currently uses the built-in authentication system to determine who is logged in or not logged in. The built-in authentication uses a session based system. I am transitioning to a custom authentication system that uses random tokens stored either in local storage or cookies to determine user identity.
I am currently using the following:
public function auth{
$user = array(
'email' => Input::get('email'),
'password' => Input::get('password'),
'active' => 1
);
if(Auth::attempt($user, true)){
$userId = Auth::id();
$salt = $customGenerator->generateSalt();
$userToken = $customGenerator->generateTokenAndSaveInDB($userId, $salt);
// Can I also sent $userToken to index.welcome to store in localStorage?
return Redirect::route('index.welcome');
}
else{
return Redirect::route('index.failed');
}
}
The above will redirect the user to a specific page if their credentials were valid. I would also like to have $userToken be sent to this new page so I can store it in the client's local storage.
Is this possible with Laravel?
can you try
return Redirect::route('index.welcome', array('userToken' => $userToken));
Laravel Forum

omnipay paypal express not returning address

I am using the omnipay setup here: https://github.com/adrianmacneil/omnipay to process a paypal express checkout.
The process works fine in that the user is redirected to paypal -> they login and choose to pay -> they get returned to my site at which point I capture the payment.
The problem I've got is that I need to capture the address they have entered into paypal as their billing / shipping address.
To send the user across to paypal I have the following:
$gateway = GatewayFactory::create('PayPal_Express');
$gateway->setUsername('XX-USERNAME_XX');
$gateway->setPassword('XX_PASSWORDXX');
$gateway->setSignature('XX_SIG_XX');
$gateway->setTestMode(true);
$response = $gateway->purchase(
array(
'cancelUrl'=>'http://www.XXX.co.uk/',
'returnUrl'=>'http://www.XXX.co.uk/paypalexpress_confirm',
'amount' => $totalamount,
'currency' => 'GBP'
)
)->send();
$response->redirect();
When the user is returned I have the following:
$gateway = GatewayFactory::create('PayPal_Express');
$gateway->setUsername('XX-USERNAME_XX');
$gateway->setPassword('XX_PASSWORDXX');
$gateway->setSignature('XX_SIG_XX');
$gateway->setTestMode(true);
$response = $gateway->completePurchase(
array(
'cancelUrl'=>'http://www.XXX.co.uk/',
'returnUrl'=>'http://www.XXX.co.uk/paypalexpress_confirm',
'amount' => $totalamount,
'currency' => 'GBP'
)
)->send();
echo $responsemsg=$response->getMessage();
echo '<br><br><br>';
$data = $response->getData();
print_r($data);
Nothing in the response message or the raw data contains the customer address.
Has anyone got this working as i'm struggling and it's the final step to complete the transaction.
For those who are trying to get this work it's as Adrian said.
You first do the normal omnipay paypal payment and then afterwards:
get the token you were given
preform a second call to paypal using the call getexpresscheckoutdetails method
this returns all the info you need
API info here: https://cms.paypal.com/uk/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_GetExpressCheckoutDetails
The php script paypal provide to do it all for you:
https://cms.paypal.com/cms_content/ES/es_ES/files/developer/nvp_ECGetExpressCheckout_php.txt
omnipay\paypal\ProGateway.php add new function
public function fetchExpressCheckoutDetail(array $parameters = array())
{
return $this->createRequest('\Omnipay\PayPal\Message\FetchExpressCheckoutRequest', $parameters);
}
omnipay\paypal\src\Message add new file FetchExpressCheckoutRequest.php
namespace Omnipay\PayPal\Message;
class FetchExpressCheckoutRequest extends AbstractRequest
{
public function getData()
{
$data = $this->getBaseData('GetExpressCheckoutDetails');
$this->validate('transactionReference');
$data['TOKEN'] = $this->getTransactionReference();
$url = $this->getEndpoint()."?USER={$data['USER']}&PWD={$data['PWD']}&SIGNATURE={$data['SIGNATURE']}&METHOD=GetExpressCheckoutDetails&VERSION={$data['VERSION']}&TOKEN={$data['TOKEN']}";
parse_str (file_get_contents( $url ),$output);
$data = array_merge($data,$output);
return $data;
}
}
Usage:
$response = $gateway->completePurchase($params)->send();
$data = $response->getData();
$gateway->fetchExpressCheckoutDetail(array('transactionReference'=>$data['TOKEN']))->getData();
It will be not the best. But it works. :)
If it's not returned by the $response->getData() method, you might need to call PayPal's GetExpressCheckoutDetails API method to get the extra details about the transaction.
Omnipay doesn't support this out of the box, so you will probably need to copy and customize one of the existing requests to make a separate API call after you have confirmed payment.

Using Different Session Namespaces with Zend Framework 2 Authentication Component

I'm planning to use ZF2 in a future project, so I'm trying Zend Framework 2 RC1 now. I started with authentication step, and noticed that when i chose a different name than 'Zend_Auth' for session storage namespace, i can't access to object stored in session (AuthenticationService class' hasIdentity method returned false, despite User object data set in session).
<?php
namespace Application\Controller;
use Zend\Authentication\Adapter\DbTable as AuthAdapter;
use Zend\Authentication\AuthenticationService;
use Zend\Authentication\Storage\Session as SessionStorage;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Application\Model\User;
use Application\Form\LoginForm;
class LoginController extends AbstractActionController
{
public function indexAction()
{
$auth = new AuthenticationService();
if ($auth->hasIdentity()) {
return $this->redirect()->toRoute('application');
}
$form = new LoginForm();
return array('form' => $form);
}
public function loginAction()
{
$auth = new AuthenticationService();
$form = new LoginForm();
$form->get('submit')->setAttribute('value', 'Add');
$request = $this->getRequest();
if ($request->isPost()) {
$user = new User();
$form->setInputFilter($user->getInputFilter('login'));
$form->setData($request->getPost());
if ($form->isValid()) {
$data = $form->getData();
// Configure the instance with constructor parameters...
$sm = $this->getServiceLocator();
$dbAdapter = $sm->get('db-adapter');
$authAdapter = new AuthAdapter($dbAdapter, 'users', 'username', 'password');
$authAdapter
->setIdentity($data['username'])
->setCredential(sha1($data['password']));
// Use 'users' instead of 'Zend_Auth'
$auth->setStorage(new SessionStorage('users'));
$result = $auth->authenticate($authAdapter);
if ($result->isValid()) {
// store the identity as an object where only the username and
// real_name have been returned
$storage = $auth->getStorage();
// store the identity as an object where the password column has
// been omitted
$storage->write($authAdapter->getResultRowObject(
null,
'password'
));
// Redirect to list of application
return $this->redirect()->toRoute('application');
}
}
}
// processed if form is not valid
return array('form' => $form);
}
}
In this code, when i changed the below line,
$auth->setStorage(new SessionStorage('users'));
like this:
$auth->setStorage(new SessionStorage());
hasIdentity method returned true.
I checked two classes Zend\Authentication\AuthenticationService and Zend\Authentication\Storage\Session, and didn't see a way to access session data which has different session namespace other than default.
What i need to understand is how can i access session data which has a different namespace and if there is no way to do it for now, should we define this as a bug?
I can update the question if any other information needed.
We are kinda missing one part of your code, the one where you try and receive the user identity. im guessing that you have forgotten to pass the the SessionStorage Object with the same namespace.
Also the configuration of the Authentication object should be moved to a factory so these kind of issues to not arrise.
Thats my five cents atleast :)

Resources