How to make https requests in symfony2 functional test? - php

Hi i am using phpunit for testing and Symfony\Bundle\FrameworkBundle\Test\WebTestCase for unit testing. So far there were no problem but now we start to use https and my tests are not working anymore.I start to get 301 response code for my every request in my tests. My question is how do i tell to Symfony\Component\HttpKernel\Client to make requests to
https://localhost.com/uri instead of http://localhost.com/uri ?
EDIT
In symfony website http://symfony.com/doc/current/book/testing.html they show how to configure server parameters and there is a code peace like
$client->request(
'GET',
'/demo/hello/Fabien',
array(),
array(),
array(
'CONTENT_TYPE' => 'application/json',
'HTTP_REFERER' => '/foo/bar',
'HTTP_X-Requested-With' => 'XMLHttpRequest',
)
);
I tried to give HTTPS element as mentioned in http://php.net/manual/en/reserved.variables.server.php changed my code as
$client->request('GET',
'/'.$version.'/agencies/'.$agencyId,
array(),
array(),
array('HTTPS' => 'on')
);
However, it is still not working?

Thanks to #WouterJ i changed my client creation from :
static::createClient();
to:
static::createClient(array(),array('HTTPS' => true));
it solved my problem.
It turns out that I cant give HTTP_HOST and HTTPS parameters in client ->request. It should be determined while client creation.

I am using Symfony4 and this how I created my functional test. I have tested following code and it works fine.
namespace App\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class CategoryControllerTest extends WebTestCase
{
public function testList()
{
$client = static::createClient();
$client->request('GET', '/category/list');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
}
}

Related

Getting "SignatureDoesNotMatch" when using GuzzleHttp to follow a redirect to a pre-signed S3 URL

I am consuming an API which receives a POST request with some parameters and uses them to generate a file in S3. This API returns a 303 redirect response with Location: set to the signed URL to access the S3 file.
This works file when accessing the API via e.g. Postman however when accessing it via GuzzleHttp (v7.4) it fails with error SignatureDoesNotMatch.
While debugging I have used code:
$client = new Client([
'allow_redirects' => true,
'on_stats' => function (TransferStats $stats) {
var_dump($stats->getEffectiveUri());
}
]);
$client->post('https://api.example.com', [
'json' => [ ... ]
]);
Doing this I have confirmed that the URL is correct and copy/pasting the exact url that is accessed actually works. However using GuzzleHttp it does not work at all.
Update: The API developer has informed me that this issue was also because they were using v2 of the AWS S3 signature. They have now changed it to v4 which makes my code work as is (I think they may have had the same issues from other clients).
The issue turned out to be that when Guzzle follows redirects, it retains most of the original request headers. However the Amazon computed signature also validates the signature against (at least some of) the headers. The offending header was the Content-Type header which was still sent even though the request no longer had any content.
To fix this I created a new Guzzle middleware:
use Psr\Http\Message\RequestInterface;
class RemoveContentType {
public function __invoke(callable $handler) {
return function (RequestInterface $request, array $options) use ($handler) {
if ($request->getHeaderLine('Content-Type')
&& $request->getBody()->getSize() === 0) {
return $handler($request->withoutHeader('Content-Type'), $options);
}
return $handler($request, $options);
};
}
}
this would remove the content type from a request which has an empty body.
I then modified my code to use this middleware:
$stack = HandlerStack::create();
$stack->push(new RemoveContentType ());
$client = new Client([
'handler' => $stack,
'allow_redirects' => true,
]);
$client->post('https://api.example.com', [
'json' => [ ... ]
]);
This finally solved my issue.

Absolute URL fails in Symfony2 WebCaseTest

The following code always results in no Route found. But the Route does exist.
$client = static::createClient();
$crawler = $client->request(
'GET',
'/app_dev.php/admin/',
array(),
array(),
array("HTTP_HOST" => "dev.example:8080"));
But always fails. If I go to http://dev.example:8080/app_dev.php/admin/ in my browser then it works fine.
Its like PHPUnit cannot see that host?
$crawler->request() should not receive an actual URI, but the part after the front controller. So in your case, use:
$client = static::createClient();
$crawler = $client->request(
'GET',
'/admin/',
array(),
array(),
array("HTTP_HOST" => "dev.example:8080"));
The reason behind this is that the client doesn't actually request for a page (to a host). It just simulates a request, by creating a Request object, passing it to the AppKernel and then parsing the Response. This is much faster.
If you want to test using a real request, I recommend installing and using Mink or Goutte.

How do I use the Symfony DomCrawler with Laravel's subdomain routing?

