<?php
namespace Backend\BaseBundle\Tests\EventListener;


use Backend\BaseBundle\EventListener\APICORSResponseListener;
use Backend\BaseBundle\Tests\Fixture\BaseTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\KernelEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;

/**
 * @group unit
 */
class APICORSResponseListenerTest extends BaseTestCase
{
    public function test_onRequest_not_master_request()
    {
        //arrange
        $listener = new APICORSResponseListener();
        $event = $this->getMock(GetResponseEvent::class, array('getRequest'), array(), '', false);
        $event
            ->expects($this->never())
            ->method('getRequest')
            ;
        $request = new Request();
        $this->setObjectAttribute($event, 'request', $request, KernelEvent::class);
        $this->setObjectAttribute($event, 'requestType', HttpKernelInterface::SUB_REQUEST, KernelEvent::class);

        //act
        $listener->onRequest($event);

        //assert
        $this->assertNull($request->attributes->get('_cors'));
        $this->assertNull($event->getResponse());
    }

    public function test_onRequest_null_origin()
    {
        //arrange
        $listener = new APICORSResponseListener();
        $event = $this->getMock(GetResponseEvent::class, null, array(), '', false);
        $request = new Request();
        $this->setObjectAttribute($event, 'request', $request, KernelEvent::class);
        $this->setObjectAttribute($event, 'requestType', HttpKernelInterface::MASTER_REQUEST, KernelEvent::class);

        //act
        $listener->onRequest($event);

        //assert
        $this->assertNull($request->attributes->get('_cors'));
        $this->assertNull($event->getResponse());
    }

    public function test_onRequest_null_slug()
    {
        //arrange
        $listener = new APICORSResponseListener();
        $event = $this->getMock(GetResponseEvent::class, null, array(), '', false);
        $request = new Request();
        $this->setObjectAttribute($event, 'request', $request, KernelEvent::class);
        $this->setObjectAttribute($event, 'requestType', HttpKernelInterface::MASTER_REQUEST, KernelEvent::class);
        $request->headers->set('Origin', 'http://localhost');

        //act
        $listener->onRequest($event);

        //assert
        $this->assertNull($request->attributes->get('_cors'));
        $this->assertNull($event->getResponse());
    }

    public function test_onRequest_invalid_origin()
    {
        //arrange
        $listener = $this->getMock(APICORSResponseListener::class, array('isValidOrigin'));
        $listener
            ->expects($this->once())
            ->method('isValidOrigin')
            ->willReturnCallback(function($origin, $slug){
                $this->assertEquals('http://localhost', $origin);
                $this->assertEquals('dgfactor', $slug);
                return false;
            });
        $event = $this->getMock(GetResponseEvent::class, null, array(), '', false);
        $request = new Request();
        $this->setObjectAttribute($event, 'request', $request, KernelEvent::class);
        $this->setObjectAttribute($event, 'requestType', HttpKernelInterface::MASTER_REQUEST, KernelEvent::class);
        $request->headers->set('Origin', 'http://localhost');
        $request->attributes->set('slug', 'dgfactor');

        //act
        $listener->onRequest($event);
        $response = $event->getResponse();

        //assert
        $this->assertNull($request->attributes->get('_cors'));
        $this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
    }

    public function test_onRequest_valid_origin()
    {
        //arrange
        $listener = $this->getMock(APICORSResponseListener::class, array('isValidOrigin'));
        $listener
            ->expects($this->once())
            ->method('isValidOrigin')
            ->willReturnCallback(function($origin, $slug){
                $this->assertEquals('http://localhost', $origin);
                $this->assertEquals('dgfactor', $slug);
                return true;
            });
        $event = $this->getMock(GetResponseEvent::class, null, array(), '', false);
        $request = new Request();
        $this->setObjectAttribute($event, 'request', $request, KernelEvent::class);
        $this->setObjectAttribute($event, 'requestType', HttpKernelInterface::MASTER_REQUEST, KernelEvent::class);
        $request->headers->set('Origin', 'http://localhost');
        $request->attributes->set('slug', 'dgfactor');

        //act
        $listener->onRequest($event);
        $response = $event->getResponse();
        $cors = $request->attributes->get('_cors');

        //assert
        $this->assertEquals(array(
            'Access-Control-Allow-Origin' => 'http://localhost',
            'Access-Control-Allow-Methods' => 'PUT, GET, POST, DELETE, OPTIONS',
            'Access-Control-Allow-Headers' => 'X-Requested-With, Content-Type, Accept',
            'Access-Control-Max-Age' => 86400,
        ), $cors);
        $this->assertNull($event->getResponse());
    }

