Browse Source

Merge branch 'master' into 3.next

Mark Story 8 years ago
parent
commit
c9a059ce1b

+ 5 - 1
.travis.yml

@@ -4,6 +4,7 @@ php:
   - 7.0
   - 5.6
   - 7.1
+  - nightly
 
 dist: trusty
 
@@ -31,6 +32,9 @@ cache:
 
 matrix:
   fast_finish: true
+  
+  allow_failures:
+    - php: nightly
 
   include:
     - php: 7.0
@@ -43,7 +47,7 @@ matrix:
       env: PHPSTAN=1 DEFAULT=0
 
 before_install:
-  - if [ $TRAVIS_PHP_VERSION != 7.0 ]; then phpenv config-rm xdebug.ini; fi
+  - if [ $TRAVIS_PHP_VERSION != 7.0 && $TRAVIS_PHP_VERSION != 'nightly' ]; then phpenv config-rm xdebug.ini; fi
 
   - if [ $DB = 'mysql' ]; then mysql -u root -e 'CREATE DATABASE cakephp_test;'; fi
   - if [ $DB = 'mysql' ]; then mysql -u root -e 'CREATE DATABASE cakephp_test2;'; fi

+ 9 - 2
src/Auth/BaseAuthenticate.php

@@ -110,15 +110,22 @@ abstract class BaseAuthenticate implements EventListenerInterface
             return false;
         }
 
+        $passwordField = $this->_config['fields']['password'];
         if ($password !== null) {
             $hasher = $this->passwordHasher();
-            $hashedPassword = $result->get($this->_config['fields']['password']);
+            $hashedPassword = $result->get($passwordField);
             if (!$hasher->check($password, $hashedPassword)) {
                 return false;
             }
 
             $this->_needsPasswordRehash = $hasher->needsRehash($hashedPassword);
-            $result->unsetProperty($this->_config['fields']['password']);
+            $result->unsetProperty($passwordField);
+        }
+        $hidden = $result->getHidden();
+        if ($password === null && in_array($passwordField, $hidden)) {
+            $key = array_search($passwordField, $hidden);
+            unset($hidden[$key]);
+            $result->setHidden($hidden);
         }
 
         return $result->toArray();

+ 5 - 0
src/Console/ConsoleIo.php

@@ -205,6 +205,11 @@ class ConsoleIo
         if ($newlines) {
             $this->out($this->nl($newlines), 0);
         }
+
+        // Store length of content + fill so if the new content
+        // is shorter than the old content the next overwrite
+        // will work.
+        $this->_lastWritten = $newBytes + $fill;
     }
 
     /**

+ 3 - 1
src/Database/QueryCompiler.php

@@ -125,7 +125,9 @@ class QueryCompiler
     protected function _sqlCompiler(&$sql, $query, $generator)
     {
         return function ($parts, $name) use (&$sql, $query, $generator) {
-            if (!count($parts)) {
+            if (!isset($parts) ||
+                ((is_array($parts) || $parts instanceof \Countable) && !count($parts))
+            ) {
                 return;
             }
             if ($parts instanceof ExpressionInterface) {

+ 1 - 1
src/Datasource/EntityTrait.php

@@ -419,7 +419,7 @@ trait EntityTrait
     /**
      * Sets hidden properties.
      *
-     * @param array $properties An array of properties to treat as virtual.
+     * @param array $properties An array of properties to hide from array exports.
      * @param bool $merge Merge the new properties with the existing. By default false.
      * @return $this
      */

+ 38 - 0
src/Http/Client.php

@@ -20,6 +20,7 @@ use Cake\Http\Client\CookieCollection;
 use Cake\Http\Client\Request;
 use Cake\Http\Cookie\CookieInterface;
 use Cake\Utility\Hash;
