Out there in #2950132: Support PHPUnit 7 optionally in Drupal 8, while keeping support for ^6.5, all EntityResourceTestBase tests fail.

That is due to the following code:

diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
index 993957eaba..5fb8d0b6eb 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
@@ -1538,8 +1538,11 @@ protected function assert406Response(ResponseInterface $response) {
     else {
       // This is the desired response.
       $this->assertSame(406, $response->getStatusCode());
       $this->stringContains('?_format=' . static::$format . '>; rel="alternate"; type="' . static::$mimeType . '"', $response->getHeader('Link'));
      $this->stringContains('?_format=foobar>; rel="alternate"', $response->getHeader('Link'));
     }
   }
 

::stringContains signature in PHPUnit 7 uses scalar type hinting, and the second argument is type hinted to bool. That leads to a fatal 'cause $response->getHeader('Link')) returns an array.

However, that method expects a bool also in previous PHPUnit versions, so I am not sure what this code is really expected to do.

I was thinking that instead of ::stringContains there should really be an ::assertContains, but that also seems weird on an array.

So, what is that snippet really expected to assert?

Comments

mondrake created an issue. See original summary.

wim leers’s picture

Category: Bug report » Task
Issue tags: +API-First Initiative

It's meant to assert that the substring in the first parameter is present in the second parameter. The second parameter used to return a string, not an array

mondrake’s picture

Status: Active » Needs review
StatusFileSize
new1.07 KB

Like this then? Using assertContains on the first element of the array.

Status: Needs review » Needs work

The last submitted patch, 3: 3031466-3.patch, failed testing. View results

mondrake’s picture

Status: Needs work » Needs review
StatusFileSize
new1.24 KB
new1.24 KB

Looks like $response->getHeader('Link') sometimes return a string and sometimes an array...

wim leers’s picture

So apparently what I said in #2 is still true, sometimes 🙃

Status: Needs review » Needs work

The last submitted patch, 5: 3031466-5.patch, failed testing. View results

mondrake’s picture

Random fail in #5? Queued retest.

mondrake’s picture

Status: Needs work » Needs review

#5 passed.

Status: Needs review » Needs work

The last submitted patch, 5: 3031466-5.patch, failed testing. View results

mondrake’s picture

Status: Needs work » Needs review
wim leers’s picture

Title: EntityResourceTestBase tests fail under PHPUnit 7 » EntityResourceTestBase tests fail under PHPUnit 7, because it became stricter
Status: Needs review » Reviewed & tested by the community

The need for this change is caused by https://github.com/sebastianbergmann/phpunit/commit/5113b86ce113e531b65b....

All this change does is guarantee that string is passed to $this->stringContains(), instead of potentially string[].

RTBC. Thanks for doing this!

mondrake’s picture

Thank you. If anyone can, a review of #3031539: FileFieldTestBase tests fail under PHPUnit 7 would also be appreciated (and actually is more relevant for PHPUnit 7 compatibility).

mondrake’s picture

All this change does is guarantee that string is passed to $this->stringContains(), instead of potentially string[].

Actually no, this is also changing the call to assertContains instead of stringContains, which was, I think, misused.

wim leers’s picture

That's not what the patch is doing :)

wim leers’s picture

Status: Reviewed & tested by the community » Needs work

NW for #14/#15.

mondrake’s picture

Assigned: Unassigned » mondrake

Ooops..

mondrake’s picture

Assigned: mondrake » Unassigned
Status: Needs work » Needs review
StatusFileSize
new1.24 KB
new1.18 KB

stringContains is not an assertion method, it returns a PHPUnit_Framework_Constraint_StringContains matcher object, and expects the second argument to be a bool. Based on #2, we should have an assertContains instead.

mondrake’s picture

Title: EntityResourceTestBase tests fail under PHPUnit 7, because it became stricter » EntityResourceTestBase tests fail under PHPUnit 7
mondrake’s picture

Status: Needs review » Needs work

Uhm fails seem to uncover some bugs here. NW, I’ll pass here as I do not know enough about the matter at hand.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

berdir’s picture

Looks like there is sometimes no header at all and then it fails on that?

mondrake’s picture

Status: Needs work » Needs review
StatusFileSize
new1.27 KB
new1.17 KB

#22 thanks, so this maybe?

Status: Needs review » Needs work

The last submitted patch, 23: 3031466-23.patch, failed testing. View results

mondrake’s picture

Status: Needs work » Needs review
StatusFileSize
new1.23 KB
new1.28 KB

Per #2 and #3 then?

Status: Needs review » Needs work

The last submitted patch, 25: 3031466-25.patch, failed testing. View results

mondrake’s picture

So the problem seems to be here: the link header is

http://php-apache-jenkins-drupal-patches-101096/subdirectory/entity/entity_view_display/node.camelids.default?_format=xml>; rel="alternate"; type="text/xml", http://php-apache-jenkins-drupal-patches-101096/subdirectory/entity/entity_view_display/node.camelids.default?_format=foobar>; rel="alternate"; type=""

but expected to contain

?_format=xml>; rel="alternate"; type="text/xml; charset=UTF-8"

Clearly ; charset=UTF-8 is a wrong expectation within the 'type' parameter.

mondrake’s picture

StatusFileSize
new90.69 KB
new91.98 KB
mondrake’s picture

Status: Needs work » Needs review
mondrake’s picture

