<commit_before><?php
/**
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link          http://cakephp.org CakePHP(tm) Project
 * @since         CakePHP(tm) v 3.0
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
 */
namespace Cake\Test\TestCase\View\Form;

use Cake\Collection\Collection;
use Cake\Network\Request;
use Cake\ORM\Entity;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use Cake\TestSuite\TestCase;
use Cake\Validation\Validator;
use Cake\View\Form\EntityContext;
use ArrayIterator;
use ArrayObject;

/**
 * Test stub.
 */
class Article extends Entity {
}

/**
 * Entity context test case.
 */
class EntityContextTest extends TestCase {

/**
 * Fixtures to use.
 *
 * @var array
 */
	public $fixtures = ['core.article', 'core.comment'];

/**
 * setup method.
 *
 * @return void
 */
	public function setUp() {
		parent::setUp();
		$this->request = new Request();
	}

/**
 * Test an invalid table scope throws an error.
 *
 * @expectedException \RuntimeException
 * @expectedExceptionMessage Unable to find table class for current entity
 */
	public function testInvalidTable() {
		$row = new \StdClass();
		$context = new EntityContext($this->request, [
			'entity' => $row,
		]);
	}

/**
 * Test operations with no entity.
 *
 * @return void
 */
	public function testOperationsNoEntity() {
		$context = new EntityContext($this->request, [
			'table' => 'Articles'
		]);

		$this->assertNull($context->val('title'));
		$this->assertFalse($context->isRequired('title'));
		$this->assertFalse($context->hasError('title'));
		$this->assertEquals('string', $context->type('title'));
		$this->assertEquals([], $context->error('title'));
		$this->assertEquals(
			['length' => null, 'precision' => null],
			$context->attributes('title'));
	}

/**
 * Test operations that lack a table argument.
 *
 * @return void
 */
	public function testOperationsNoTableArg() {
		$row = new Article([
			'title' => 'Test entity',
			'body' => 'Something new'
		]);
		$row->errors('title', ['Title is required.']);

		$context = new EntityContext($this->request, [
			'entity' => $row,
		]);

		$result = $context->val('title');
		$this->assertEquals($row->title, $result);

		$result = $context->error('title');
		$this->assertEquals($row->errors('title'), $result);
	}

/**
 * Test collection operations that lack a table argument.
 *
 * @dataProvider collectionProvider
 * @return void
 */
	public function testCollectionOperationsNoTableArg($collection) {
		$context = new EntityContext($this->request, [
			'entity' => $collection,
		]);

		$result = $context->val('0.title');
		$this->assertEquals('First post', $result);

		$result = $context->error('1.body');
		$this->assertEquals(['Not long enough'], $result);
	}

/**
 * Data provider for testing collections.
 *
 * @return array
 */
	public static function collectionProvider() {
		$one = new Entity([
			'title' => 'First post',
			'body' => 'Stuff',
			'user' => new Entity(['username' => 'mark'])
		]);
		$one->errors('title', 'Required field');

		$two = new Entity([
			'title' => 'Second post',
			'body' => 'Some text',
			'user' => new Entity(['username' => 'jose'])
		]);
		$two->errors('body', 'Not long enough');

		return [
			'array' => [[$one, $two]],
			'basic iterator' => [new ArrayObject([$one, $two])],
			'array iterator' => [new ArrayIterator([$one, $two])],
			'collection' => [new Collection([$one, $two])],
		];
	}

/**
 * Test operations on a collection of entities.
 *
 * @dataProvider collectionProvider
 * @return void
 */
	public function testValOnCollections($collection) {
		$context = new EntityContext($this->request, [
			'entity' => $collection,
			'table' => 'Articles',
		]);

		$result = $context->val('0.title');
		$this->assertEquals('First post', $result);

		$result = $context->val('0.user.username');
		$this->assertEquals('mark', $result);

		$result = $context->val('1.title');
		$this->assertEquals('Second post', $result);

		$result = $context->val('1.user.username');
		$this->assertEquals('jose', $result);
	}

/**
 * Test error operations on a collection of entities.
 *
 * @dataProvider collectionProvider
 * @return void
 */
	public function testErrorsOnCollections($collection) {
		$context = new EntityContext($this->request, [
			'entity' => $collection,
			'table' => 'Articles',
		]);

		$this->assertTrue($context->hasError('0.title'));
		$this->assertEquals(['Required field'], $context->error('0.title'));
		$this->assertFalse($context->hasError('0.body'));

		$this->assertFalse($context->hasError('1.title'));
		$this->assertEquals(['Not long enough'], $context->error('1.body'));
		$this->assertTrue($context->hasError('1.body'));
	}

/**
 * Test schema operations on a collection of entities.
 *
 * @dataProvider collectionProvider
 * @return void
 */
	public function testSchemaOnCollections($collection) {
		$this->_setupTables();
		$context = new EntityContext($this->request, [
			'entity' => $collection,
			'table' => 'Articles',
		]);

		$this->assertEquals('string', $context->type('0.title'));
		$this->assertEquals('text', $context->type('1.body'));
		$this->assertEquals('string', $context->type('0.user.username'));
		$this->assertEquals('string', $context->type('1.user.username'));
		$this->assertNull($context->type('0.nope'));

		$expected = ['length' => 255, 'precision' => null];
		$this->assertEquals($expected, $context->attributes('0.user.username'));
	}

/**
 * Test validation operations on a collection of entities.
 *
 * @dataProvider collectionProvider
 * @return void
 */
	public function testValidatorsOnCollections($collection) {
		$this->_setupTables();

		$context = new EntityContext($this->request, [
			'entity' => $collection,
			'table' => 'Articles',
			'validator' => [
				'Articles' => 'create',
				'Users' => 'custom',
			]
		]);

		$this->assertTrue($context->isRequired('0.title'));
		$this->assertFalse($context->isRequired('1.body'));
		$this->assertTrue($context->isRequired('0.user.username'));
	}

/**
 * Test reading data.
 *
 * @return void
 */
	public function testValBasic() {
		$row = new Entity([
			'title' => 'Test entity',
			'body' => 'Something new'
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);
		$result = $context->val('title');
		$this->assertEquals($row->title, $result);

		$result = $context->val('body');
		$this->assertEquals($row->body, $result);

		$result = $context->val('nope');
		$this->assertNull($result);
	}

/**
 * Test reading values from associated entities.
 *
 * @return void
 */
	public function testValAssociated() {
		$row = new Entity([
			'title' => 'Test entity',
			'user' => new Entity([
				'username' => 'mark',
				'fname' => 'Mark'
			]),
			'comments' => [
				new Entity(['comment' => 'Test comment']),
				new Entity(['comment' => 'Second comment']),
			]
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);

		$result = $context->val('user.fname');
		$this->assertEquals($row->user->fname, $result);

		$result = $context->val('comments.0.comment');
		$this->assertEquals($row->comments[0]->comment, $result);

		$result = $context->val('comments.1.comment');
		$this->assertEquals($row->comments[1]->comment, $result);

		$result = $context->val('comments.0.nope');
		$this->assertNull($result);

		$result = $context->val('comments.0.nope.no_way');
		$this->assertNull($result);
	}

/**
 * Test validator as a string.
 *
 * @return void
 */
	public function testIsRequiredStringValidator() {
		$this->_setupTables();

		$context = new EntityContext($this->request, [
			'entity' => new Entity(),
			'table' => 'Articles',
			'validator' => 'create',
		]);

		$this->assertTrue($context->isRequired('title'));
		$this->assertFalse($context->isRequired('body'));

		$this->assertFalse($context->isRequired('Herp.derp.derp'));
		$this->assertFalse($context->isRequired('nope'));
		$this->assertFalse($context->isRequired(''));
	}

/**
 * Test isRequired on associated entities.
 *
 * @return void
 */
	public function testIsRequiredAssociatedHasMany() {
		$this->_setupTables();

		$comments = TableRegistry::get('Comments');
		$validator = $comments->validator();
		$validator->add('user_id', 'number', [
			'rule' => 'numeric',
		]);

		$row = new Entity([
			'title' => 'My title',
			'comments' => [
				new Entity(['comment' => 'First comment']),
				new Entity(['comment' => 'Second comment']),
			]
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
			'validator' => 'default',
		]);

		$this->assertTrue($context->isRequired('comments.0.user_id'));
		$this->assertFalse($context->isRequired('comments.0.other'));
		$this->assertFalse($context->isRequired('user.0.other'));
		$this->assertFalse($context->isRequired(''));
	}

/**
 * Test isRequired on associated entities with custom validators.
 *
 * @return void
 */
	public function testIsRequiredAssociatedValidator() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'comments' => [
				new Entity(['comment' => 'First comment']),
				new Entity(['comment' => 'Second comment']),
			]
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
			'validator' => [
				'Articles' => 'create',
				'Comments' => 'custom'
			]
		]);