+use Zend\Diactoros\Uri;
 
 /**
  * The end user interface for doing HTTP requests.
@@ -383,6 +384,43 @@ class Client
      */
     public function send(Request $request, $options = [])
     {
+        $redirects = 0;
+        if (isset($options['redirect'])) {
+            $redirects = (int)$options['redirect'];
+            unset($options['redirect']);
+        }
+
+        do {
+            $response = $this->_sendRequest($request, $options);
+
+            $handleRedirect = $response->isRedirect() && $redirects-- > 0;
+            if ($handleRedirect) {
+                $url = $request->getUri();
+                $request->cookie($this->_cookies->get($url));
+
+                $location = $response->getHeaderLine('Location');
+                $locationUrl = $this->buildUrl($location, [], [
+                    'host' => $url->getHost(),
+                    'port' => $url->getPort(),
+                    'scheme' => $url->getScheme()
+                ]);
+
+                $request = $request->withUri(new Uri($locationUrl));
+            }
+        } while ($handleRedirect);
+
+        return $response;
+    }
+
+    /**
+     * Send a request without redirection.
+     *
+     * @param \Cake\Http\Client\Request $request The request to send.
+     * @param array $options Additional options to use.
+     * @return \Cake\Http\Client\Response
+     */
+    protected function _sendRequest(Request $request, $options)
+    {
         $responses = $this->_adapter->send($request, $options);
         $url = $request->getUri();
         foreach ($responses as $response) {

+ 1 - 1
src/I18n/Parser/MoFileParser.php

@@ -119,7 +119,7 @@ class MoFileParser
             fseek($stream, $offset);
             $translated = fread($stream, $length);
 
-            if (strpos($translated, "\000") !== false) {
+            if ($pluralId !== null || strpos($translated, "\000") !== false) {
                 $translated = explode("\000", $translated);
                 $plurals = $pluralId !== null ? array_map('stripcslashes', $translated) : null;
                 $translated = $translated[0];

+ 12 - 6
src/Mailer/Email.php

@@ -882,7 +882,7 @@ class Email implements JsonSerializable, Serializable
     protected function _setEmail($varName, $email, $name)
     {
         if (!is_array($email)) {
-            $this->_validateEmail($email);
+            $this->_validateEmail($email, $varName);
             if ($name === null) {
                 $name = $email;
             }
@@ -895,7 +895,7 @@ class Email implements JsonSerializable, Serializable
             if (is_int($key)) {
                 $key = $value;
             }
-            $this->_validateEmail($key);
+            $this->_validateEmail($key, $varName);
             $list[$key] = $value;
         }
         $this->{$varName} = $list;
@@ -907,10 +907,11 @@ class Email implements JsonSerializable, Serializable
      * Validate email address
      *
      * @param string $email Email address to validate
+     * @param string $context Which property was set
      * @return void
      * @throws \InvalidArgumentException If email address does not validate
      */
-    protected function _validateEmail($email)
+    protected function _validateEmail($email, $context)
     {
         if ($this->_emailPattern === null) {
             if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
@@ -919,7 +920,12 @@ class Email implements JsonSerializable, Serializable
         } elseif (preg_match($this->_emailPattern, $email)) {
             return;
         }
-        throw new InvalidArgumentException(sprintf('Invalid email: "%s"', $email));
+
+        $context = ltrim($context, '_');
+        if ($email == '') {
+            throw new InvalidArgumentException(sprintf('The email set for "%s" is empty.', $context));
+        }
+        throw new InvalidArgumentException(sprintf('Invalid email set for "%s". You passed "%s".', $context, $email));
     }
 
     /**
@@ -958,7 +964,7 @@ class Email implements JsonSerializable, Serializable
     protected function _addEmail($varName, $email, $name)
     {
         if (!is_array($email)) {
-            $this->_validateEmail($email);
+            $this->_validateEmail($email, $varName);
             if ($name === null) {
                 $name = $email;
             }
@@ -971,7 +977,7 @@ class Email implements JsonSerializable, Serializable
             if (is_int($key)) {
                 $key = $value;
             }
-            $this->_validateEmail($key);
+            $this->_validateEmail($key, $varName);
             $list[$key] = $value;
         }
         $this->{$varName} = array_merge($this->{$varName}, $list);

+ 3 - 2
src/Network/Session.php

@@ -371,7 +371,7 @@ class Session
      * Returns given session variable, or all of them, if no parameters given.
      *
      * @param string|null $name The name of the session variable (or a path as sent to Hash.extract)
-     * @return string|null The value of the session variable, null if session not available,
+     * @return string|array|null The value of the session variable, null if session not available,
      *   session not started, or provided name not found in the session.
      */
     public function read($name = null)
@@ -538,7 +538,8 @@ class Session
     {
         return !ini_get('session.use_cookies')
             || isset($_COOKIE[session_name()])
-            || $this->_isCLI;
+            || $this->_isCLI
+            || (ini_get('session.use_trans_sid') && isset($_GET[session_name()]));
     }
 
     /**

+ 1 - 1
src/ORM/Query.php

@@ -782,6 +782,7 @@ class Query extends DatabaseQuery implements JsonSerializable, QueryInterface
     public function cleanCopy()
     {
         $clone = clone $this;
+        $clone->setEagerLoader(clone $this->getEagerLoader());
         $clone->triggerBeforeFind();
         $clone->enableAutoFields(false);
         $clone->limit(null);
@@ -791,7 +792,6 @@ class Query extends DatabaseQuery implements JsonSerializable, QueryInterface
         $clone->formatResults(null, true);
         $clone->setSelectTypeMap(new TypeMap());
         $clone->decorateResults(null, true);
-        $clone->setEagerLoader(clone $this->getEagerLoader());
 
         return $clone;
     }

+ 3 - 1
src/Validation/composer.json

@@ -24,9 +24,11 @@
     "require": {
         "php": ">=5.6.0",
         "cakephp/utility": "^3.0.0",
-        "cakephp/i18n": "^3.0.0",
         "psr/http-message": "^1.0.0"
     },
+    "suggest": {
+        "cakephp/i18n": "If you want to use Validation::localizedTime()"
+    },
     "autoload": {
         "psr-4": {
             "Cake\\Validation\\": "."

+ 3 - 2
src/View/Form/EntityContext.php

@@ -14,6 +14,7 @@
  */
 namespace Cake\View\Form;
 
+use ArrayAccess;
 use Cake\Collection\Collection;
 use Cake\Datasource\EntityInterface;
 use Cake\Http\ServerRequest;
@@ -259,7 +260,7 @@ class EntityContext implements ContextInterface
 
             return $this->_schemaDefault($part, $entity);
         }
-        if (is_array($entity)) {
+        if (is_array($entity) || $entity instanceof ArrayAccess) {
             $key = array_pop($parts);
 
             return isset($entity[$key]) ? $entity[$key] : null;
@@ -483,7 +484,7 @@ class EntityContext implements ContextInterface
      */
     protected function _getTable($parts, $fallback = true)
     {
-        if (count($parts) === 1) {
+        if (!is_array($parts) || count($parts) === 1) {
             return $this->_tables[$this->_rootName];
         }
 

+ 45 - 0
tests/TestCase/Auth/DigestAuthenticateTest.php

@@ -23,10 +23,19 @@ use Cake\Http\Response;
 use Cake\Http\ServerRequest;
 use Cake\I18n\Time;
 use Cake\Network\Exception\UnauthorizedException;
+use Cake\ORM\Entity;
 use Cake\ORM\TableRegistry;
 use Cake\TestSuite\TestCase;
 
 /**
+ * Entity for testing with hidden fields.
+ */
+class ProtectedUser extends Entity
+{
+    protected $_hidden = ['password'];
+}
+
+/**
  * Test case for DigestAuthentication
  */
 class DigestAuthenticateTest extends TestCase
@@ -269,6 +278,42 @@ class DigestAuthenticateTest extends TestCase
     }
 
     /**
+     * test authenticate success even when digest 'password' is a hidden field.
+     *
+     * @return void
+     */
+    public function testAuthenticateSuccessHiddenPasswordField()
+    {
+        $User = TableRegistry::get('Users');
+        $User->setEntityClass(ProtectedUser::class);
+
+        $request = new ServerRequest([
+            'url' => 'posts/index',
+            'environment' => ['REQUEST_METHOD' => 'GET']
+        ]);
+        $request->addParams(['pass' => []]);
+
+        $data = [
+            'uri' => '/dir/index.html',
+            'nonce' => $this->generateNonce(),
+            'nc' => 1,
+            'cnonce' => '123',
+            'qop' => 'auth',
+        ];
+        $data['response'] = $this->auth->generateResponseHash($data, '09faa9931501bf30f0d4253fa7763022', 'GET');
+        $request->env('PHP_AUTH_DIGEST', $this->digestHeader($data));
+
+        $result = $this->auth->authenticate($request, $this->response);
+        $expected = [
+            'id' => 1,
+            'username' => 'mariano',
+            'created' => new Time('2007-03-17 01:16:23'),
+            'updated' => new Time('2007-03-17 01:18:31')
+        ];
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
      * test authenticate success
      *
      * @return void

+ 51 - 0
tests/TestCase/Console/ConsoleIoTest.php

@@ -333,6 +333,57 @@ class ConsoleIoTest extends TestCase
     }
 
     /**
+     * Test overwriting content with shorter content
+     *
+     * @return void
+     */
+    public function testOverwriteShorterContent()
+    {
+        $length = strlen('12345');
+
+        $this->out->expects($this->at(0))
+            ->method('write')
+            ->with('12345')
+            ->will($this->returnValue($length));
+
+        // Backspaces
+        $this->out->expects($this->at(1))
+            ->method('write')
+            ->with(str_repeat("\x08", $length), 0)
+            ->will($this->returnValue($length));
+
+        $this->out->expects($this->at(2))
+            ->method('write')
+            ->with('123', 0)
+            ->will($this->returnValue(3));
+
+        // 2 spaces output to pad up to 5 bytes
+        $this->out->expects($this->at(3))
+            ->method('write')
+            ->with(str_repeat(' ', $length - 3), 0)
+            ->will($this->returnValue($length - 3));
+
+        // Backspaces
+        $this->out->expects($this->at(4))
+            ->method('write')
+            ->with(str_repeat("\x08", $length), 0)
+            ->will($this->returnValue($length));
+
+        $this->out->expects($this->at(5))
+            ->method('write')
+            ->with('12', 0)
+            ->will($this->returnValue(2));
+
+        $this->out->expects($this->at(6))
+            ->method('write')
+            ->with(str_repeat(' ', $length - 2), 0);
+
+        $this->io->out('12345');
+        $this->io->overwrite('123', 0);
+        $this->io->overwrite('12', 0);
+    }
+
+    /**
      * Tests that setLoggers works properly
      *
      * @return void

+ 2 - 2
tests/TestCase/DatabaseSuite.php

@@ -40,9 +40,9 @@ class DatabaseSuite extends TestSuite
         return $suite;
     }
 
-    public function count()
+    public function count($preferCache = false)
     {
-        return parent::count() * 2;
+        return parent::count($preferCache) * 2;
     }
 
     /**

+ 86 - 0
tests/TestCase/Http/ClientTest.php

@@ -684,4 +684,90 @@ class ClientTest extends TestCase
         ]);
         $this->assertSame($result, $response);
     }
+
+    /**
+     * test redirects
+     *
+     * @return void
+     */
+    public function testRedirects()
+    {
+        $url = 'http://cakephp.org';
+
+        $adapter = $this->getMockBuilder(Client\Adapter\Stream::class)
+            ->setMethods(['send'])
+            ->getMock();
+
+        $redirect = new Response([
+            'HTTP/1.0 301',
+            'Location: http://cakephp.org/redirect1?foo=bar',
+            'Set-Cookie: redirect1=true;path=/',
+        ]);
+        $adapter->expects($this->at(0))
+            ->method('send')
+            ->with(
+                $this->callback(function ($request) use ($url) {
+                    $this->assertInstanceOf(Request::class, $request);
+                    $this->assertEquals($url, $request->getUri());
+
+                    return true;
+                }),
+                $this->callback(function ($options) {
+                    $this->assertArrayNotHasKey('redirect', $options);
+
+                    return true;
+                })
+            )
+            ->willReturn([$redirect]);
+
+        $redirect2 = new Response([
+            'HTTP/1.0 301',
+            'Location: /redirect2#foo',
+            'Set-Cookie: redirect2=true;path=/',
+        ]);
+        $adapter->expects($this->at(1))
+            ->method('send')
+            ->with(
+                $this->callback(function ($request) use ($url) {
+                    $this->assertInstanceOf(Request::class, $request);
+                    $this->assertEquals($url . '/redirect1?foo=bar', $request->getUri());
+
+                    return true;
+                }),
+                $this->callback(function ($options) {
+                    $this->assertArrayNotHasKey('redirect', $options);
+
+                    return true;
+                })
+            )
+            ->willReturn([$redirect2]);
+
+        $response = new Response([
+            'HTTP/1.0 200'
+        ]);
+        $adapter->expects($this->at(2))
+            ->method('send')
+            ->with($this->callback(function ($request) use ($url) {
+                $this->assertInstanceOf(Request::class, $request);
+                $this->assertEquals($url . '/redirect2#foo', $request->getUri());
+
+                return true;
+            }))
+            ->willReturn([$response]);
+
+        $client = new Client([
+            'adapter' => $adapter
+        ]);
+
+        $result = $client->send(new Request($url), [
+            'redirect' => 10
+        ]);
+
+        $this->assertInstanceOf(Response::class, $result);
+        $this->assertTrue($result->isOk());
+        $cookies = $client->cookies()->get($url);
+
+        $this->assertArrayHasKey('redirect1', $cookies);
+        $this->assertArrayHasKey('redirect2', $cookies);
+    }
 }

+ 21 - 0
tests/TestCase/I18n/Parser/MoFileParserTest.php

@@ -46,6 +46,27 @@ class MoFileParserTest extends TestCase
     }
 
     /**
+     * Tests parsing a file with single form plurals
+     *
+     * @return void
+     */
+    public function testParse0()
+    {
+        $parser = new MoFileParser;
+        $file = APP . 'Locale' . DS . 'rule_0_mo' . DS . 'core.mo';
+        $messages = $parser->parse($file);
+        $this->assertCount(3, $messages);
+        $expected = [
+            'Plural Rule 1 (from core)' => 'Plural Rule 0 (from core translated)',
+            '%d = 1 (from core)' => '%d ends with any # (from core translated)',
+            '%d = 0 or > 1 (from core)' => [
+                '%d ends with any # (from core translated)',
+            ],
+        ];
+        $this->assertEquals($expected, $messages);
+    }
+
+    /**
      * Tests parsing a file with larger plural forms
      *
      * @return void

+ 15 - 1
tests/TestCase/Mailer/EmailTest.php

@@ -400,7 +400,7 @@ class EmailTest extends TestCase
      * @return void
      *
      * @expectedException \InvalidArgumentException
-     * @expectedExceptionMessage Invalid email: "fail.@example.com"
+     * @expectedExceptionMessage Invalid email set for "to". You passed "fail.@example.com".
      */
     public function testUnsetEmailPattern()
     {
@@ -415,6 +415,20 @@ class EmailTest extends TestCase
     }
 
     /**
+     * Tests that passing an empty string throws an InvalidArgumentException.
+     *
+     * @return void
+     *
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage The email set for "to" is empty.
+     */
+    public function testEmptyTo()
+    {
+        $email = new Email();
+        $email->setTo('');
+    }
+
+    /**
      * testFormatAddress method
      *
      * @return void

+ 102 - 3
tests/TestCase/Network/SessionTest.php

@@ -44,6 +44,25 @@ class TestDatabaseSession extends DatabaseSession
 }
 
 /**
+ * Overwrite Session to simulate a web session even if the test runs on CLI.
+ */
+class TestWebSession extends Session
+{
+
+    protected function _hasSession()
+    {
+        $isCLI = $this->_isCLI;
+        $this->_isCLI = false;
+
+        $result = parent::_hasSession();
+
+        $this->_isCLI = $isCLI;
+
+        return $result;
+    }
+}
+
+/**
  * SessionTest class
  */
 class SessionTest extends TestCase
@@ -121,7 +140,7 @@ class SessionTest extends TestCase
             ]
         ];
 
-        $session = Session::create($config);
+        Session::create($config);
         $this->assertEquals('', ini_get('session.use_trans_sid'), 'Ini value is incorrect');
         $this->assertEquals('example.com', ini_get('session.referer_check'), 'Ini value is incorrect');
         $this->assertEquals('test', ini_get('session.name'), 'Ini value is incorrect');
@@ -136,10 +155,10 @@ class SessionTest extends TestCase
     {
         ini_set('session.cookie_path', '/foo');
 
-        $session = new Session();
+        new Session();
         $this->assertEquals('/', ini_get('session.cookie_path'));
 
-        $session = new Session(['cookiePath' => '/base']);
+        new Session(['cookiePath' => '/base']);
         $this->assertEquals('/base', ini_get('session.cookie_path'));
     }
 
@@ -569,4 +588,84 @@ class SessionTest extends TestCase
         new Session(['cookie' => 'made_up_name']);
         $this->assertEquals('made_up_name', session_name());
     }
+
+    /**
+     * Test that a call of check() starts the session when cookies are disabled in php.ini
+     */
+    public function testCheckStartsSessionWithCookiesDisabled()
+    {
+        $_COOKIE = [];
+        $_GET = [];
+
+        $session = new TestWebSession([
+            'ini' => [
+                'session.use_cookies' => 0,
+                'session.use_trans_sid' => 0,
+            ]
+        ]);
+
+        $this->assertFalse($session->started());
+        $session->check('something');
+        $this->assertTrue($session->started());
+    }
+
+    /**
+     * Test that a call of check() starts the session when a cookie is already set
+     */
+    public function testCheckStartsSessionWithCookie()
+    {
+        $_COOKIE[session_name()] = '123abc';
+        $_GET = [];
+
+        $session = new TestWebSession([
+            'ini' => [
+                'session.use_cookies' => 1,
+                'session.use_trans_sid' => 0,
+            ]
+        ]);
+
+        $this->assertFalse($session->started());
+        $session->check('something');
+        $this->assertTrue($session->started());
+    }
+
+    /**
+     * Test that a call of check() starts the session when the session ID is passed via URL and session.use_trans_sid is enabled
+     */
+    public function testCheckStartsSessionWithSIDinURL()
+    {
+        $_COOKIE = [];
+        $_GET[session_name()] = '123abc';
+
+        $session = new TestWebSession([
+            'ini' => [
+                'session.use_cookies' => 1,
+                'session.use_trans_sid' => 1,
+            ]
+        ]);
+
+        $this->assertFalse($session->started());
+        $session->check('something');
+        $this->assertTrue($session->started());
+    }
+
+    /**
+     * Test that a call of check() does not start the session when the session ID is passed via URL and session.use_trans_sid is disabled
+     */
+    public function testCheckDoesntStartSessionWithoutTransSID()
+    {
+        $_COOKIE = [];
+        $_GET[session_name()] = '123abc';
+
+        $session = new TestWebSession([
+            'ini' => [
+                'session.use_cookies' => 1,
+                'session.use_trans_sid' => 0,
+            ]
+        ]);
+
+        $this->assertFalse($session->started());
+        $session->check('something');
+        $this->assertFalse($session->started());
+    }
 }

+ 28 - 0
tests/TestCase/ORM/QueryRegressionTest.php

@@ -742,6 +742,34 @@ class QueryRegressionTest extends TestCase
     }
 
     /**
+     * Test count() with inner join containments.
+     *
+     * @return void
+     */
+    public function testCountWithInnerJoinContain()
+    {
+        $this->loadFixtures('Articles', 'Authors');
+        $table = TableRegistry::get('Articles');
+        $table->belongsTo('Authors')->setJoinType('INNER');
+
+        $result = $table->save($table->newEntity([
+            'author_id' => null,
+            'title' => 'title',
+            'body' => 'body',
+            'published' => 'Y'
+        ]));
+        $this->assertNotFalse($result);
+
+        $table->eventManager()
+            ->on('Model.beforeFind', function (Event $event, $query) {
+                $query->contain(['Authors']);
+            });
+
+        $count = $table->find()->count();
+        $this->assertEquals(3, $count);
+    }
+
+    /**
      * Tests that bind in subqueries works.
      *
      * @return void

+ 7 - 5
tests/TestCase/View/Form/EntityContextTest.php

@@ -467,7 +467,8 @@ class EntityContextTest extends TestCase
                 'name' => 'Test tag',
             ],
             'author' => new Entity([
-                'roles' => ['admin', 'publisher']
+                'roles' => ['admin', 'publisher'],
+                'aliases' => new ArrayObject(['dave', 'david']),
             ])
         ]);
         $context = new EntityContext($this->request, [
@@ -483,11 +484,12 @@ class EntityContextTest extends TestCase
         $result = $context->val('tag.name');
         $this->assertEquals($row->tag['name'], $result);
 
-        $result = $context->val('tag.nope');
-        $this->assertNull($result);
+        $result = $context->val('author.aliases.0');
+        $this->assertEquals($row->author->aliases[0], $result, 'ArrayAccess can be read');
 
-        $result = $context->val('author.roles.3');
-        $this->assertNull($result);
+        $this->assertNull($context->val('author.aliases.3'));
+        $this->assertNull($context->val('tag.nope'));
+        $this->assertNull($context->val('author.roles.3'));
     }
 
     /**