Seditio Source
Root |
./othercms/croogo-4.0.7/vendor/friendsofcake/crud-json-api/tests/TestCase/View/JsonApiViewTest.php
<?php
namespace CrudJsonApi\Test\TestCase\View;

use
Cake\Controller\Controller;
use
Cake\Core\Configure;
use
Cake\Core\Plugin;
use
Cake\Event\Event;
use
Cake\Filesystem\File;
use
Cake\Http\Response;
use
Cake\Http\ServerRequest;
use
Cake\ORM\Entity;
use
Cake\ORM\Query;
use
Cake\ORM\TableRegistry;
use
Cake\TestSuite\StringCompareTrait;
use
Cake\Utility\Inflector;
use
CrudJsonApi\Listener\JsonApiListener;
use
CrudJsonApi\View\JsonApiView;
use
Crud\Error\Exception\CrudException;
use
Crud\Event\Subject;
use
Crud\TestSuite\TestCase;
use
Neomerx\JsonApi\Schema\Link;
use
StdClass;

/**
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 */
class JsonApiViewTest extends TestCase
{
    use
StringCompareTrait;

   
/**
     * Fixtures
     *
     * @var array
     */
   
public $fixtures = [
       
'plugin.CrudJsonApi.Countries',
       
'plugin.CrudJsonApi.Currencies',
       
'plugin.CrudJsonApi.Cultures',
    ];

   
/**
     * Path to directory holding the JSON API response fixtures.
     *
     * @var
     */
   
protected $_JsonApiResponseBodyFixtures;

   
/**
     * Loaded with JsonApiListener default config settings on every setUp()
     *
     * @var array
     */
   
protected $_defaultViewVars;

   
/**
     * setUp
     */
   
public function setUp()
    {
       
parent::setUp();

       
$this->deprecated(function () {
            \
Cake\Core\Plugin::load('Crud', ['path' => ROOT . DS, 'autoload' => true]);
            \
Cake\Core\Plugin::load('CrudJsonApi', ['path' => ROOT . DS, 'autoload' => true]);
        });

       
$listener = new JsonApiListener(new Controller());
       
$this->_defaultViewVars = $listener->getConfig();

       
$this->_defaultViewVars = [
           
'_urlPrefix' => $listener->getConfig('urlPrefix'),
           
'_withJsonApiVersion' => $listener->getConfig('withJsonApiVersion'),
           
'_meta' => $listener->getConfig('meta'),
           
'_absoluteLinks' => $listener->getConfig('absoluteLinks'),
           
'_jsonApiBelongsToLinks' => $listener->getConfig('jsonApiBelongsToLinks'),
           
'_include' => $listener->getConfig('include'),
           
'_fieldSets' => $listener->getConfig('fieldSets'),
           
'_jsonOptions' => $listener->getConfig('jsonOptions'),
           
'_debugPrettyPrint' => $listener->getConfig('debugPrettyPrint'),
           
'_inflect' => $listener->getConfig('inflect')
        ];

       
// override some defaults to create more DRY tests
       
$this->_defaultViewVars['_jsonOptions'] = [JSON_PRETTY_PRINT];
       
$this->_defaultViewVars['_serialize'] = true;

       
// set path the the JSON API response fixtures
       
$this->_JsonApiResponseBodyFixtures = Plugin::path('Crud') . 'tests' . DS . 'Fixture' . DS . 'JsonApiResponseBodies';
    }

   
/**
     * Make sure we are testing with expected configuration settings.
     */
   
public function testDefaultViewVars()
    {
       
$expected = [
           
'_urlPrefix' => null,
           
'_withJsonApiVersion' => false,
           
'_meta' => [],
           
'_absoluteLinks' => false,
           
'_jsonApiBelongsToLinks' => false,
           
'_include' => [],
           
'_fieldSets' => [],
           
'_jsonOptions' => [
               
JSON_PRETTY_PRINT
           
],
           
'_debugPrettyPrint' => true,
           
'_inflect' => 'dasherize',
           
'_serialize' => true
       
];
       
$this->assertSame($expected, $this->_defaultViewVars);
    }

   
/**
     * Helper function to easily create specific view for each test.
     *
     * @param string|bool $tableName False to get view for resource-less response
     * @param array $viewVars
     * @return string NeoMerx jsonapi encoded array
     */
   
protected function _getView($tableName, $viewVars)
    {
       
// determine user configurable viewVars
       
if (empty($viewVars)) {
           
$viewVars = $this->_defaultViewVars;
        } else {
           
$viewVars = array_replace_recursive($this->_defaultViewVars, $viewVars);
        }

       
// create required (but non user configurable) viewVars next
       
$request = new ServerRequest();
       
$response = new Response();
       
$controller = new Controller($request, $response, $tableName);

       
$builder = $controller->viewBuilder();
       
$builder->setClassName(JsonApiView::class);

       
// create view with viewVars for resource-less response
       
if (!$tableName) {
           
$controller->set($viewVars);

            return
$controller->createView();
        }

       
// still here, create view with viewVars for response with resource(s)
       
$controller->setName($tableName); // e.g. Countries
       
$table = $controller->loadModel(); // table object

        // fetch data from test viewVar normally found in subject
       
$subject = new Subject(new Event('Crud.beforeHandle'));
       
$findResult = $viewVars[$table->getTable()];
        if (
is_a($findResult, '\Cake\ORM\ResultSet')) {
           
$subject->entities = $findResult;
        } else {
           
$subject->entity = $findResult;
        }
       
$subject->query = $table->query();

       
// create required '_entities' and '_associations' viewVars normally
        // produced and set by the JsonApiListener
       
$listener = $this
           
->getMockBuilder(JsonApiListener::class)
            ->
disableOriginalConstructor()
            ->
setMethods(null)
            ->
getMock();

       
$this->setReflectionClassInstance($listener);
       
$associations = $this->callProtectedMethod('_getContainedAssociations', [$table, $subject->query->getContain()], $listener);
       
$repositories = $this->callProtectedMethod('_getRepositoryList', [$table, $associations], $listener);

       
$viewVars['_repositories'] = $repositories;
       
$viewVars['_associations'] = $associations;

       
// set viewVars before creating the view
       
$controller->set($viewVars);

        return
$controller->createView();
    }

   
/**
     * Make sure viewVars starting with an underscore are treated as specialVars.
     *
     * @return void
     */
   
public function testGetSpecialVars()
    {
       
$view = $this->_getView('Countries', [
           
'countries' => 'dummy-value',
           
'non_underscored_should_not_be_in_special_vars' => 'dummy-value',
           
'_underscored_should_be_in_special_vars' => 'dummy-value',
        ]);

       
$this->setReflectionClassInstance($view);
       
$result = $this->callProtectedMethod('_getSpecialVars', [], $view);

       
$this->assertNotContains('non_underscored_should_not_be_in_special_vars', $result);
       
$this->assertContains('_underscored_should_be_in_special_vars', $result);
    }

   
/**
     * Make sure that an exception is thrown for generic entity classes
     *
     * @return void
     * @expectedException \Crud\Error\Exception\CrudException
     * @expectedExceptionMessage Entity classes must not be the generic "Cake\ORM\Entity" class for repository "Countries"
     */
   
public function testEncodeWithGenericEntity()
    {
       
TableRegistry::get('Countries')->setEntityClass(Entity::class);

       
// test collection of entities without relationships
       
$countries = TableRegistry::get('Countries')
            ->
find()
            ->
all();
       
$view = $this->_getView('Countries', [
           
'countries' => $countries
       
]);

       
$view->render();
    }

   
/**
     * Make sure expected JSON API strings are generated as expected when
     * using Crud's DynamicEntitySchema.
     *
     * Please note that we are deliberately using assertSame() instead of
     * assertJsonFileEqualsJsonFile() for all tests because the latter will
     * ignore formatting like e.g. JSON_PRETTY_PRINT.
     *
     * @return void
     */
   
public function testEncodeWithDynamicSchemas()
    {
       
// test collection of entities without relationships
       
$countries = TableRegistry::get('Countries')->find()->all();
       
$view = $this->_getView('Countries', [
           
'countries' => $countries
       
]);

       
$this->assertSameAsFile(
           
$this->_JsonApiResponseBodyFixtures . DS . 'FetchingCollections' . DS . 'get-countries-without-pagination.json',
           
$view->render()
        );

       
// test single entity without relationships
       
$countries = TableRegistry::get('Countries')->find()->first();
       
$view = $this->_getView('Countries', [
           
'countries' => $countries,
        ]);

       
$this->assertSameAsFile(
           
$this->_JsonApiResponseBodyFixtures . DS . 'FetchingResources' . DS . 'get-country-no-relationships.json',
           
$view->render()
        );
    }

   
/**
     * Make sure resource-less JSON API strings are generated as expected.
     *
     * @return void
     */
   
public function testEncodeWithoutSchemas()
    {
       
// make sure empty body is rendered
       
$view = $this->_getView(false, [
           
'_meta' => false
       
]);
       
$this->assertNull($view->render());

       
// make sure body with only/just `meta` node is rendered
       
$view = $this->_getView(false, [
           
'_meta' => [
               
'author' => 'bravo-kernel'
           
]
        ]);

       
$this->assertSameAsFile(
           
$this->_JsonApiResponseBodyFixtures . DS . 'MetaInformation' . DS . 'meta-only.json',
           
$view->render()
        );
    }

   
/**
     * Make sure user option `withJsonVersion` produces expected json
     *
     * @return void
     */
   
public function testOptionalWithJsonApiVersion()
    {
       
// make sure top-level node is added when true
       
$countries = TableRegistry::get('Countries')->find()->all();
       
$view = $this->_getView('Countries', [
           
'countries' => $countries,
           
'_withJsonApiVersion' => true
       
]);
       
$expectedVersionArray = [
           
'jsonapi' => [
               
'version' => '1.1'
           
]
        ];

       
$this->assertArraySubset($expectedVersionArray, json_decode($view->render(), true));

       
// make sure top-level node is added when passed an array
       
$countries = TableRegistry::get('Countries')->find()->all();
       
$view = $this->_getView('Countries', [
           
'countries' => $countries,
           
'_withJsonApiVersion' => [
               
'meta-key-1' => 'meta-val-1',
               
'meta-key-2' => 'meta-val-2',
            ]
        ]);
       
$expectedVersionArray = [
           
'jsonapi' => [
               
'version' => '1.1',
               
'meta' => [
                   
'meta-key-1' => 'meta-val-1',
                   
'meta-key-2' => 'meta-val-2',
                ]
            ]
        ];

       
$this->assertArraySubset($expectedVersionArray, json_decode($view->render(), true));

       
// make sure top-level node is not added when false
       
$view = $this->_getView('Countries', [
           
'countries' => $countries,
           
'_withJsonApiVersion' => false,
        ]);
       
$this->assertArrayNotHasKey('jsonapi', json_decode($view->render(), true));
    }

   
/**
     * Make sure user option `meta` produces expected json
     *
     * @return void
     */
   
public function testOptionalMeta()
    {
       
// make sure top-level node is added when passed an array
       
$countries = TableRegistry::get('Countries')->find()->all();
       
$view = $this->_getView('Countries', [
           
'countries' => $countries,
           
'_meta' => [
               
'author' => 'bravo-kernel'
           
],
        ]);
       
$expectedMetaArray = [
           
'meta' => [
               
'author' => 'bravo-kernel'
           
]
        ];

       
$this->assertArraySubset($expectedMetaArray, json_decode($view->render(), true));

       
// make sure top-level node is not added when false
       
$view = $this->_getView('Countries', [
           
'countries' => $countries,
           
'_meta' => false,
        ]);
       
$this->assertArrayNotHasKey('meta', json_decode($view->render(), true));

       
// make sure a response with just/only a meta node is generated if
        // no corresponding entity data was retrieved from the viewVars
        // (as supported by the jsonapi spec)
       
$view = $this->_getView('Countries', [
           
'countries' => null,
           
'_meta' => [
               
'author' => 'bravo-kernel'
           
],
        ]);
       
$expectedResponseArray = [
           
'meta' => [
               
'author' => 'bravo-kernel'
           
]
        ];

       
$this->assertSame($expectedResponseArray, json_decode($view->render(), true));
    }

   
/**
     * Make sure user option `debugPrettyPrint` behaves produces expected json
     *
     * @return void
     */
   
public function testOptionalDebugPrettyPrint()
    {
       
// make sure pretty json is generated when true AND in debug mode
       
$countries = TableRegistry::get('Countries')
            ->
find()
            ->
first();
       
$this->assertTrue(Configure::read('debug'));
       
$view = $this->_getView('Countries', [
           
'countries' => $countries,
           
'_debugPrettyPrint' => true,
        ]);

       
$this->assertSameAsFile(
           
$this->_JsonApiResponseBodyFixtures . DS . 'FetchingResources' . DS . 'get-country-no-relationships.json',
           
$view->render()
        );

       
// make sure we can produce non-pretty in debug mode as well
       
$countries = TableRegistry::get('Countries')
            ->
find()
            ->
first();
       
$this->assertTrue(Configure::read('debug'));
       
$view = $this->_getView('Countries', [
           
'countries' => $countries,
           
'_debugPrettyPrint' => false,
           
'_jsonOptions' => 0,
        ]);

       
$this->assertSame(
           
'{"data":{"type":"countries","id":"1","attributes":{"code":"NL","name":"The Netherlands","dummy-counter":11111},"relationships":{"currency":{"links":{"self":"\/currencies\/1"},"data":{"type":"currencies","id":"1"}},"national-capital":{"links":{"self":"\/national_capitals\/view\/1"},"data":{"type":"national-capitals","id":"1"}}},"links":{"self":"\/countries\/1"}}}',
           
$view->render()
        );
    }

   
/**
     * Make sure the resource type is only the entity name
     *
     * @return void
     */
   
public function testResourceTypes()
    {
       
$countries = TableRegistry::get('Countries')
            ->
find()
            ->
first();
       
$view = $this->_getView('Countries', [
           
'countries' => $countries,
        ]);

       
$this->assertEquals('countries', json_decode($view->render())->data->type);
    }

   
/**
     * Make sure correct $data to be encoded is fetched from set viewVars
     *
     * @return void
     */
   
public function testGetDataToSerializeFromViewVarsSuccess()
    {
       
$view = $this
           
->getMockBuilder('\CrudJsonApi\View\JsonApiView')
            ->
disableOriginalConstructor()
            ->
setMethods(null)
            ->
getMock();

       
$this->setReflectionClassInstance($view);

       
// make sure set expected data is returned when _serialize is true
       
$view->viewVars = [
           
'countries' => 'dummy-would-normally-be-an-entity-or-resultset',
        ];

       
$this->assertSame(
           
'dummy-would-normally-be-an-entity-or-resultset',
           
$this->callProtectedMethod('_getDataToSerializeFromViewVars', [], $view)
        );

       
// make sure null is returned when no data is found (which would mean
        // only _specialVars were set and since these are all flipped it leads
        // to a null result)
       
$view->viewVars = [
           
'_meta' => false
           
];
       
$this->assertNull($this->callProtectedMethod('_getDataToSerializeFromViewVars', [], $view));

       
// When passing an array as _serialize ONLY the first entity in that
        // array list will be used to return the corresponding viewar as $data.
       
$view->viewVars = [
           
'countries' => 'dummy-country-would-normally-be-an-entity-or-resultset',
           
'currencies' => 'dummy-currency-would-normally-be-an-entity-or-resultset',
        ];

       
$parameters = [
           
'countries',
           
'currencies'
       
];

       
$this->assertSame(
           
'dummy-country-would-normally-be-an-entity-or-resultset',
           
$this->callProtectedMethod('_getDataToSerializeFromViewVars', [$parameters], $view)
        );

       
// In this case the first entity in the _serialize array does not have
        // a corresponding viewVar so null will be returned as data.
       
$view->viewVars = [
           
'currencies' => 'dummy-currency-would-normally-be-an-entity-or-resultset',
        ];

       
$parameters = [
           
'countries',
           
'currencies'
       
];

       
$this->assertNull($this->callProtectedMethod('_getDataToSerializeFromViewVars', [$parameters], $view));
    }

   
/**
     * Make sure `_serialize` will not accept an object
     *
     * @expectedException \Crud\Error\Exception\CrudException
     * @expectedExceptionMessage Assigning an object to JsonApiListener "_serialize" is deprecated, assign the object to its own variable and assign "_serialize" = true instead.
     */
   
public function testGetDataToSerializeFromViewVarsObjectExcecption()
    {
       
$view = $this
           
->getMockBuilder('\CrudJsonApi\View\JsonApiView')
            ->
disableOriginalConstructor()
            ->
setMethods(null)
            ->
getMock();

       
$this->setReflectionClassInstance($view);
       
$this->callProtectedMethod('_getDataToSerializeFromViewVars', [new StdClass()], $view);
    }

   
/**
     * Make sure we are producing the right jsonOptions
     *
     * @return void
     */
   
public function testJsonOptions()
    {
       
$view = $this
           
->getMockBuilder('\CrudJsonApi\View\JsonApiView')
            ->
disableOriginalConstructor()
            ->
setMethods(null)
            ->
getMock();

       
$this->setReflectionClassInstance($view);

       
// test debug mode with `debugPrettyPrint` option disabled
       
$this->assertTrue(Configure::read('debug'));
       
$view->viewVars = [
           
'_debugPrettyPrint' => false,
           
'_jsonOptions' => [
               
JSON_HEX_AMP, // 2
               
JSON_HEX_QUOT, // 8
           
]
        ];
       
$this->assertEquals(10, $this->callProtectedMethod('_jsonOptions', [], $view));

       
// test debug mode with `debugPrettyPrint` option enabled
       
$this->assertTrue(Configure::read('debug'));
       
$view->viewVars = [
           
'_debugPrettyPrint' => true, //128
           
'_jsonOptions' => [
               
JSON_HEX_AMP, // 2
               
JSON_HEX_QUOT, // 8
           
]
        ];
       
$this->assertEquals(138, $this->callProtectedMethod('_jsonOptions', [], $view));

       
// test production mode with `debugPrettyPrint` option disabled
       
Configure::write('debug', false);
       
$this->assertFalse(Configure::read('debug'));
       
$view->viewVars = [
           
'_debugPrettyPrint' => false,
           
'_jsonOptions' => [
               
JSON_HEX_AMP, // 2
               
JSON_HEX_QUOT, // 8
           
]
        ];
       
$this->assertEquals(10, $this->callProtectedMethod('_jsonOptions', [], $view));

       
// test production mode with `debugPrettyPrint` option enabled
       
$this->assertFalse(Configure::read('debug'));
       
$view->viewVars = [
           
'_debugPrettyPrint' => true,
           
'_jsonOptions' => [
               
JSON_HEX_AMP, // 2
               
JSON_HEX_QUOT, // 8
           
]
        ];
       
$this->assertEquals(10, $this->callProtectedMethod('_jsonOptions', [], $view));
    }

   
/**
     * Make sure ApiPaginationListener information is added to the
     * expected `links` and `meta` nodes in the response.
     *
     * @return void
     */
   
public function testApiPaginationListener()
    {
       
$countries = TableRegistry::get('Countries')->find()->first();

       
$view = $this->_getView('Countries', [
           
'countries' => $countries,
           
'_pagination' => [
               
'self' => '/countries?page=2',
               
'first' => '/countries?page=1',
               
'last' => '/countries?page=3',
               
'prev' => '/countries?page=1',
               
'next' => '/countries?page=3',
               
'record_count' => 28,
               
'page_count' => 3,
               
'page_limit' => 10,
            ]
        ]);

       
$jsonArray = json_decode($view->render(), true);

       
// assert `links` node is filled as expected
       
$this->assertArrayHasKey('links', $jsonArray);
       
$links = $jsonArray['links'];

       
$this->assertEquals($links['self'], '/countries?page=2');
       
$this->assertEquals($links['first'], '/countries?page=1');
       
$this->assertEquals($links['last'], '/countries?page=3');
       
$this->assertEquals($links['prev'], '/countries?page=1');
       
$this->assertEquals($links['next'], '/countries?page=3');

       
// assert `meta` node is filled as expected
       
$this->assertArrayHasKey('meta', $jsonArray);
       
$meta = $jsonArray['meta'];

       
$this->assertEquals($meta['record_count'], 28);
       
$this->assertEquals($meta['page_count'], 3);
       
$this->assertEquals($meta['page_limit'], 10);
    }

   
/**
     * Make sure NeoMerx pagination Links are generated from `_pagination`
     * viewVar set by ApiPaginationListener.
     *
     * @return void
     */
   
public function testGetPaginationLinks()
    {
       
$pagination = [
           
'self' => 'http://api.app/v0/countries?page=2',
           
'first' => 'http://api.app/v0/countries?page=1',
           
'last' => 'http://api.app/v0/countries?page=3',
           
'prev' => 'http://api.app/v0/countries?page=1',
           
'next' => 'http://api.app/v0/countries?page=3',
           
'record_count' => 42, // should be skipped
           
'page_count' => 3, // should be skipped
           
'page_limit' => 10 // should be skipped
       
];

       
$view = $this
           
->getMockBuilder('\CrudJsonApi\View\JsonApiView')
            ->
disableOriginalConstructor()
            ->
setMethods(null)
            ->
getMock();

       
$this->setReflectionClassInstance($view);
       
$links = $this->callProtectedMethod('_getPaginationLinks', [$pagination], $view);

       
// assert all 5 elements in the array are NeoMerx Link objects
       
$this->assertCount(5, $links);

        foreach (
$links as $link) {
           
$this->assertInstanceOf(Link::class, $link);
        }
    }

   
/**
     * Make sure ApiQueryLogListener information is added to the `query` node.
     *
     * @return void
     */
   
public function testApiQueryLogListener()
    {
       
$countries = TableRegistry::get('Countries')->find()->first();

       
$view = $this->_getView('Countries', [
           
'countries' => $countries,
           
'queryLog' => 'viewVar only set by ApiQueryLogListener'
       
]);

       
$jsonArray = json_decode($view->render(), true);

       
// assert `links` node is filled as expected
       
$this->assertArrayHasKey('query', $jsonArray);
       
$this->assertEquals($jsonArray['query'], 'viewVar only set by ApiQueryLogListener');
    }
}