Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
The Simpletest tests in rest module should use Guzzle instead of cURL because it would make the tests more readable (per @dawehner).
Proposed resolution
Change the rest module Simpletests to use GuzzleHttp\Client.
Remaining tasks
- Depends on #1447736: Adopt Guzzle library to replace drupal_http_request() and #1801570: DX: Replace hook_route_info() with YAML files and RouteBuildEvent (DONE)
Depends on Guzzle Cookie Plugin(No longer the case)- Write patch to change tests
- Review patch
User interface changes
None.
API changes
None.
Data model changes
None.
Comment | File | Size | Author |
---|---|---|---|
#113 | convert_rest_tests_to-1841474-113.patch | 17.19 KB | Wim Leers |
#99 | convert_rest_tests_to-1841474-99.patch | 16.83 KB | gnuget |
#99 | 1841474-97-99-interdiff.txt | 528 bytes | gnuget |
#97 | interdiff.txt | 8.28 KB | dawehner |
#97 | 1841474-97.patch | 16.79 KB | dawehner |
Comments
Comment #1
klausiI experimented with Guzzle a bit, but it is frustrating to use it in Simpletest instead of cURL. For access testing I need a user session, for that I need cookies. To get the cookies I need to perform a login. To login I need to use the correct Simpletest User agent. To login I need the form build id token to work around the CSRF protection. Finally, to retain the cookies I would need the Guzzle Cookie plugin which is not included in #1447736: Adopt Guzzle library to replace drupal_http_request().
But I even failed at issuing a successful login POST request and I'm giving up for now. Attached is the code I experimented with for future reference. I always got 403 access denied pages as response.
Comment #2
klausiComment #3
Wim LeersThis is an excellent novice task!
Comment #4
Saphyel CreditAttribution: Saphyel as a volunteer commentedFor this we need to use Guzzle\Plugin\Oauth\OauthPlugin or similar plugin
Comment #5
Wim LeersInteresting! Why?
Comment #6
mradcliffeUpdated issue summary. The WIP patch was for Guzzle 3 if I recall correctly, and Guzzle 6 handles cookies now.
I do not think that the OAuth1 subscriber is necessary for this test unless @Saphyel's idea is to add the oauth-subscriber dependency to Drupal?
Comment #7
lokapujyaModernizing the guzzle code.
Comment #8
Wim LeersComment #9
mradcliffeLooking at the re-roll of the original patch (thanks!), I do not think we are testing trying to do a HTTP POST to the login form. I think that we should refactor RESTTestBase::httpRequest() to use GuzzleHttp\Client instead of Curl.
Comment #10
lokapujyaComment #12
dawehnerWell, we need it for the entire method, not just delete requests ...
Comment #13
mradcliffeThis isn't necessary if you're using the global \Drupal object below.
This should either be a part of $options below as a query parameter or maybe set the Accept or Content-Type header.
Simpletest's curl stuff adds the session cookie, which is why the test is returning a 403.
http://docs.guzzlephp.org/en/latest/request-options.html#cookies
Here's what I got when I output headers from the old test. I don't think we need to add anything other than the cookie.
Also, RESTTestBase is probably going to need a new assert because assertResponse also depends on curl, and that's in WebTestBase which can't be easily refactored to be backwards-compatible.
Comment #14
lokapujyaI think we need to login with guzzle as mentioned in #1, I don't know if the cookie can be shared with drupallogin()?
Comment #15
dawehnerHere is a start. It seems to kinda work, but we really need some form of API that the test code stays sort of readable.
Comment #16
dawehner.
Comment #17
dawehnerLet's see how things turn out
Comment #19
dawehnerLet's see, how much this fixes.
Comment #21
lokapujyaAwesome, I really want to test this out when I get some free coding time. Probably not until next week.
With just a quick glance, should we remove some duplicate code for settings that are shared across request methods? :
Comment #22
dawehner@lokapujya
Yeah indeed, there is quite a bit of overlap I think.
Comment #23
lokapujyaComment #24
lokapujyarun tests.
Comment #25
dawehner@lokapujya
This seems to be for me a bit out of scope of this issue? This is changing WebTestBase quite a bit ...
Comment #26
lokapujyaWell, how else can we update the cookie for DrupalLogout() after changing the password? I think thats the problem.
Comment #27
lokapujyaAlso, It's only changing WebTestBase for the Guzzle tests. One option is that we have Guzzle completely manage the cookie from login.
Comment #28
lokapujyaHere is a start on #21. (And reroll for the head support that was added.)
Comment #29
lokapujyaWhat if we undo the WebTestBase, and instead overwrite the stored curl cookie with the guzzle cookie?
Comment #30
dawehnerWell, try it out :)
Comment #31
lokapujyaJust needed that motivation.
Bad Patch. Not Rebased.
Comment #32
lokapujyaComment #33
lokapujyaComment #38
lokapujyaDifferent approach. Just logout with Guzzle session, before trying to login again.
Comment #39
gnugetI gave it a quick review to the last patch and found some nits.
Can we remove this line?
This debug method call is on purpose? should we remove it before merge it?
missing comma here.
missing comma.
missing comma.
missing comma.
Also, this doesn't seem a good task for a novice developer anymore, I removed the tags.
Thanks!
Comment #40
dawehnerWell, actually the remaining points you found on this issue are a good novice task.
Comment #41
lluvigneHi,
I solved all the nitpicks remaining on the latest patch. Hope it works correctly.
Comment #42
dawehnerThank you for fixing the remaining review points.
I love it how it looks like now, but yeah someone else should review it properly now.
Comment #43
Wim LeersMissing docblock. And missing a lead verb. e.g.
setCookieCurlOptions()
would be a better name.Nit: Unnecessary blank line.
Let's document why we do this. I understand why. But let's document it, just like we did on the other test base classes that do this.
The bottom 3 lines have incorrect indentation: they all have two leading spaces too many.
Finally, I don't understand why we have so many mentions of "CURL" left if we are actually moving from CURL to Guzzle?
Comment #44
dawehnerThank you for the review wim!
Here is a try to use the guzzle native but rather than CURL direct cooke option.
Comment #46
dawehnerJust a reroll in the meantime.
Comment #48
dawehnerl
Comment #49
tedbowRetesting because the patch applied fine for me locally
Comment #51
Wim LeersNit: s/guzzle/Guzzle/
Can we move this code into a trait, so we can share it with
WebTestBase
andBrowserTestBase
?Comment #52
mradcliffeIt looks like the patch only applies to 8.2.x and the testbot was trying to run 8.1.x still even though Version is set to 8.2.x. I added the 8.2.x test and it is running.
Comment #55
dawehnerThis is blocking #2770921: Feb 21st: Convert chunk of WTB to BTB by just moving classes, changing use statements adding traits in some soft way at the moment. IMHO we should process with that.
Comment #56
Wim LeersDo you mean we should proceed with that other issue before this one, or the other way around?
Comment #57
dawehnerWell, all I'm saying is that using guzzle would be a improvement not just for the sake of it, which might some people believe in.
Comment #58
mradcliffeBasically we could add to the issue summary that the motivation is not only making the tests more readable, but would improve core developer experience to move to BTB.
Comment #59
Wim LeersRiight! Okay, thanks, that makes sense! :)
Comment #60
klausiI would argue that we should invent an ApiTestBase class as sibling of BrowserTestBase (not a child of it!) which uses Guzzle as a client but does not have all the Mink methods.
Comment #61
dawehnerNice idea!
Comment #62
Wim LeersInteresting :) Would that be appropriate to introduce here?
Comment #63
Wim LeersAlso, the fact that
UpdateTest
shows POST requests, not PATCH requests, is maddening. The reason for that seems at least equally maddening:In other words: 1000 times yes to this issue.
Bumping to major because the brittleness of REST's test coverage has caused major problems, major slowdowns during the 8.2.x cycle and will continue to do so unless we address it. This is one of the key things to make REST's test coverage trustworthy.
Comment #64
Wim LeersHaving fought our current tests once again, this time at #2795965-5: REST requests with invalid X-CSRF-Token header get "missing " mesage, I'm dropping everything to do this first. Not using CURL would have made several things there better. Plus, this is pretty much a hard blocker for #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method.
Comment #65
Wim LeersComment #66
dawehner@Wim Leers++
On the other hand its not cool, that you also had that level of pain with the existing test helpers.
Comment #67
Wim LeersFirst, a straight reroll so it applies to HEAD again. (This conflicted with #2681911: REST requests without X-CSRF-Token header: unhelpful response significantly hinders DX, should receive a 401 response.)
Still fails.
Comment #68
Wim LeersQuoting #1, from November 2012 (!!!!!!!!!!!):
Every single word of this is still true! Incredible. Although apparently that's since the refactor in #44/#46. Still trying to figure out the root cause. I verified that a cookie is correctly retrieved and sent. (By requesting
/user
, which indeed redirects to the authenticated user's profile page.) I've also ruled out that a missing?_format=hal_json
is the cause. No idea yet what the problem is.Comment #70
Wim LeersI've tracked the problem down further.
RESTTestBase::cookies()
's cookie jar does contain the correct cookie name and value. But it is not sent to the server. So, somewhere along the way, guzzle loses it. Either because we're not using Guzzle in the right way (likely), or Guzzle is not set up in the right way (also somewhat likely, according to the documentation in\GuzzleHttp\Middleware::cookies()
), or … Guzzle is broken (very unlikely).Since I'm writing this on a train, I don't have access to the full Guzzle documentation, but at least we now know exactly where things are going wrong: Guzzle is not sending cookies.
Comment #71
Wim LeersI've tracked the problem down further.
RESTTestBase::cookies()
's cookie jar does contain the correct cookie name and value. But it is not sent to the server. So, somewhere along the way, guzzle loses it. Either because we're not using Guzzle in the right way (likely), or Guzzle is not set up in the right way (also somewhat likely, according to the documentation in\GuzzleHttp\Middleware::cookies()
), or … Guzzle is broken (very unlikely).Since I'm writing this on a train, I don't have access to the full Guzzle documentation, but at least we now know exactly where things are going wrong: Guzzle is not sending cookies.
Comment #72
dawehnerI'm happy to have a look at that this morning, if you don't mind.
Comment #73
Wim LeersPlease do! That'd be splendid!
Comment #74
Wim LeersComment #75
dawehnerCool, glad you saw my post in your shaky train internet :)
Comment #76
dawehnerLet's see
There have been two problems:
\GuzzleHttp\Cookie\SetCookie::validate
Comment #78
dawehnerWhen a cookie is removed (a user is logged out and the session is removed), its send back with value 'deleted'. We set back this cookie, so the page cache believes we have a session opened.
I honestly don't get 100% yet, why this works in WTB, maybe we have a slightly different invoked CURL or so. This patch ensures that we don't send back 'deleted' cookies.
Comment #79
Wim LeersAwesome, great detective work, @dawehner! The remaining fail looks trivial to solve: Guzzle doesn't lowercase all headers like curl does. So these need to be updated to have the proper casing:
Comment #80
dawehnerIs this WTB which is doing the lowering?
Comment #81
Wim LeersApparently, yes, in
\Drupal\simpletest\WebTestBase::drupalGetHeaders()
there is astrtolower()
.Comment #82
dawehnerI'm done for this week, probably. Feel free to bring it home :)
Comment #84
dawehnerWell, then this should be it.
Comment #85
Wim LeersI think this is ready. This is a big step forward for the REST test coverage. Let's get this in, and then #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method can provide the next round of REST test improvements.
(This issue doesn't change anything about test comprehensiveness, but it improves test debuggability and maintainability. And as a bonus, it helps #2770921: Feb 21st: Convert chunk of WTB to BTB by just moving classes, changing use statements adding traits!)
Comment #86
Wim LeersOh and IMO this should go in 8.2.x, because #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method is also aimed at 8.2.x (to find any remaining REST bugs in 8.2.x, and to aid overall reliability of REST in 8.2.x), and this is a soft blocker for that issue.
Comment #87
alexpottConflicts with #2795965: REST requests with invalid X-CSRF-Token header get "missing " mesage - which has just landed...
Comment #88
gnuget#84's Reroll
Comment #90
gnugetFixed small error in my last reroll.
Comment #91
dawehnerThank you for the reroll @gnuget!
Comment #92
Wim Leers<3 gnuget — thanks a lot!
Comment #93
alexpottHow come this change is necessary?
Let's file a new issue to move this into a trait to get the XDEBUG_SESSION from the request and use it here, BrowserTestBase and SimpleTest...
[Edit: complete a sentence]
Comment #94
Wim LeersComment #95
mradcliffeRunning down memory lane (the comment history)...
We were trying to solve the issue of WTB adding session cookies on requests when changing the logged in user's password (#26). One approach was to add a conditional in httpRequest for Guzzle or Curl, or a modification of drupalLogout (up to #33), but that was changed in #38 to the current implementation.
Perhaps a comment based on @lokapujya's comments in #26, #29 and #38?
What about "Log out the user to clear the cookies used by the Guzzle client so that a log in request can be made for the changed user."?
Comment #96
mradcliffeI had time to make a patch of my suggestion above.
Comment #97
dawehnerComment #99
gnuget#97's Small fix.
Comment #100
dawehnerUps. I'm personally happy with this issue. Anyone else?
Comment #101
Wim LeersLooks like all feedback from #93 has been addressed. Thanks all!
Comment #102
alexpottCommitted d27d6c3 and pushed to 8.3.x. Thanks!
Comment #104
alexpottShould we backport this to 8.2.x once 8.2.0 is out? I think we should because test divergence is quite risky.
Comment #105
dawehnerThere is a slightly risk because people might leverage the base class, and well, as we have seen on the UpdateTest, they might be adapted.
Comment #106
Wim LeersYes.
The reason I worked on this is because it helps #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method, which aims to bring comprehensive test coverage for REST resources to 8.2.x. And in the process discover bugs (already happening), and then fixing them. Because bug fixes can go in 8.2.x, and we should make REST as reliable as possible in 8.2.x.
This helps with that.
Comment #107
mradcliffeSome documentation and maybe a change record would be nice as well to outline the new capabilities in the test runner.
Comment #108
Wim Leers@mradcliffe you mean for contrib/custom modules that ship with REST resource plugins, and have test coverage via
RESTTestBase
?Comment #109
mradcliffe@wimleers, or for someone who might want to use Guzzle in tests in general.
Comment #110
Wim LeersOh, you mean documentation for the new trait! That makes sense.
Comment #111
Wim LeersComment #113
Wim LeersStraight reroll.
Comment #115
mradcliffeTest fail for Test #521646 seems to be random. I re-rolled and didn't see any changes to the re-roll, ran the test locally on 8.2.x HEAD and it passed. Then I queued up the tests again and it passed.
Setting back to previous status set by Wim Leers in #113.
Comment #116
catchAre we sure that's not a new random fail introduced by the guzzle conversion? It's a REST test after all.
Comment #117
gnugetI reviewed the patch and re ran the tests and all looks good (also this was already committed to 8.3.x) so I really really think this is RTBC.
Comment #118
Wim Leers#2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method landed, which made the
RESTTestBase
-based test coverage mostly obsolete. #2824576: Delete old REST test coverage: (Read|Create|Update|Delete)Test, deprecate RESTTestBase then deleted most of that obsolete test coverage.Because of that, the value of this issue is much smaller now (but the insights I got here helped me a lot during #2737719!).
RESTTestBase
has been updated in 8.3, but not 8.2. So, the only thing we gain here, is improved consistency between 8.2 and 8.3.A reroll should be easy: just removing hunks. Then I'll RTBC and a committer can decide.
Comment #119
alexpottConsidering we've deprecated the class I think we should just leave it alone until we remove it.
Comment #120
dawehnerIt is marked as deprecate already, so yeah, I think we should just close this issue.
Comment #121
Wim LeersAlright, thanks for chiming in, @alexpott & @dawhener!
Comment #122
Wim LeersActually, this seems more accurate. This did get committed to 8.3.x.
Comment #123
anish.a CreditAttribution: anish.a at Axelerant commentedComment #124
Wim Leers