    public function test_onResponse_not_master_request()
    {
        //arrange
        $listener = new APICORSResponseListener();
        $event = $this->getMock(FilterResponseEvent::class, array('getRequest'), array(), '', false);
        $event
            ->expects($this->never())
            ->method('getRequest')
        ;
        $request = new Request();
        $request->attributes->set('_cors', array(
            'Access-Control-Allow-Origin' => 'http://localhost',
            'Access-Control-Allow-Methods' => 'PUT, GET, POST, DELETE, OPTIONS',
        ));
        $this->setObjectAttribute($event, 'request', $request, KernelEvent::class);
        $this->setObjectAttribute($event, 'requestType', HttpKernelInterface::SUB_REQUEST, KernelEvent::class);

        $response = new Response();
        $this->setObjectAttribute($event, 'response', $response, FilterResponseEvent::class);

        //act
        $listener->onResponse($event);

        //assert
        $this->assertNull($response->headers->get('Access-Control-Allow-Origin'));
        $this->assertNull($response->headers->get('Access-Control-Allow-Methods'));
    }

    public function test_onResponse_no_cors()
    {
        //arrange
        $listener = new APICORSResponseListener();
        $event = $this->getMock(FilterResponseEvent::class, null, array(), '', false);
        $request = new Request();
        $this->setObjectAttribute($event, 'request', $request, KernelEvent::class);
        $this->setObjectAttribute($event, 'requestType', HttpKernelInterface::MASTER_REQUEST, KernelEvent::class);

        $response = new Response();
        $this->setObjectAttribute($event, 'response', $response, FilterResponseEvent::class);

        //act
        $listener->onResponse($event);

        //assert
        $this->assertNull($response->headers->get('Access-Control-Allow-Origin'));
        $this->assertNull($response->headers->get('Access-Control-Allow-Methods'));
    }

    public function test_onResponse_has_cors()
    {
        //arrange
        $listener = new APICORSResponseListener();
        $event = $this->getMock(FilterResponseEvent::class, null, array(), '', false);
        $request = new Request();
        $request->attributes->set('_cors', array(
            'Access-Control-Allow-Origin' => 'http://localhost',
            'Access-Control-Allow-Methods' => 'PUT, GET, POST, DELETE, OPTIONS',
        ));
        $this->setObjectAttribute($event, 'request', $request, KernelEvent::class);
        $this->setObjectAttribute($event, 'requestType', HttpKernelInterface::MASTER_REQUEST, KernelEvent::class);

        $response = new Response();
        $this->setObjectAttribute($event, 'response', $response, FilterResponseEvent::class);

        //act
        $listener->onResponse($event);

        //assert
        $this->assertEquals('http://localhost', $response->headers->get('Access-Control-Allow-Origin'));
        $this->assertEquals('PUT, GET, POST, DELETE, OPTIONS', $response->headers->get('Access-Control-Allow-Methods'));
    }

    public function test_isValidOrigin_bad_origin_url()
    {
        //arrange
        $origin = 'bad_host';
        $slug = 'dgfactor';
        $listener = $this->getMock(APICORSResponseListener::class, array('findAcceptDomain'));
        $listener
            ->expects($this->once())
            ->method('findAcceptDomain')
            ->willReturnCallback(function($slug){
                $this->assertEquals('dgfactor', $slug);
                return array(
                    'localhost',
                );
            });

        //act
        $result = $this->callObjectMethod($listener, 'isValidOrigin', $origin, $slug);

        //assert
        $this->assertFalse($result);
    }

    public function test_isValidOrigin_null_domain()
    {
        //arrange
        $origin = 'http://localhost';
        $slug = 'dgfactor';
        $listener = $this->getMock(APICORSResponseListener::class, array('findAcceptDomain'));
        $listener
            ->expects($this->once())
            ->method('findAcceptDomain')
            ->willReturnCallback(function($slug){
                $this->assertEquals('dgfactor', $slug);
                return null;
            });

        //act
        $result = $this->callObjectMethod($listener, 'isValidOrigin', $origin, $slug);

        //assert
        $this->assertFalse($result);
    }

    public function test_isValidOrigin_not_in_valid_domain()
    {
        //arrange
        $origin = 'http://example.com';
        $slug = 'dgfactor';
        $listener = $this->getMock(APICORSResponseListener::class, array('findAcceptDomain'));
        $listener
            ->expects($this->once())
            ->method('findAcceptDomain')
            ->willReturnCallback(function($slug){
                $this->assertEquals('dgfactor', $slug);
                return array(
                    'localhost',
                );
            });

        //act
        $result = $this->callObjectMethod($listener, 'isValidOrigin', $origin, $slug);

        //assert
        $this->assertFalse($result);
    }

    public function test_isValidOrigin_valid()
    {
        //arrange
        $origin = 'http://example.com';
        $slug = 'dgfactor';
        $listener = $this->getMock(APICORSResponseListener::class, array('findAcceptDomain'));
        $listener
            ->expects($this->once())
            ->method('findAcceptDomain')
            ->willReturnCallback(function($slug){
                $this->assertEquals('dgfactor', $slug);
                return array(
                    'localhost',
                    'example.com'
                );
            });

        //act
        $result = $this->callObjectMethod($listener, 'isValidOrigin', $origin, $slug);

        //assert
        $this->assertTrue($result);
    }

}