Title: EntityResourceTestBase tests fail under PHPUnit 7 » EntityResourceTestBase::assert406Response uses a wrong assertion and results in false positives (and fails for PHPUnit 7)

Status: Needs review » Needs work

The last submitted patch, 28: 3031466-28.patch, failed testing. View results

mondrake’s picture

Status: Needs work » Needs review
StatusFileSize
new92.76 KB
new797 bytes

Playing whac-a-mole

Status: Needs review » Needs work

The last submitted patch, 32: 3031466-32.patch, failed testing. View results

mondrake’s picture

Status: Needs work » Needs review
StatusFileSize
new3.23 KB
new94.81 KB

Status: Needs review » Needs work

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

mondrake’s picture

Status: Needs work » Needs review
StatusFileSize
new1.34 KB
new1.27 KB

#34 is getting too complicated, let's do something way simpler i.e. just split the $mimeType into the MIME type and charset, and use the MIME type only to compare against. Interdiff against #25.

wim leers’s picture

Status: Needs review » Reviewed & tested by the community

Thanks, @mondrake, #36 looks great :)

lendude’s picture

+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
@@ -1538,8 +1538,13 @@ protected function assert406Response(ResponseInterface $response) {
+      if ($actual_link_header) {

any way to find out when this if() triggers and add an else? Because currently we have no idea if this part of the test is EVER run, the if could be if ($i_am_always_false) and the test would still be green.

catch’s picture

Status: Reviewed & tested by the community » Needs review

Let's answer #38

mondrake’s picture

Status: Needs review » Reviewed & tested by the community

I've no straight answer to #38 (not an expert), but the fact that this entire method is meant to be removed by #2805279: Routing system + authentication system + format-specific routes (e.g. those in rest.module) = frustrating, unhelpful 403 responses instead of 406 responses, see the docblock

  /**
   * Asserts a 406 response… or in some cases a 403 response, because weirdness.
   *
   * Asserting a 406 response should be easy, but it's not, due to bugs.
   *
   * Drupal returns a 403 response instead of a 406 response when:
   * - there is a canonical route, i.e. one that serves HTML
   * - unless the user is logged in with any non-global authentication provider,
   *   because then they tried to access a route that requires the user to be
   *   authenticated, but they used an authentication provider that is only
   *   accepted for specific routes, and HTML routes never have such specific
   *   authentication providers specified. (By default, only 'cookie' is a
   *   global authentication provider.)
   *
   * @todo Remove this in https://www.drupal.org/node/2805279.
   *

Trying to figure out what to test in case if ($actual_link_header) { is FALSE means getting into understanding what should be checked if there's no 'Link' header, which is probably the scope of the other issue.

Here I'd suggest to keep the scope just to the replacementl of the misused $this->stringContains calls with the correct $this->assertContains ones. Then when #2805279: Routing system + authentication system + format-specific routes (e.g. those in rest.module) = frustrating, unhelpful 403 responses instead of 406 responses will be in, this method will be anyway removed.

Setting back to RTBC, tentatively, to see if other more educated comments pop up.

krzysztof domański’s picture

+1 RTBC

1. $response->getHeader() always returns an array so $this->assertTrue(is_array($actual_link_header)); is unnecessary.

2. We can use $response->hasHeader('Link').

vendor\psr\http-message\src\MessageInterface.php

/**
 * Checks if a header exists by the given case-insensitive name.
 *
 * @param string $name Case-insensitive header field name.
 * @return bool Returns true if any header names match the given header
 *     name using a case-insensitive string comparison. Returns false if
 *     no matching header name is found in the message.
 */
public function hasHeader($name);

/**
 * Retrieves a message header value by the given case-insensitive name.
 *
 * This method returns an array of all the header values of the given
 * case-insensitive header name.
 *
 * If the header does not appear in the message, this method MUST return an
 * empty array.
 *
 * @param string $name Case-insensitive header field name.
 * @return string[] An array of string values as provided for the given
 *    header. If the header does not appear in the message, this method MUST
 *    return an empty array.
 */
public function getHeader($name);

My suggestions

if ($response->hasHeader('Link')) {
  $actual_link_header = $response->getHeader('Link');
  $expected_type = explode(';', static::$mimeType)[0];
  $this->assertContains('?_format=' . static::$format . '>; rel="alternate"; type="' . $expected_type . '"', $actual_link_header[0]);
  $this->assertContains('?_format=foobar>; rel="alternate"', $actual_link_header[0]);
}

Interdiff

diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
index d4b111c..a5b6df6 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
@@ -1538,9 +1538,8 @@ protected function assert406Response(ResponseInterface $response) {
     else {
       // This is the desired response.
       $this->assertSame(406, $response->getStatusCode());
-      $actual_link_header = $response->getHeader('Link');
-      if ($actual_link_header) {
-        $this->assertTrue(is_array($actual_link_header));
+      if ($response->hasHeader('Link')) {
+        $actual_link_header = $response->getHeader('Link');
         $expected_type = explode(';', static::$mimeType)[0];
         $this->assertContains('?_format=' . static::$format . '>; rel="alternate"; type="' . $expected_type . '"', $actual_link_header[0]);
         $this->assertContains('?_format=foobar>; rel="alternate"', $actual_link_header[0]);
catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed b57f550 and pushed to 8.8.x. Thanks!

  • catch committed b57f550 on 8.8.x
    Issue #3031466 by mondrake, Wim Leers: EntityResourceTestBase::...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.