		$this->assertTrue($context->isRequired('title'));
		$this->assertFalse($context->isRequired('body'));
		$this->assertTrue($context->isRequired('comments.0.comment'));
		$this->assertTrue($context->isRequired('comments.1.comment'));
	}

/**
 * Test isRequired on associated entities.
 *
 * @return void
 */
	public function testIsRequiredAssociatedBelongsTo() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'user' => new Entity(['username' => 'Mark']),
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
			'validator' => [
				'Articles' => 'create',
				'Users' => 'custom'
			]
		]);

		$this->assertTrue($context->isRequired('user.username'));
		$this->assertFalse($context->isRequired('user.first_name'));
	}

/**
 * Test type() basic
 *
 * @return void
 */
	public function testType() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'body' => 'Some content',
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);

		$this->assertEquals('string', $context->type('title'));
		$this->assertEquals('text', $context->type('body'));
		$this->assertEquals('integer', $context->type('user_id'));
		$this->assertNull($context->type('nope'));
	}

/**
 * Test getting types for associated records.
 *
 * @return void
 */
	public function testTypeAssociated() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'user' => new Entity(['username' => 'Mark']),
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);

		$this->assertEquals('string', $context->type('user.username'));
		$this->assertEquals('text', $context->type('user.bio'));
		$this->assertNull($context->type('user.nope'));
	}

