I just read this blog post by Wim Leers and apparently each entity type should have a bunch of test cases attached that test its REST functionality – even config entity types.
So, since we define three entity types, we should add such tests for all of them. (Whether it really makes sense for tasks is a bit of a question, but then again, why not? We should at least make sure they have proper access restrictions in place (don't want anonymous users to create tasks) and I guess it's at least theoretically possible that someone will want to review existing tasks via REST.)

(Note: Great instructions for how to add such tests are in the EntityResourceTestBase class doc comment – and in the blog post linked above, of course.)

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

drunken monkey created an issue. See original summary.

drunken monkey’s picture

Issue summary: View changes
borisson_’s picture

Issue tags: +Novice

Tagging this as novice, as there isn't really an "intermediate"-type of tag. Because of all the examples in core, it should be easy to resolve this.

borisson_’s picture

Status: Active » Needs review
FileSize
7.82 KB

We can only do this for content entities, so that means only for the task entity type. We should be able to do this for config entities in the future.

This doesn't work yet, and I don't know why that is, but at least this is a start.

Status: Needs review » Needs work

The last submitted patch, 4: 2929603.patch, failed testing. View results

borisson_’s picture

Looks like that last-minute change before the patch upload got us some messages that were easier to test. Fixing this now

borisson_’s picture

Status: Needs work » Needs review
FileSize
8.25 KB
4.78 KB

I can't reproduce those fails locally, I keep getting this:

-{"message":"No route found for \u0022GET entity\/search_api_task\/1\u0022"}
+{"message":"No route found for \u0022GET \/entity\/search_api_task\/1\u0022"}
 <?xml version="1.0"?>
-<response><message>No route found for "GET entity/search_api_task/1"</message></response>
+<response><message>No route found for "GET /entity/search_api_task/1"</message></response>

I don't understand why that is happening.

We should be getting more errors now, I copied these tests from media, and looks like I didn't rename all of them correctly.

I also added 2 @group annotations to the tests, because that makes it a little easier to run these: ./vendor/bin/phpunit -c core/ modules/globalignore/search_api --group rest

borisson_’s picture

Status: Needs review » Needs work

The last submitted patch, 7: 2929603-7.patch, failed testing. View results

drunken monkey’s picture

Issue tags: -Novice

I also added 2 @group annotations to the tests, because that makes it a little easier to run these: ./vendor/bin/phpunit -c core/ modules/globalignore/search_api --group rest

Makes sense for now, but would you say we should also commit it like this? Not sure what the consequences of that would be (if any, apart from making testing those easier).
Also: Why not just ./vendor/bin/phpunit -c core/ modules/globalignore/search_api/tests/src/Functional/EntityResource?

We can only do this for content entities, so that means only for the task entity type. We should be able to do this for config entities in the future.

Would be a nice simplification – but are you sure about that? It seems like Core provides tests for all config entity types as well.

Regarding the patch itself: Seems like a great start, thanks! Unfortunately, I also couldn't figure out why the tests are failing. (Also, I had completely different fails with not a single passing test method, so even worse.) Or what's going on at all.

I don't really think tagging this as "Novice" is fair, I'd feel bad for any actual novice trying to take this on. It seems more like we'd need a REST expert to work on this, not a Search API expert.

borisson_’s picture

Makes sense for now, but would you say we should also commit it like this?

Probably not.

We can only do this for content entities, so that means only for the task entity type. We should be able to do this for config entities in the future.

Would be a nice simplification – but are you sure about that? It seems like Core provides tests for all config entity types as well.

I was wrong, this can be done for config as well, but let's fix Task first, before we do the other entities.

I don't really think tagging this as "Novice" is fair, I'd feel bad for any actual novice trying to take this on. It seems more like we'd need a REST expert to work on this, not a Search API expert.

I agree, if both of us can't figure it out yet, asking a novice to work on this wouldn't be great. I'll did review a couple of the issues that went into drupal core regarding this, but even that knowledge isn't enough to help me figure out the fails.

Wim Leers’s picture

Issue tags: +API-First Initiative

❤️

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
2.11 KB
8.48 KB

We should at least make sure they have proper access restrictions in place

+1. Especially when using https://www.drupal.org/project/jsonapi this is important (and we want to move that into core in 8.6), because that exposes all entities by default, and relies on entity access to ensure access is given to only those who should be allowed to access it!

We can only do this for content entities, so that means only for the task entity type. We should be able to do this for config entities in the future.

That's not quite true — it's possible to view (GET) config entities. Run e.g. \Drupal\Tests\rest\Functional\EntityResource\NodeType\NodeTypeJsonBasicAuthTest and observe :)