Currently my site is designed to serve up multiple subdomains like this:
Route::group(array('domain' => 'site1'.AppHelper::getDomain()), function(){
Route::get('{state?}/{variant?}', array("uses" => 'Site1Controller#showIndex'));
});
Route::group(array('domain' => 'site2'.AppHelper::getDomain()), function(){
Route::get('{state?}/{variant?}', array("uses" => 'Site2Controller#showIndex'));
});
Route::group(array('domain' => 'site3'.AppHelper::getDomain()), function(){
Route::get('{state?}/{variant?}', array("uses" => 'Site3Controller#showIndex'));
});
Now I want to write some tests that crawl these pages checking for specific content. Unfortunately, the only documentation I can find on the Symfony DomCrawler for subdomains tells me to do this (let's say this is on line 312 of my test):
$crawler = $this->$client->request('GET', '/', array(), array(), array(
'HTTP_HOST' => 'site1.domain.dev',
'HTTP_USER_AGENT' => 'Symfony/2.0',
));
Unfortunately when I run PHPUnit and it gets to this test I see this:
1) SiteTest::testSite1ForContent
Symfony\Component\HttpKernel\Exception\NotFoundHttpException:
/var/www/vendor/laravel/framework/src/Illuminate/Routing/Router.php:1429
/var/www/vendor/laravel/framework/src/Illuminate/Routing/Router.php:1050
/var/www/vendor/laravel/framework/src/Illuminate/Routing/Router.php:1014
/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Application.php:576
/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Application.php:597
/var/www/vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Client.php:81
/var/www/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Client.php:339
/var/www/app/tests/SiteTest.php:312
So what is it that I'm not understanding here? The site1.domain.dev definitely resolves normally and has no issues that I can see. It seems to be a problem specific to trying to force the header for the subdomain.
Even apart from a solution, how do I debug this properly? I want to figure out why it's not working/where it's failing since this is my first attempt at writing tests.
I started a blank project that just tried to handle this with subdomain routes and it worked perfectly. I realized the issue had to be with what I wrote.
Turns out the issue was actually with a helper I wrote to retrieve the domain. It was trying to access $_SERVER vars that weren't available to the tests when running. Having that return a dev domain if it couldn't retrieve the values solved my issue.
Try this one:
public function refreshApplication()
{
parent::refreshApplication();
$this->client = $this->createClient(
array('HTTP_HOST' => 'site1.domain.dev'));
}
public function testSomething()
{
$crawler = $this->client->request('GET', '/something');
}

How to use Goutte without cookie

How to use goutte but don't send cookies back to the server?
I want to do that because the server can manage sessionid in the URL.
I end up using guzzle and browserkit directly without using goutte. Guzzle let you chose to manage or not cookies http://guzzle.readthedocs.org/en/latest/quickstart.html#cookies.
This solved this problem amount others.
If really you like goutte, I guess you can also delete cookie between each requests.
The only way I see is to instantiate the GuzzleClient yourself and pass it to the Goutte client.
Like this:
use Goutte\Client as GoutteClient;
use GuzzleHttp\Client as GuzzleClient;
$guzzleClient = new GuzzleClient(array('defaults' => array(
'allow_redirects' => false,
'cookies' => false
));
$client = new GoutteClient();
$client->setClient($guzzleClient);
$client->request('GET', 'http://example.org');

Symfony2: Unable to simulate HTTP authentication in functional test

I'm trying to use the following technique described on symfony.com : http://symfony.com/doc/current/cookbook/testing/http_authentication.html
in an attempt to functionally test a controller that requires a user to be logged in.
So far, my login form is working, I can login, and the Symfony2 debug web toolbar is showing my user as authenticated. Also, I have already written a functional test for the login process itself, this passes. So I now by two scenarios, that my login is working.
The only problem I'm having, is when trying to simulate HTTP authentication like so for other controllers:
$client = static::createClient(array(), array(
'PHP_AUTH_USER' => 'tester',
'PHP_AUTH_PW' => 'testpass',
));
I can see by inspecting the $client, that I am being redirected to my login page, the moment I try something like this:
$crawler = $client->request('GET', '/');
I know that the user tester with password testpass exists in the DB, as I can login with that account via browser as well.
I can use the following code from the security controller test:
$client = $this->createClient(array(), array('HTTP_HOST' => 'myapp.test'));
// Visit user login page and login
$crawler = $client->request('GET', '/login');
$form = $crawler->selectButton('Login')->form();
$crawler = $client->submit($form, array('_username' => 'tester', '_password' => 'testpass'));
// ... carry on with remainder of tests
but I'm not too sure if this is the most efficient way to do this.
I'm a bit stunned as to what is wrong. Any ideas? Was there a change applied to Symfony2 that means that this process has changed and the HTTP authentication simulation now doesn't work or works differently?
Thinking about it, I might just do the login using the following setUp method:
public function setUp()
{
// Perform user login.
$this->client = $this->createClient(array(), array('HTTP_HOST' => 'scire.test'));
$crawler = $this->client->request('GET', '/login');
$form = $crawler->selectButton('Login')->form();
$this->client->submit($form, array('_username' => 'tester', '_password' => 'tester'));
}
The HTTP authentication won't work here, unless I alter my config_test.yml with some security setup to allow HTTP authentication.
Note to self: HTTP authentication is different from using a Doctrine user provider!!!
Send your request like this:
<?php
$client->request(
'GET',
'/',
array(),
array(),
array('PHP_AUTH_USER' => 'username', 'PHP_AUTH_PW' => 'pa$$word')
);
This actually sends the information as real authentication header information.

Categories