/**
 * Test attributes for fields.
 *
 * @return void
 */
	public function testAttributes() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'user' => new Entity(['username' => 'Mark']),
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);

		$expected = [
			'length' => 255, 'precision' => null
		];
		$this->assertEquals($expected, $context->attributes('title'));

		$expected = [
			'length' => null, 'precision' => null
		];
		$this->assertEquals($expected, $context->attributes('body'));

		$expected = [
			'length' => 10, 'precision' => 3
		];
		$this->assertEquals($expected, $context->attributes('user.rating'));
	}

/**
 * Test hasError
 *
 * @return void
 */
	public function testHasError() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'user' => new Entity(['username' => 'Mark']),
		]);
		$row->errors('title', []);
		$row->errors('body', 'Gotta have one');
		$row->errors('user_id', ['Required field']);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);

		$this->assertFalse($context->hasError('title'));
		$this->assertFalse($context->hasError('nope'));
		$this->assertTrue($context->hasError('body'));
		$this->assertTrue($context->hasError('user_id'));
	}

/**
 * Test hasError on associated records
 *
 * @return void
 */
	public function testHasErrorAssociated() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'user' => new Entity(['username' => 'Mark']),
		]);
		$row->errors('title', []);
		$row->errors('body', 'Gotta have one');
		$row->user->errors('username', ['Required']);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);

		$this->assertTrue($context->hasError('user.username'));
		$this->assertFalse($context->hasError('user.nope'));
		$this->assertFalse($context->hasError('no.nope'));
	}

/**
 * Test error
 *
 * @return void
 */
	public function testError() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'user' => new Entity(['username' => 'Mark']),
		]);
		$row->errors('title', []);
		$row->errors('body', 'Gotta have one');
		$row->errors('user_id', ['Required field']);

		$row->user->errors('username', ['Required']);

		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);

		$this->assertEquals([], $context->error('title'));

		$expected = ['Gotta have one'];
		$this->assertEquals($expected, $context->error('body'));

		$expected = ['Required'];
		$this->assertEquals($expected, $context->error('user.username'));
	}