#7: I cannot reproduce those failures locally (on Drupal 8.6.x + PHP 7 + latest search_api 8.x-1.x). I can reproduce the failures that testbot is showing.


Patch review:

  1. +++ b/tests/src/Functional/EntityResource/TaskJsonAnonTest.php
    @@ -0,0 +1,27 @@
    +namespace Drupal\Tests\search_api\Functional\EntityResource;
    

    Nit: I'd suggest moving these into a Rest directory rather than EntityResource. That'll make 10x more sense for you as a contrib module :)

    (That's also the location that \Drupal\Tests\rest\Functional\EntityResource\EntityResourceRestTestCoverageTest checks, in case this module ever makes into core, or in case that test gets somehow generalized to run for contrib modules.)

  2. +++ b/tests/src/Functional/EntityResource/TaskResourceTestBase.php
    @@ -0,0 +1,132 @@
    +use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
    ...
    +  use BcTimestampNormalizerUnixTestTrait;
    

    Unused, can be removed. (There are no timestamp fields in the Task content entity type.)

  3. +++ b/tests/src/Functional/EntityResource/TaskResourceTestBase.php
    @@ -0,0 +1,132 @@
    +      ->set('type', 'barn_owl')
    

    😀

  4. +++ b/tests/src/Functional/EntityResource/TaskResourceTestBase.php
    @@ -0,0 +1,132 @@
    +  protected function getNormalizedPostEntity() {
    +    return [
    +      'title' => [
    +        [
    +          'value' => 'Feed Resource Post Test',
    +        ],
    +      ],
    +      'url' => [
    +        [
    +          'value' => 'http://example.com/feed',
    +        ],
    +      ],
    +      'refresh' => [
    +        [
    +          'value' => 900,
    +        ],
    +      ],
    +      'description' => [
    +        [
    +          'value' => 'Feed Resource Post Test Description',
    +        ],
    +      ],
    +    ];
    +  }
    

    C/P remnant.

  5. +++ b/tests/src/Functional/EntityResource/TaskResourceTestBase.php
    @@ -0,0 +1,132 @@
    +    switch ($method) {
    +      case 'GET':
    +      case 'POST':
    +      case 'PATCH':
    +      case 'DELETE':
    +        return "You are not authorized to view this search_api_task entity.";
    +      default:
    +        return parent::getExpectedUnauthorizedAccessMessage($method);
    +    }
    

    These need to be updated too.


Test failures:

Failed asserting that Array &0 (
    0 => ''
) is identical to Array &0 (
    0 => 'user.permissions'
).

Seems to be the most complex/puzzling test failure. It's caused by the fact that the Task entity type … does NOT specify an admin_permission annotation key! So this is pointless:

+++ b/tests/src/Functional/EntityResource/TaskResourceTestBase.php
@@ -0,0 +1,132 @@
+  protected function setUpAuthorization($method) {
+    $this->grantPermissionsToTestedRole(['administer search_api']);
+  }

The reason can be found in \Drupal\Core\Entity\EntityAccessControlHandler::checkAccess():

  protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
    if ($operation == 'delete' && $entity->isNew()) {
      return AccessResult::forbidden()->addCacheableDependency($entity);
    }
    if ($admin_permission = $this->entityType->getAdminPermission()) {
      return AccessResult::allowedIfHasPermission($account, $admin_permission);
    }
    else {
      // No opinion.
      return AccessResult::neutral();
    }
  }

It ends up executing the last branch, and hence there's no cacheability to be associated, hence no user.permissions cache context.

Adding an admin_permission annotation key fixes that. Tests then fail just a little bit further, because then both url.site and url.permissions are expected. url.site is expected by default because 99% of entity types specify link relations. And link relations cause Link response headers to be generated, containing absolute URLs, and those then of course vary by which site in a multi-site setup is being accessed. That's not the case here, so we need to override that default expectation.

From then onward, the failures are more obvious, and I think you'll be easily able to drive them to completion :)

Status: Needs review » Needs work

The last submitted patch, 13: 2929603-13.patch, failed testing. View results

borisson_’s picture

Status: Needs work » Needs review
FileSize
7.26 KB
8.04 KB

I still have those same failures locally - which makes it really hard to see if the things I'm doing are actually improving the patch. (php7.1, drupal 8.6 and latest search api 8.x-1.x)

Status: Needs review » Needs work

The last submitted patch, 15: 2929603.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

drunken monkey’s picture

Status: Needs work » Needs review
FileSize
1.87 KB
8.02 KB

