skqr/hateoas-bundle

View on GitHub
Test/PHPUnit/ApiTestCase.php

Summary

Maintainability
A
35 mins
Test Coverage
<?php
/**
 * @copyright 2014 Integ S.A.
 * @license http://opensource.org/licenses/MIT The MIT License (MIT)
 * @author Javier Lorenzana <javier.lorenzana@gointegro.com>
 */

namespace GoIntegro\Bundle\HateoasBundle\Test\PHPUnit;

// Testing.
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
// ORM Fixtures
use Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader,
    Doctrine\Common\DataFixtures\Purger\ORMPurger,
    Doctrine\Common\DataFixtures\Executor\ORMExecutor;
// Test tools.
use JsonSchema\Validator,
    GoIntegro\Hateoas\Http\Client;

use GoIntegro\Bundle\HateoasBundle\Util\HttpClientUtil;

abstract class ApiTestCase extends WebTestCase
{
    const FAIL_RESPONSE_STATUS_PATTERN = "Failed asserting that the status code %s (\"%s)\" matches the expected %s (\"%s\").",
        WSSE_CREDENTIALS_PATTERN = "X-WSSE: UsernameToken Username=\"%s\", PasswordDigest=\"%s\", Nonce=\"%s\", Created=\"%s\"",
        HEADER_LOCALE = 'en-GB',
        CONTENT_JSON = 'application/json',
        /**
         * @see http://www.iana.org/assignments/media-types/application/vnd.api+json
         */
        CONTENT_JSON_API = 'application/vnd.api+json';

    /**
     * @var array Los estados de respuesta HTTP y sus códigos.
     */
    protected static $statusCodes = array(
        // Successful.
        'OK' => 200,
        'Created' => 201,
        'NoContent' => 204,
        'PartialContent' => 206,
        // Redirection.
        'Found' => 302,
        // Client error.
        'BadRequest' => 400,
        'Unauthorized' => 401,
        'Forbidden' => 403,
        'NotFound' => 404,
        'MethodNotAllowed' => 405,
        'Conflict' => 409,
        'UnsupportedMediaType' => 415,
        'UnprocessableEntity' => 422,
        // Server error.
        'InternalServerError' => 500,
        'NotImplemented' => 501
    );
    /**
     * @var \Doctrine\ORM\EntityManager
     */
    protected static $em;
    /**
     * @var string
     */
    protected static $rootUrl;

    /**
     * Prepara el entorno antes de la primer prueba.
     */
    public static function setUpBeforeClass()
    {
        // Inicializamos el framework web.
        static::$kernel = static::createKernel();
        static::$kernel->boot();
        $container = static::$kernel->getContainer();

        // Obtenemos algunos servicios de uso común.
        static::$em = $container->get('doctrine')->getManager();
        static::$rootUrl = $container->getParameter('api.base_url');

        if ($fixtures = static::getFixtures()) {
            $loader = new ContainerAwareLoader($container);
            array_walk($fixtures, array($loader, 'addFixture'));
            $purger = new ORMPurger(static::$em);
            $executor = new ORMExecutor(static::$em, $purger);
            $executor->execute($loader->getFixtures());
        }
    }

    /**
     * Prepara el entorno antes de cada prueba.
     */
    public function setUp()
    {
        parent::setUp();
        self::$kernel->boot();
    }

    /**
     * Desarma el entorno después de la última prueba.
     */
    public static function tearDownAfterClass()
    {
        //Purgamos la base de datos.
        $purger = new ORMPurger(static::$em);
        $purger->purge();
    }

    /**
     * Obtiene el entity manager de Doctrine 2.
     * @return DoctrineRegistry
     */
    protected function getEntityManager()
    {
        return static::$em;
    }

    /**
     * @return array <FixtureInterface>
     */
    protected static function getFixtures()
    {
        return array();
    }

    /**
     * Añadiéndole expresiones regulares a las aserciones de JSON.
     * @see PHPUnit_Framework_TestCase::assertJsonStringEqualsJsonFile
     * @see http://json-schema.org/
     */
    public static function assertJsonSchema(
        $expectedSchema, $actualJson, $message = ''
    )
    {
        $jsonCoder = static::$kernel
            ->getContainer()
            ->get('hateoas.json_coder');
        $condition = $jsonCoder->matchSchema($actualJson, $expectedSchema);
        $message .= $jsonCoder->getSchemaErrorMessage();

        return static::assertThat($condition, static::isTrue(), $message);
    }

    /**
     * Añadiéndole expresiones regulares a las aserciones de JSON.
     * @see PHPUnit_Framework_TestCase::assertJsonStringEqualsJsonFile
     * @see http://jsonapi.org/schema
     */
    public static function assertJsonApiSchema($actualJson, $message = '')
    {
        $jsonCoder = static::$kernel
            ->getContainer()
            ->get('hateoas.json_validator');
        $condition = $jsonCoder->assertJsonApi($actualJson);
        $message .= $jsonCoder->getSchemaErrorMessage();

        return static::assertThat($condition, static::isTrue(), $message);
    }

    /**
     * Maneja todas las llamadas a métodos no declarados.
     * @param string $method
     * @param array $args
     * @return bool En caso de existir el método, es una verificación.
     * @throws \BadMethodCallException Para métodos inexistentes.
     */
    public function __call($method, $args)
    {
        if (
            0 === strpos($method, 'assertResponse')
            and $status = substr($method, strlen('assertResponse'))
            and in_array($status, array_keys(static::$statusCodes))
        ) {
            array_unshift($args, static::$statusCodes[$status]);

            return call_user_func_array(
                [$this, 'assertResponseStatus'], $args
            );
        } else {
            throw new \BadMethodCallException("No such method can be called.");
        }
    }

    /**
     * Verifica el código de estado de una respuesta HTTP.
     * @param int $expectedCode
     * @param Client $client
     */
    public static function assertResponseStatus(
        $expectedCode, Client $client, $message = ''
    )
    {
        $actualCode = $client->getInfo(CURLINFO_HTTP_CODE);
        $condition = $expectedCode === $actualCode;
        $message .= sprintf(
            static::FAIL_RESPONSE_STATUS_PATTERN,
            $actualCode,
            array_flip(static::$statusCodes)[$actualCode],
            $expectedCode,
            array_flip(static::$statusCodes)[$expectedCode]
        );

        return static::assertThat($condition, static::isTrue(), $message);
    }

    /**
     * Obtiene la URL base de la API REST.
     * @return string
     */
    public function getRootUrl()
    {
        return static::$rootUrl;
    }

    /**
     * Obtiene un cliente de HTTP.
     * @param string $url
     * @param string $username
     * @param string $password
     * @return Client
     */
    protected function buildHttpClient(
        $url = NULL,
        $username = NULL,
        $password = NULL,
        $contentType = self::CONTENT_JSON_API,
        $language = self::HEADER_LOCALE
    )
    {
        return HttpClientUtil::buildHttpClient(
            $url, 
            $username,
            $password,
            $contentType,
            $language);
    }

    /**
     * Compone un header de autenticación WSSE.
     * @param string $username
     * @param string $password
     * @see http://en.wikipedia.org/wiki/WS-Security
     */
    protected function buildWsseHeader($username, $password)
    {
        return HttpClientUtil::buildWsseHeader($username, $password);
    }
}