/**
 * Setup tables for tests.
 *
 * @return void
 */
	protected function _setupTables() {
		$articles = TableRegistry::get('Articles');
		$articles->belongsTo('Users');
		$articles->hasMany('Comments');

		$comments = TableRegistry::get('Comments');
		$users = TableRegistry::get('Users');

		$articles->schema([
			'id' => ['type' => 'integer', 'length' => 11, 'null' => false],
			'title' => ['type' => 'string', 'length' => 255],
			'user_id' => ['type' => 'integer', 'length' => 11, 'null' => false],
			'body' => ['type' => 'text']
		]);
		$users->schema([
			'id' => ['type' => 'integer', 'length' => 11],
			'username' => ['type' => 'string', 'length' => 255],
			'bio' => ['type' => 'text'],
			'rating' => ['type' => 'decimal', 'length' => 10, 'precision' => 3],
		]);

		$validator = new Validator();
		$validator->add('title', 'minlength', [
			'rule' => ['minlength', 10]
		])
		->add('body', 'maxlength', [
			'rule' => ['maxlength', 1000]
		])->allowEmpty('body');
		$articles->validator('create', $validator);

		$validator = new Validator();
		$validator->add('username', 'length', [
			'rule' => ['minlength', 10]
		]);
		$users->validator('custom', $validator);

		$validator = new Validator();
		$validator->add('comment', 'length', [
			'rule' => ['minlength', 10]
		]);
		$comments->validator('custom', $validator);
	}

}
<commit_msg>Add additional test coverage.<commit_after><?php
/**
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link          http://cakephp.org CakePHP(tm) Project
 * @since         CakePHP(tm) v 3.0
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
 */
namespace Cake\Test\TestCase\View\Form;

use Cake\Collection\Collection;
use Cake\Network\Request;
use Cake\ORM\Entity;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use Cake\TestSuite\TestCase;
use Cake\Validation\Validator;
use Cake\View\Form\EntityContext;
use ArrayIterator;
use ArrayObject;

/**
 * Test stub.
 */
class Article extends Entity {
}

/**
 * Entity context test case.
 */