Thanks for your continued work here, and thanks for jumping in, Wim!
With the additional hints, I was able to get a bit further, but now I'm stuck again (and out of time for today): apparently there is an unavoidable test during POST (EntityResourceTestBase.php:857) which ensures you can't add a second value to the label field – but we don't have a label field for tasks, so not sure if that will even work. But it fails with an unexpected 403 response anyways, so couldn't even find out if it does.

Status: Needs review » Needs work

The last submitted patch, 17: 2929603-17--add_rest_tests.patch, failed testing. View results

borisson_’s picture

Status: Needs work » Needs review
FileSize
566 bytes
7.99 KB

I reinstalled mamp and now am running on php7.2, this made it so that I could reproduce the errors and I fixed them.

I'm not sure if we should add new issues for the other entities or if we should do that in one go.

drunken monkey’s picture

Oh, that's all that was needed? Awesome, great job, thanks!
As per your request, I'm leaving this open for the index/server tests.
But thanks again, in any case!

borisson_’s picture

Not sure if that was the big thing, but at least that made it so that I could reproduce the failures.

I added the work I did on the Index and Server, but this needs a fix in core tests.

@Wim Leers, do you think this is something sane to be added in core tests or should we try to fix this in Search API? The problem is that the not all links that we add to the Index class trough the annotation don't exist in the link relation type plugin manager.

The last submitted patch, 21: 2929603-21.patch, failed testing. View results

borisson_’s picture

1) Drupal\Tests\search_api\Functional\Rest\IndexXmlCookieTest::testGet
Drupal\Component\Plugin\Exception\PluginNotFoundException: The "fields" plugin does not exist.

That was the problem that is fixed with the core patch.

The problem that we see with the Anon test is something that I don't know how to fix:

1) Drupal\Tests\search_api\Functional\Rest\ServerXmlAnonTest::testGet
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@

-The used authentication method is not allowed on this route.
+The 'administer search_api' permission is required.

borisson_’s picture

Status: Needs review » Needs work

The last submitted patch, 24: 2929603-24.patch, failed testing. View results

borisson_’s picture

Status: Needs work » Needs review
FileSize
1.17 KB
22.99 KB

Status: Needs review » Needs work

The last submitted patch, 26: 2929603.patch, failed testing. View results

borisson_’s picture

The core patch I added in #21 was wrong and I fixed that by adding the correct link_relation_types in #26. Now all we need to to is figure out the 6 remaining failures, but I don't have a clue why they are happening and how to resolve them.

borisson_’s picture

Issue summary: View changes
FileSize
136.16 KB

I asked @tedbow for help on this in slack.

tedbow [4:32 PM]
@borisson_ so first 1 seems related to missing cache tags on when you don’t send auth

As expected, his assesment is correct, the expected cache tags are:

array(3) {
  [0]=>
  string(12) "4xx-response"
  [1]=>
  string(26) "config:user.role.anonymous"
  [2]=>
  string(13) "http_response"
}

The actual cache tags ($response->getHeader('X-Drupal-Cache-Tags')) are empty (array(0) {}).

I've tried figuring out why this happens, but I can't seem to find the reason.
I tried doing this by just getting the same page with Insomnia, but it looks like not getting the correct result is actually the correct response. At least it looks like that is what is happening.

drunken monkey’s picture

I've also tried looking into it, but I'm stumped as well. I did uncover some useful information (I think/hope), but wasn't able to convert it to any actual change that would make this pass:

  • The test expects the following cache tags on the response: 4xx-response, config:user.role.anonymous, http_response.
  • The default entity access handler correctly sets the user.permissions cache context on the access result (since it's caused by the (anonymous) user not having the Search API admin permission). As I see it, this would lead to the second cache tag being correctly applied via \Drupal\Core\Cache\Context\AccountPermissionsCacheContext::getCacheableMetadata(). The other two just seem to specify the kind of response, so would likely be added at some other point in the process.
  • However, that cache metadata never reaches the response, because the thrown exception is a "pure" Symfony AccessDeniedHttpException, not a Drupal CacheableAccessDeniedHttpException. This leads to a responce object that also doesn't contain any cache metadata (see \Drupal\serialization\EventSubscriber\DefaultExceptionSubscriber::on4xx()).
  • The reason for the wrong exception seems to be that the access check happens via \Drupal\Core\Routing\AccessAwareRouter::checkAccess(), not in \Drupal\rest\Plugin\rest\resource\EntityResource::get() (which throws the correct exception).
  • Why this is the case, and how the Search API can change it – I don't know.
borisson_’s picture

I tried all afternoon to figure out why these failures were happening, but I didn't get one any closer. We're going to need help on this one.