Previously on nginx discovery, I told you I was about to move from my python-based testing to agentzh's Test::Nginx. I was half-way through and the other half was nto the easiest one, so I figured I would share with you the pitfalls I ran into (just to avoid you reading through the perl code like I had to.
URL encoding. Yes, I am a lazy guy and I refuse to commit to memory the hexa code of
:
(that is if I remember that :
should be url encoded, which I tend to forget as there is only one in urls, just after the http
). So, when I write a test, I like to keep my arguments simple (e.g. N:12345
), not crippled with percent signs (e.g. N%3A12345
). Which is not that simple when your test framework is purely declarative. Now, that's where the dynamic aspects of a language like perl comes handy: you can use a perl expression in you data (instead of a regular constant). To be correct, this is actually possible because it is supported by both the language and the testing framework. Here is what it looks like:=== TEST 4: POST The main case (when everything submitted is GOOD and the DB should be updated). --- config location /rrd/taratata { rrd /var/rrd/taratata.rrd; } --- more_headers Content-type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST /rrd/taratata value=".uri_escape("N:12345") --- response_body_like: go round.*Robin --- error_code: 200The magic lies in
request eval
which basically says that the content of request
should be applied (or filtered by as the FILTERS section of Test::Base would say) the eval
function before being handed over to the tests runner. This is actually a handy feature that I had to dig out of Test::Base. You can apply any kind of perl function on this. Gives you the power of perl without breaking the "declarative" paradigm.One test, multiple requests. As I mentioned before, one of my python tests was specifically built to show that once a RRD request was in error, all the following requests would be too. It went like:
- POST with a correct value: response is OK
- POST with a bad value: server barks out with a message
- POST with the same value as in step 1: response is OK
pipelined_requests
. Here is the relevant snippet:--- more_headers Content-type: application/x-www-form-urlencoded --- pipelined_requests eval use URI::Escape; ["POST /rrd/taratata value=".uri_escape("N:12345"), "POST /rrd/taratata value=".uri_escape("N:whatever"), "POST /rrd/taratata value=".uri_escape("N:12345")] --- response_body_like: Robin.*Problem.*Robin --- error_code: 200This is not strictly equivalent to what I was doing for a couple of reasons:
- The requests are all performed on the same TCP connection (from what I can see,
pipelined_requests
was intended to test HTTP/1.1 cases). - Checking the results is not as easy as it was in python (or as one could hope), so you end up testing only the last
error_code
and using a patternRobin.*Problem.*Robin
to make sure the responses were OK/KO/OK. - The client sends all the requests in one gulp whereas with python I was waiting for request one to complete before sending request 2 etc. This is not a problem in our case because there is only one nginx process handling all the requests and they are handled sequentially. But this is not as natural as what I used to do with python.
Controling the buffers. I talked about the buffer problems you have to face/understand when dealing with POST requests and I even made a post about this: Post body buffers. There is a way to finely control what is sent by the test and when. But it takes you to manually craft the request yourself. Something most people don't like to do because it requires a good understanding of the HTTP protocol. On the other side, if you are trying to test nginx buffering special cases, there is a good chance HTTP doesn't scare you:
=== TEST 10: POST show buffers Request specially crafted to produce 2 buffers in the body received handler (one with the beginning of the entity read with the headers and one with the rest of the body). --- config location /rrd/taratata { rrd /var/rrd/taratata.rrd; } --- raw_request eval use URI::Escape; my $val="value=".uri_escape("N:12345:678").("678"x300); ["POST /rrd/taratata HTTP/1.1\r Host: localhost\r Connection: Close\r Content-Type: application/x-www-form-urlencoded\r Content-Length:".length($val)."\r\n\r\n", substr($val, 0, 6), substr($val, 6, 15), substr($val, 21)] --- raw_request_middle_delay 1 --- response_body_like: Problem .*updating --- error_code: 500The two things worth noting here are the
raw_request
and raw_request_middle_delay
. The first one provides the "chunks" that constitute the request where as the second one indicates how much the test should wait between two chunks.Now, I managed to move all my testing to Test::Nginx. And there are still things I think should be improved. But one of the things I did not like in my previous post (namely the inability to run only one test) is solved by Test::Base (using
--- ONLY
to have it run only the specified test).I'll try to chat with agentzh to see if there is any chance we could improve things.
No comments:
Post a Comment