class EntityContextTest extends TestCase {

/**
 * Fixtures to use.
 *
 * @var array
 */
	public $fixtures = ['core.article', 'core.comment'];

/**
 * setup method.
 *
 * @return void
 */
	public function setUp() {
		parent::setUp();
		$this->request = new Request();
	}

/**
 * Test an invalid table scope throws an error.
 *
 * @expectedException \RuntimeException
 * @expectedExceptionMessage Unable to find table class for current entity
 */
	public function testInvalidTable() {
		$row = new \StdClass();
		$context = new EntityContext($this->request, [
			'entity' => $row,
		]);
	}

/**
 * Test operations with no entity.
 *
 * @return void
 */
	public function testOperationsNoEntity() {
		$context = new EntityContext($this->request, [
			'table' => 'Articles'
		]);

		$this->assertNull($context->val('title'));
		$this->assertFalse($context->isRequired('title'));
		$this->assertFalse($context->hasError('title'));
		$this->assertEquals('string', $context->type('title'));
		$this->assertEquals([], $context->error('title'));
		$this->assertEquals(
			['length' => null, 'precision' => null],
			$context->attributes('title'));
	}

/**
 * Test operations that lack a table argument.
 *
 * @return void
 */
	public function testOperationsNoTableArg() {
		$row = new Article([
			'title' => 'Test entity',
			'body' => 'Something new'
		]);
		$row->errors('title', ['Title is required.']);

		$context = new EntityContext($this->request, [
			'entity' => $row,
		]);

		$result = $context->val('title');
		$this->assertEquals($row->title, $result);

		$result = $context->error('title');
		$this->assertEquals($row->errors('title'), $result);
		$this->assertTrue($context->hasError('title'));
	}

/**
 * Test collection operations that lack a table argument.
 *
 * @dataProvider collectionProvider
 * @return void
 */
	public function testCollectionOperationsNoTableArg($collection) {
		$context = new EntityContext($this->request, [
			'entity' => $collection,
		]);

		$result = $context->val('0.title');
		$this->assertEquals('First post', $result);

		$result = $context->error('1.body');
		$this->assertEquals(['Not long enough'], $result);
	}

/**
 * Data provider for testing collections.
 *
 * @return array
 */
	public static function collectionProvider() {
		$one = new Entity([
			'title' => 'First post',
			'body' => 'Stuff',
			'user' => new Entity(['username' => 'mark'])
		]);
		$one->errors('title', 'Required field');

		$two = new Entity([
			'title' => 'Second post',
			'body' => 'Some text',
			'user' => new Entity(['username' => 'jose'])
		]);
		$two->errors('body', 'Not long enough');

		return [
			'array' => [[$one, $two]],
			'basic iterator' => [new ArrayObject([$one, $two])],
			'array iterator' => [new ArrayIterator([$one, $two])],
			'collection' => [new Collection([$one, $two])],
		];
	}

/**
 * Test operations on a collection of entities.
 *
 * @dataProvider collectionProvider
 * @return void
 */
	public function testValOnCollections($collection) {
		$context = new EntityContext($this->request, [
			'entity' => $collection,
			'table' => 'Articles',
		]);

		$result = $context->val('0.title');
		$this->assertEquals('First post', $result);

		$result = $context->val('0.user.username');
		$this->assertEquals('mark', $result);

		$result = $context->val('1.title');
		$this->assertEquals('Second post', $result);

		$result = $context->val('1.user.username');
		$this->assertEquals('jose', $result);
	}

/**
 * Test error operations on a collection of entities.
 *
 * @dataProvider collectionProvider
 * @return void
 */
	public function testErrorsOnCollections($collection) {
		$context = new EntityContext($this->request, [
			'entity' => $collection,
			'table' => 'Articles',
		]);

		$this->assertTrue($context->hasError('0.title'));
		$this->assertEquals(['Required field'], $context->error('0.title'));
		$this->assertFalse($context->hasError('0.body'));

		$this->assertFalse($context->hasError('1.title'));
		$this->assertEquals(['Not long enough'], $context->error('1.body'));
		$this->assertTrue($context->hasError('1.body'));
	}

/**
 * Test schema operations on a collection of entities.
 *
 * @dataProvider collectionProvider
 * @return void
 */
	public function testSchemaOnCollections($collection) {
		$this->_setupTables();
		$context = new EntityContext($this->request, [
			'entity' => $collection,
			'table' => 'Articles',
		]);

		$this->assertEquals('string', $context->type('0.title'));
		$this->assertEquals('text', $context->type('1.body'));
		$this->assertEquals('string', $context->type('0.user.username'));
		$this->assertEquals('string', $context->type('1.user.username'));
		$this->assertNull($context->type('0.nope'));

		$expected = ['length' => 255, 'precision' => null];
		$this->assertEquals($expected, $context->attributes('0.user.username'));
	}

/**
 * Test validation operations on a collection of entities.
 *
 * @dataProvider collectionProvider
 * @return void
 */
	public function testValidatorsOnCollections($collection) {
		$this->_setupTables();

		$context = new EntityContext($this->request, [
			'entity' => $collection,
			'table' => 'Articles',
			'validator' => [
				'Articles' => 'create',
				'Users' => 'custom',
			]
		]);

		$this->assertTrue($context->isRequired('0.title'));
		$this->assertFalse($context->isRequired('1.body'));
		$this->assertTrue($context->isRequired('0.user.username'));
	}

/**
 * Test reading data.
 *
 * @return void
 */
	public function testValBasic() {
		$row = new Entity([
			'title' => 'Test entity',
			'body' => 'Something new'
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);
		$result = $context->val('title');
		$this->assertEquals($row->title, $result);

		$result = $context->val('body');
		$this->assertEquals($row->body, $result);

		$result = $context->val('nope');
		$this->assertNull($result);
	}

/**
 * Test reading values from associated entities.
 *
 * @return void
 */
	public function testValAssociated() {
		$row = new Entity([
			'title' => 'Test entity',
			'user' => new Entity([
				'username' => 'mark',
				'fname' => 'Mark'
			]),
			'comments' => [
				new Entity(['comment' => 'Test comment']),
				new Entity(['comment' => 'Second comment']),
			]
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);

		$result = $context->val('user.fname');
		$this->assertEquals($row->user->fname, $result);

		$result = $context->val('comments.0.comment');
		$this->assertEquals($row->comments[0]->comment, $result);

		$result = $context->val('comments.1.comment');
		$this->assertEquals($row->comments[1]->comment, $result);

		$result = $context->val('comments.0.nope');
		$this->assertNull($result);

		$result = $context->val('comments.0.nope.no_way');
		$this->assertNull($result);
	}

/**
 * Test validator as a string.
 *
 * @return void
 */
	public function testIsRequiredStringValidator() {
		$this->_setupTables();

		$context = new EntityContext($this->request, [
			'entity' => new Entity(),
			'table' => 'Articles',
			'validator' => 'create',
		]);

		$this->assertTrue($context->isRequired('title'));
		$this->assertFalse($context->isRequired('body'));

		$this->assertFalse($context->isRequired('Herp.derp.derp'));
		$this->assertFalse($context->isRequired('nope'));
		$this->assertFalse($context->isRequired(''));
	}

/**
 * Test isRequired on associated entities.
 *
 * @return void
 */
	public function testIsRequiredAssociatedHasMany() {
		$this->_setupTables();

		$comments = TableRegistry::get('Comments');
		$validator = $comments->validator();
		$validator->add('user_id', 'number', [
			'rule' => 'numeric',
		]);

		$row = new Entity([
			'title' => 'My title',
			'comments' => [
				new Entity(['comment' => 'First comment']),
				new Entity(['comment' => 'Second comment']),
			]
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
			'validator' => 'default',
		]);

		$this->assertTrue($context->isRequired('comments.0.user_id'));
		$this->assertFalse($context->isRequired('comments.0.other'));
		$this->assertFalse($context->isRequired('user.0.other'));
		$this->assertFalse($context->isRequired(''));
	}

/**
 * Test isRequired on associated entities with custom validators.
 *
 * @return void
 */
	public function testIsRequiredAssociatedValidator() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'comments' => [
				new Entity(['comment' => 'First comment']),
				new Entity(['comment' => 'Second comment']),
			]
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
			'validator' => [
				'Articles' => 'create',
				'Comments' => 'custom'
			]
		]);

