symfony 2 and phpunit crawler client does not reach ngnix - php

I have written this simple test:
<?php
namespace Hello\ApiBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class HelloControllerTest extends WebTestCase {
public function test() {
$client = static::createClient();
$crawler = $client->request("GET","http://localhost/hello");
$response = $client->getResponse()->getStatusCode();
var_dump($response);
}
}
?>
When I am running this test, it prints a 404 status code.
Oddly, I don't see the request on ngninx access log. Even if I change the URL to "/hello" it still looks like the request isn't reaching the local webserver.
Needless to say it works if I just open Chrome and try this URL (http://localhost/hello) normally.
What am I missing?

It's because Symfony's test framework actually only simulates request (and run dispatcher directly behind). It doesn't send a real request.
When you are testing your app, use relative paths:
$crawler = $client->request("GET","/hello");

Related

Using reactive PHP in a blocking application

I'm currently working on a PHP application that will be using some websocket connections to talk to another service.
To talk to this websocket service, we are using Ratchet - which is a PHP library based on react PHP.
This piece of code needs to send and respond to a couple of requests, and after that, should return the information to the "main thread".
Example flow:
HTTP request -> controller -> Starts a service which opens a websocket client -> websocket client is talking to server -> once its done it should return the outcome to the controller code -> controller outputs to user
The issue I'm having is that I'm not familiar with Reactive PHP and am not sure how to handle this.
I've tried;
$service = new WebsocketService();
$startTimer = time();
$service->getList(44);
while($service->getResponse() == null) {
usleep(500);
if (time() > $startTimer + 10) {
continue; //Timeout on 10 seconds
}
}
var_dump($service->getResponse());
The service code would set its "response" variable to something other than null once its done. This obviously fails, because the sleep method is blocking the thread. Also without, it seems like the while loop is blocking I/O and the reactive code fails.
A solution would be to open up a new thread and run the websocket code there, but I wouldn't be happy with that.
I feel like I need to implement some sort of "watcher" around the websocket process, but I'm not sure how to do that.
Our Websocket service client code looks like this;
private $response = null;
/**
* #return null|object
*/
public function getResponse() {
return $this->response;
}
public function getList($accountId) {
$this->response = null;
\Ratchet\Client\connect('ws://192.168.56.1:8080')->then(function(\Ratchet\Client\WebSocket $conn) use ($accountId) {
$login = new \stdClass();
$login->action = 'login';
$conn->on('message', function($msg) use ($conn, $login, $accountId) {
try {
$response = json_decode($msg);
if ($response->result_id == 100) {
//Succesfully logged in to websocket server
//Do our request now.
$message = new \stdClass();
$message->target = 'test';
$conn->send(json_encode($message));
}
if (isset($response->reply) && $response->reply == 'list') {
$this->response = $response; //This is the content I need returned in the controller
$conn->close(); //Dont need it anymore
}
} catch (\Exception $e) {
echo 'response exception!';
//Do nothing for now
}
});
$conn->send(json_encode($login));
}, function ($e) {
echo "Could not connect: {$e->getMessage()}\n";
});
}
Running the code like this also does not work;
$service = new WebsocketService();
$service->getList(44);
echo 'Test';
var_dump($service->getResponse());
because the "test" echo comes before I even get a response from the websocket server.
Please, enlighten me! I'm not sure what to search for.
PHP and websockets still seem to be a bit experimental. Nevertheless I have found a great tutorial on medium.com, written by Adam Winnipass which should be really helpful for solving your problem: https://medium.com/#winni4eva/php-websockets-with-ratchet-5e76bacd7548
The only difference is that they are implementing their websocket client with JavaScript instead of PHP. But in the end there should not be much of a difference, because as soon as we have opened the Websocket connection of each end both applications have to send and also wait to receive notifications - this is how they illustrate it:
Seems like one possibility to create a successful Websocket connection is to extend the MessageComponentInterface
use Ratchet\MessageComponentInterface;
which also requires
use Ratchet\ConnectionInterface;
The message component interface defines the following methods:
onOpen
onMessage
onClose
onError
And I think this is how the Ratchet library is implementing it. This is how they are finally starting their server:
use Ratchet\Server\IoServer;
use MyApp\MyCustomMessageComponentInterface;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
require dirname(__DIR__) . '/vendor/autoload.php';
$server = IoServer::factory(
new HttpServer(
new WsServer(
new MyCustomMessageComponentInterface()
)
),
8080
);
$server->run();
With this architecture you already can receive (onMessage) and sending is also possible with the send() method.
I can not solve the exact problem with your existing code. But I guess if you are using the pre-built classes and interfaces of the library as intended (and demonstrated here) you should be able to achieve what you want by adding your code to the corresponding methods.
More information and examples can be found in the docs:
http://socketo.me/docs/server
http://socketo.me/api/namespace-Ratchet.html
Are you extending class with WsServer, This might be issue, if you are getting fatal errors. I am not sure whether you are getting fatal errors or warnings. Also i notice the public function onOpen() opens a connection. Please try referring this document http://socketo.me/api/class-Ratchet.WebSocket.WsServer.html might be useful.

Create a manual symfony 2 request and response object from a command

I'm attempting to write a symfony 2 command which can basically read in a number of routes (either through a yml file or through arguments) and can go and get the response for each of these pages so I can report back whether they came back as 200/404/502 etc..
The routes are relative so would be routes such as '/' and '/news'.
Can't seem to work out how to send these requests through to get a real response, I can use Request::create() to create a request, but this doesn't seem to work how I want it to.
Do I have to go through the Kernel even though its a command? Any help would be appreciated.
What I have so far:
$request = Request::create('/news', 'GET');
$response = new Response();
$response->prepare($request);
$res = $response->send();
var_dump($res->getContent());
This comes back with an empty string all the time.
Also tried the following:
$client = new Client(
new HttpKernel(new EventDispatcher(), new ControllerResolver())
);
$client->request('GET', '/news');
var_dump($client->getResponse());
Which comes back with a route is wrongly configured error
Thanks
Kevin
According to your comment:
Basically im testing the code base to ensure its all set up ok before I release it to the public, so I want to simulate a client request to the code and ensure I get a 200 response back, if a 404 came back then I know there is a problem so to pause the release.
This is easy to do with a test:
<?php
// tests/AppBundle/Controller/PostControllerTest.php
namespace Tests\AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class PostControllerTest extends WebTestCase
{
public function testNews()
{
$client = static::createClient();
$crawler = $client->request('GET', '/news');
// Assert a specific 200 status code
$this->assertEquals(
200, // or Symfony\Component\HttpFoundation\Response::HTTP_OK
$client->getResponse()->getStatusCode()
);
}
}
You also need to install PHPUnit:
composer require --dev "phpunit/phpunit=5.4.*"
Then you can launch the tests:
php ./vendor/bin/phpunit -c app/phpunit.xml.dist
You'll have a result like this:
PHPUnit 5.4.6 by Sebastian Bergmann and contributors.
..................... 21 / 21 (100%)
Time: 5.29 seconds, Memory: 58.00MB
OK (21 tests, 149 assertions)
The best way to do it is using an http client to which transfer the responsibility to create a request object, perform the request, and return a response.
One of the most used is the guzzle http client (http://docs.guzzlephp.org/en/latest/)
So in your command you can get the router from the container, generate the url for that, and perform the request with guzzle client.
EDIT after comments:
To reach that goal to perform request without actually have a server running, you can use \Symfony\Bundle\FrameworkBundle\Client object that uses the kernel to perform request-response ( you have of course to inject the current kernel of your command). Pretty much as the WebTestCase class does.
http://api.symfony.com/2.8/Symfony/Bundle/FrameworkBundle/Client.html
Hope this will help ! (I will update with some code later)

Zend setting the authorization header for unit testing using PHPUnit

Recently I tried to test my REST API's using PHPUnit.
I am facing problem to send http authorization header for my test case.
Every time I do that I get an 403 response instead of 200
Here is my code :
<?php
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
use Zend\Http\Request;
use Zend\Http\Headers;
use Zend\Http\Response;
class TrialTest extends AbstractHttpControllerTestCase
{
protected $traceError = true;
public function setUp()
{
$this->setApplicationConfig(
include 'config/application.config.php'
);
parent::setUp();
}
public function testAction()
{
$this->request = new Request();
$this->getRequest()->setMethod('GET');
//$headers = new \Zend\Http\Headers;
//$header = $headers->addHeader($headers->fromString('Authorization:Bearer test'));
$this->getRequest()->sendHeaders('Authorization:Bearer test');
//var_dump($headers);
//$this->getRequest()->setHeaders($header);
$this->dispatch('/campaign');
$this->assertResponseStatusCode(200);
}
}
Kindly help !! where am I going wrong ?
Try setting your headers like this:
$headers = new \Zend\Http\Headers;
$headers->addHeaderLine('Authorization', 'Bearer test');
$this->request->setHeaders($headers);
And you have to make sure that test a valid OAuth token otherwise it will never work. I am not so sure if a 4 character token will ever validate correctly...
UPDATE
I think there is a general problem with your test design. You only set the request object in the controller instance, but the service taking care of authentication has no access to this request object and thus it will not authorize the request correctly.
If you write a controller test in which you test the route '/campaign' you should only test the controller functionality and set mocks for all dependencies. I think the main problem starts in your setUp method. To test this controller you should not load your whole application.config.php. You should set an MvcEvent instance and attach all you need to this event (the correct Router instance, etc) and then dispatch the controller.
Check a proper example of such a ZF2 controller test here.
Testing your OAuth module should happen in an independent test.

Zend Framework Call to undefined method Zend\Http\Request::getServer()

I have a controller, that when request is Post, it gets the request, and from there I get REMOTE_ADDR, and REQUEST_TIME. The code works just fine, I get that information that I need.
However, I am writing an integration test for the entire flow of my web app, and when I send the request, I get Call to undefined method Zend\Http\Request::getServer() when it gets to that point of my action in the controller.
$server = $this->getRequest()->getServer();
$remoteAddr = $server['REMOTE_ADDR'];
$timestamp = $server['REQUEST_TIME'];
When I do
$request = $this->getRequest();
and look at $request, it has method, uri, queryParams, postParams, fileParams, version, headers, metadata and content.
postData has everything I'm sending via my test, but it crashes when it gets to the point of getting the server.
Any ideas?
Thank you.
The request class your application is using is Zend\Http\PhpEnvironment\Request, which extends Zend\Http\Request with some PHP-specific stuff like getServer(). Change your test to use that and it should work fine.

SOAP Client Error: "Error Fetching Http Headers"

I am trying to use a SOAP Client-Server in my computer and it doesn't look like it is going to work, I am getting this error Error Fetching Http Headers when I try to run my SOAP Client.
I have been looking and the solution that I have encountred is to increase the default_socket_timeout from 60 to 120 seconds and it doesn't work for me, also I have seen another solution that is putting the vhost in my apache KeepAlive Off and that didn't work.
The WSDL is working fine because I try to use it in another computer and it work.
I am running PHP Version 5.3.5-1ubuntu7.4 in Linux Mint using Zend Framework, I hope some of you can help me fix this thank you.
I'm sorry but I don't know what you are using to set up your SOAP service.....
If you can give more information about your SOAP service (poss Zend_Soap given the Zend Framework tag) etc that would be great.
Also, as a quick alternative, you say you've looked at the WSDL on another computer, perhaps try the application in an alternative environment to ensure it's not an environment issue.
May be a simple issue with your client-server code.
UPDATE: Ok so I realised the example I mentioned yesterday wasn't fully implemented so I've hacked something together quickly that you can try to see if it works in your environment.
The code is a mix of something I found here (an example of Zend_Soap_Server) and something from another SO question here (an example of a basic SOAP service test).
I've tested it at my end using ZF 1.11 and the example I'm outlining uses the default Application path you get with a new ZF project (e.g models are in directory application/models so the model shown is headed up Application_Model_Classname).
If it works, you can tweak accordingly....if it doesn't work we can try something else.
Start by creating a new SOAP controller and set the class up like this:
<?php
class SoapController extends Zend_Controller_Action
{
public function init()
{
ini_set("soap.wsdl_cache_enabled", "0"); //disable WSDL caching
$this->_helper->layout()->disableLayout(); //disable the layout
$this->_helper->viewRenderer->setNoRender(); //disable the view
}
public function indexAction ()
{
if (isset($_GET['wsdl'])) {
//return the WSDL
$this->handleWSDL();
} else {
//handle SOAP request
$this->handleSOAP();
}
}
private function handleWSDL ()
{
$strategy = new Zend_Soap_Wsdl_Strategy_AnyType();
$autodiscover = new Zend_Soap_AutoDiscover();
$autodiscover->setComplexTypeStrategy($strategy);
$autodiscover->setClass('Application_Model_SoapService');
$autodiscover->handle();
}
private function handleSOAP ()
{
$server = new Zend_Soap_Server(null,
array('uri' => "http://YOURDOMAIN/soap?wsdl"));
$server->setClass("Application_Model_SoapService");
$server->handle();
}
public function testAction()
{
$client = new Zend_Soap_Client("http://YOURDOMAIN/soap?wsdl");
try {
echo $client->testMethod('test');
} catch (Exception $e) {
echo $e;
}
}
}
In the class above, the WSDL is automatically generated using Zend_Soap_Autodiscover with a SoapService.php file at application/models/SoapService.php used as the template. Note the DocBock comments above each method in your target class are integral to this process.
Next create the SoapService.php file in the default models folder:
<?php
class Application_Model_SoapService
{
/**
* testMethod
*
* #param string $string
* #return string $testSuccess
*/
public function testMethod(string $string)
{
$testSuccess = 'Test successful, the message was: ' . $string;
return $testSuccess;
}
}
If all is working as it should be you can visit:
http://YOURDOMAIN/soap?wsdl
to see the WSDL and visit:
http://YOURDOMAIN/soap/test
to get a success message with the string you specified in the client request within the testAction() code in the SoapController class as part of the message.
Let me know if it's working or not and we can go from there.
I'll be able to have another look on Monday.

Categories