1
votes

I am currently working on unit testing my symfony 2.8 based admin area. So I wrote a small basic test for the dashboard: The tests checks that

a) If the user is currently not logged in, there should be a redirect to the login page.

b) If the user is logged in, the dashboard should be shown.

In order to "log in" the user ( = to create an active session) I came up with a small helper function that is based on the respective cookbook article from the symfony documentation: How to Simulate Authentication with a Token in a Functional Test

This however does not seem to work. My initial tests fails. So I have added another request to a dummy controller that only prints out session information. This shows that while the seems to be set correctly, the current user information and the security token storage seem to be incorrect - as I get the default "AnonymousToken" and a null return from the getUser method.

Here is my test code:

<?php

namespace GOC\Bundle\AdminBundle\Tests\Controller;

use FOS\RestBundle\Util\Codes;

use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;

use GOC\Bundle\FrameworkBundle\Model\ContextInterface;
use GOC\Bundle\FrameworkBundle\Tests\WebTestCase;

class DashboardControllerTest extends WebTestCase
{
    const BASE_URL = '';

    /**
     * @var ContextInterface|null
     */
    protected $context;

    public static function setUpBeforeClass()
    {
        $client = static::createClient([
            'environment' => 'test',
            'debug'       => false,
        ], [
            'HTTP_HOST' => 'demo-cms.dev',
        ]);

        $container = $client->getContainer();
        $kernel    = $container->get('kernel');

        $application = new Application($kernel);
        $application->setAutoExit(false);

        $input = new ArrayInput(array(
            'command' => 'doctrine:mongodb:fixtures:load',
        ));
        $output = new NullOutput();
        $application->run($input, $output);
    }

    /**
     * Testing whether the backend is not publicly accessible
     */
    public function testFirewallRedirect()
    {
        $client = static::createClient();

        $client->request('GET', $this->buildUrl('/admin/dashboard'));
        $response = $client->getResponse();

        $this->assertTrue($client->getResponse()->isRedirect($this->buildUrl('http://localhost/admin/login')));

        $crawler  = $client->request('GET', $response->headers->get('location'));

        $this->assertEquals(
            1,
            $crawler->filter('html:contains("Willkommen")')->count()
        );
    }

    /**
     * Testing whether the backend is not publicly accessible
     */
    public function testFirewallAccess()
    {
        $client = static::createClient([
            'environment' => 'test',
            'debug'       => false,
        ], [
            'HTTP_HOST' => 'demo-cms.dev',
        ]);

        $this->logIn($client);

        $client->request('GET', $this->buildUrl('/admin/dashboard'));
        $response = $client->getResponse();

        // This fails...
        //$this->assertEquals(Codes::HTTP_OK, $response->getStatusCode());

        // Debug statements
        $client->request('GET', $this->buildUrl('/forgot-password-sent'));
        $response = $client->getResponse();
        dump($response);
    }

    /**
     * @param Client $client
     */
    protected function logIn(Client $client)
    {
        $container = $client->getContainer();

        $repository = $container->get('goc_account.user_manager')->getRepository();
        $user       = $repository->getUserByUsername('[email protected]', $this->getContext($client));

        $firewall = 'main';
        $token    = new UsernamePasswordToken($user, null, $firewall, $user->getRoles());

        $container->get('security.token_storage')->setToken($token);
        $session = $container->get('session');

        // Saw this somewhere else, makes no difference though
        //$session = new Session(new MockArraySessionStorage());
        //$session->start();
        //$container->set('session', $session);


        $session->set('_security_'.$firewall, serialize($token));
        $session->save();

        $cookie = new Cookie($session->getName(), $session->getId());
        $client->getCookieJar()->set($cookie);
    }

    /**
     * @param $url
     *
     * @return string
     */
    protected function buildUrl($url)
    {
        return $this::BASE_URL . $url;
    }

    /**
     * @param Client $client
     *
     * @return ContextInterface
     */
    protected function getContext(Client $client)
    {
        if ($this->context) {

            return $this->context;
        }

        $this->context = $client->getContainer()->get('goc_framework.context_manager')->getContextByName('demo');

        return $this->context;
    }
}

Thanks in advance for any help - it's highly appreciated !

1

1 Answers

0
votes

We are not trying to mock the token but actually create one with the following function (which might be adaptable to your situation):

protected function createAuthenticatedClient($username, $password)
{
    $client = static::createClient();
    $client->request(
        'POST',
        '/api/v1/login_check',
        array(
            'username' => $username,
            'password' => $password,
        )
    );

    $data = json_decode($client->getResponse()->getContent(), true);

    $client = static::createClient();
    $client->setServerParameter('HTTP_Authorization', sprintf('Bearer %s', $data['token']));

    return $client;
}

After using this like

 $client = $this->createAuthenticatedClient('user', 'password');

every request sent will be attached with our actual Bearer.