		$this->assertTrue($context->isRequired('title'));
		$this->assertFalse($context->isRequired('body'));
		$this->assertTrue($context->isRequired('comments.0.comment'));
		$this->assertTrue($context->isRequired('comments.1.comment'));
	}

/**
 * Test isRequired on associated entities.
 *
 * @return void
 */
	public function testIsRequiredAssociatedBelongsTo() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'user' => new Entity(['username' => 'Mark']),
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
			'validator' => [
				'Articles' => 'create',
				'Users' => 'custom'
			]
		]);

		$this->assertTrue($context->isRequired('user.username'));
		$this->assertFalse($context->isRequired('user.first_name'));
	}

/**
 * Test type() basic
 *
 * @return void
 */
	public function testType() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'body' => 'Some content',
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);

		$this->assertEquals('string', $context->type('title'));
		$this->assertEquals('text', $context->type('body'));
		$this->assertEquals('integer', $context->type('user_id'));
		$this->assertNull($context->type('nope'));
	}

/**
 * Test getting types for associated records.
 *
 * @return void
 */
	public function testTypeAssociated() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'user' => new Entity(['username' => 'Mark']),
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);

		$this->assertEquals('string', $context->type('user.username'));
		$this->assertEquals('text', $context->type('user.bio'));
		$this->assertNull($context->type('user.nope'));
	}

