<?php

namespace Velis\Test\Dto;

use Phalcon\Http\RequestInterface;
use Phalcon\Mvc\Dispatcher;
use ReflectionException;
use Velis\Dto\Exceptions\ValidationException;
use Velis\Mvc\Controller\Utils\DtoRequestTransformer;
use Velis\Test\Dto\Concrete\Dto\TestDto;
use Velis\Test\Dto\Concrete\TestController;
use Velis\TestCase;

/**
 * Test transforming the request body into DTOs.
 *
 * @author Szymon Janaczek <szymon.janaczek@velistech.com>
 */
class DtoRequestTransformerTest extends TestCase
{
    /**
     * Test whether the request body is transformed into DTO and injected into the action.
     * Additionally, test whether the filter is applied to the request body.
     *
     * @throws ReflectionException
     */
    public function testTransformRequest(): void
    {
        // Prepare request body
        $data = [
            'name' => 'Mc',
            'last_name' => "Donald's", // the filter should replace the single apostrophe
            'created_at' => '1932-11-02 00:00:00',
        ];

        // Create request mock. Assuming we use POST method, append prepared data to request body
        $request = $this->createPartialMock(RequestInterface::class, []);
        $request->method('getMethod')->willReturn('POST');
        $request->method('getPost')->willReturn($data);

        // Create dispatcher mock. Assuming we use TestController::getDetailsAction
        $dispatcher = $this->createPartialMock(Dispatcher::class, ['getControllerClass', 'getActionName']);
        $dispatcher->expects($this->any())->method('getControllerClass')->willReturn(TestController::class);
        $dispatcher->expects($this->any())->method('getActionName')->willReturn('getDetails');

        // Perform request transformation
        $transformer = new DtoRequestTransformer($request);
        $transformer->transformRequest($dispatcher);

        // Assert that the request body was transformed into TestDto and injected into the action
        $this->assertTrue($dispatcher->getParam('data') instanceof TestDto);
        $this->assertEquals($data['name'], $dispatcher->getParam('data')->name);
        $this->assertEquals($data['created_at'], $dispatcher->getParam('data')->created_at);

        // a single apostrophe was replaced by ’ so - filter was applied.
        $this->assertNotEquals($data['last_name'], $dispatcher->getParam('data')->last_name);
        $this->assertEquals('Donald’s', $dispatcher->getParam('data')->last_name);
    }

    /**
     * Test whether the request body is validated.
     *
     * @throws ReflectionException
     */
    public function testValidationWarningOnTransformation(): void
    {
        // Prepare request body
        $data = [
            'name' => 'My Too Long User Name',
        ];

        // Create request mock. Assuming we use POST method, append prepared data to request body
        $request = $this->createPartialMock(RequestInterface::class, []);
        $request->method('getMethod')->willReturn('POST');
        $request->method('getPost')->willReturn($data);

        // Create dispatcher mock. Assuming we use TestController::getDetailsAction
        $dispatcher = $this->createPartialMock(Dispatcher::class, ['getControllerClass', 'getActionName']);
        $dispatcher->expects($this->any())->method('getControllerClass')->willReturn(TestController::class);
        $dispatcher->expects($this->any())->method('getActionName')->willReturn('getDetailsValidated');

        try {
            // Perform request transformation
            $transformer = new DtoRequestTransformer($request);
            $transformer->transformRequest($dispatcher);

            // Exception should be thrown, because the name is too long
            $this->fail(ValidationException::class . ' was not thrown');
        } catch (ValidationException $e) {
            $this->assertEquals('name', $e->getErrors()[0]['property']);
            $this->assertEquals('Value is too long', $e->getErrors()[0]['message']);
        }
    }
}