/**
 * Test attributes for fields.
 *
 * @return void
 */
	public function testAttributes() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'user' => new Entity(['username' => 'Mark']),
		]);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);

		$expected = [
			'length' => 255, 'precision' => null
		];
		$this->assertEquals($expected, $context->attributes('title'));

		$expected = [
			'length' => null, 'precision' => null
		];
		$this->assertEquals($expected, $context->attributes('body'));

		$expected = [
			'length' => 10, 'precision' => 3
		];
		$this->assertEquals($expected, $context->attributes('user.rating'));
	}

/**
 * Test hasError
 *
 * @return void
 */
	public function testHasError() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'user' => new Entity(['username' => 'Mark']),
		]);
		$row->errors('title', []);
		$row->errors('body', 'Gotta have one');
		$row->errors('user_id', ['Required field']);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);

		$this->assertFalse($context->hasError('title'));
		$this->assertFalse($context->hasError('nope'));
		$this->assertTrue($context->hasError('body'));
		$this->assertTrue($context->hasError('user_id'));
	}

/**
 * Test hasError on associated records
 *
 * @return void
 */
	public function testHasErrorAssociated() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'user' => new Entity(['username' => 'Mark']),
		]);
		$row->errors('title', []);
		$row->errors('body', 'Gotta have one');
		$row->user->errors('username', ['Required']);
		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);

		$this->assertTrue($context->hasError('user.username'));
		$this->assertFalse($context->hasError('user.nope'));
		$this->assertFalse($context->hasError('no.nope'));
	}

/**
 * Test error
 *
 * @return void
 */
	public function testError() {
		$this->_setupTables();

		$row = new Entity([
			'title' => 'My title',
			'user' => new Entity(['username' => 'Mark']),
		]);
		$row->errors('title', []);
		$row->errors('body', 'Gotta have one');
		$row->errors('user_id', ['Required field']);

		$row->user->errors('username', ['Required']);

		$context = new EntityContext($this->request, [
			'entity' => $row,
			'table' => 'Articles',
		]);

		$this->assertEquals([], $context->error('title'));

		$expected = ['Gotta have one'];
		$this->assertEquals($expected, $context->error('body'));

		$expected = ['Required'];
		$this->assertEquals($expected, $context->error('user.username'));
	}

/**
 * Setup tables for tests.
 *
 * @return void
 */
	protected function _setupTables() {
		$articles = TableRegistry::get('Articles');
		$articles->belongsTo('Users');
		$articles->hasMany('Comments');

		$comments = TableRegistry::get('Comments');
		$users = TableRegistry::get('Users');

		$articles->schema([
			'id' => ['type' => 'integer', 'length' => 11, 'null' => false],
			'title' => ['type' => 'string', 'length' => 255],
			'user_id' => ['type' => 'integer', 'length' => 11, 'null' => false],
			'body' => ['type' => 'text']
		]);
		$users->schema([
			'id' => ['type' => 'integer', 'length' => 11],
			'username' => ['type' => 'string', 'length' => 255],
			'bio' => ['type' => 'text'],
			'rating' => ['type' => 'decimal', 'length' => 10, 'precision' => 3],
		]);

		$validator = new Validator();
		$validator->add('title', 'minlength', [
			'rule' => ['minlength', 10]
		])
		->add('body', 'maxlength', [
			'rule' => ['maxlength', 1000]
		])->allowEmpty('body');
		$articles->validator('create', $validator);

		$validator = new Validator();
		$validator->add('username', 'length', [
			'rule' => ['minlength', 10]
		]);
		$users->validator('custom', $validator);

		$validator = new Validator();
		$validator->add('comment', 'length', [
			'rule' => ['minlength', 10]
		]);
		$comments->validator('custom', $validator);
	}

}

{'fraction_non_alphanumeric': 0.21828155897174203, 'fraction_numerical': 0.004337564585060918, 'mean_word_length': 4.225833333333333, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 713, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '406368', 'n_tokens_mistral': 11069, 'n_tokens_neox': 9952, 'n_words': 2056}