OLD | NEW |
(Empty) | |
| 1 /** |
| 2 * Each test run gets its own URL and randomized content and operates independen
tly. |
| 3 * |
| 4 * Tests are an array of objects, each representing a request to make and check. |
| 5 * The cache.py server script stashes an entry containing observed headers for |
| 6 * each request it receives. When the test fetches have run, this state is retr
ieved |
| 7 * and the expected_* lists are checked, including their length. |
| 8 * |
| 9 * Request object keys: |
| 10 * - template - A template object for the request, by name -- see "templates" be
low. |
| 11 * - request_method - A string containing the HTTP method to be used. |
| 12 * - request_headers - An array of [header_name_string, header_value_string] arr
ays to |
| 13 * emit in the request. |
| 14 * - request_body - A string to use as the request body. |
| 15 * - mode - The mode string to pass to fetch(). |
| 16 * - credentials - The credentials string to pass to fetch(). |
| 17 * - cache - The cache string to pass to fetch(). |
| 18 * - pause_after - Boolean controlling a 3-second pause after the request comple
tes. |
| 19 * - response_status - A [number, string] array containing the HTTP status code |
| 20 * and phrase to return. |
| 21 * - response_headers - An array of [header_name_string, header_value_string] ar
rays to |
| 22 * emit in the response. These values will also be checked
like |
| 23 * expected_response_headers, unless there is a third value
that is |
| 24 * false. |
| 25 * - response_body - String to send as the response body. If not set, it will co
ntain |
| 26 * the test identifier. |
| 27 * - expected_type - One of ["cached", "not_cached", "lm_validate", "etag_valida
te", "error"] |
| 28 * - expected_status - A number representing a HTTP status code to check the res
ponse for. |
| 29 * If not set, the value of response_status[0] will be used;
if that |
| 30 * is not set, 200 will be used. |
| 31 * - expected_request_headers - An array of [header_name_string, header_value_st
ring] representing |
| 32 * headers to check the request for. |
| 33 * - expected_response_headers - An array of [header_name_string, header_value_s
tring] representing |
| 34 * headers to check the response for. See also res
ponse_headers. |
| 35 * - expected_response_text - A string to check the response body against. |
| 36 */ |
| 37 |
| 38 function make_url(uuid, requests, idx) { |
| 39 var arg = ""; |
| 40 if ("query_arg" in requests[idx]) { |
| 41 arg = "&target=" + requests[idx].query_arg; |
| 42 } |
| 43 return "resources/http-cache.py?token=" + uuid + "&info=" + btoa(JSON.stringif
y(requests)) + arg; |
| 44 } |
| 45 |
| 46 function server_state(uuid) { |
| 47 return fetch("resources/http-cache.py?querystate&token=" + uuid) |
| 48 .then(function(response) { |
| 49 return response.text(); |
| 50 }).then(function(text) { |
| 51 // null will be returned if the server never received any requests |
| 52 // for the given uuid. Normalize that to an empty list consistent |
| 53 // with our representation. |
| 54 return JSON.parse(text) || []; |
| 55 }); |
| 56 } |
| 57 |
| 58 |
| 59 templates = { |
| 60 "fresh": { |
| 61 "response_headers": [ |
| 62 ['Expires', http_date(100000)], |
| 63 ['Last-Modified', http_date(0)] |
| 64 ] |
| 65 }, |
| 66 "stale": { |
| 67 "response_headers": [ |
| 68 ['Expires', http_date(-5000)], |
| 69 ['Last-Modified', http_date(-100000)] |
| 70 ] |
| 71 }, |
| 72 "lcl_response": { |
| 73 "response_headers": [ |
| 74 ['Location', "location_target"], |
| 75 ['Content-Location', "content_location_target"] |
| 76 ] |
| 77 }, |
| 78 "location": { |
| 79 "query_arg": "location_target", |
| 80 "response_headers": [ |
| 81 ['Expires', http_date(100000)], |
| 82 ['Last-Modified', http_date(0)] |
| 83 ] |
| 84 }, |
| 85 "content_location": { |
| 86 "query_arg": "content_location_target", |
| 87 "response_headers": [ |
| 88 ['Expires', http_date(100000)], |
| 89 ['Last-Modified', http_date(0)] |
| 90 ] |
| 91 } |
| 92 } |
| 93 |
| 94 function make_test(raw_requests) { |
| 95 var requests = []; |
| 96 for (var i = 0; i < raw_requests.length; i++) { |
| 97 var request = raw_requests[i]; |
| 98 if ("template" in request) { |
| 99 var template = templates[request["template"]]; |
| 100 for (var member in template) { |
| 101 if (! request.hasOwnProperty(member)) { |
| 102 request[member] = template[member]; |
| 103 } |
| 104 } |
| 105 } |
| 106 if ("expected_type" in request && request.expected_type === "cached") { |
| 107 // requests after one that's expected to be cached will get out of sync |
| 108 // with the server; not currently supported. |
| 109 if (raw_requests.length > i + 1) { |
| 110 assert_unreached("Making requests after something is expected to be cach
ed."); |
| 111 } |
| 112 } |
| 113 requests.push(request); |
| 114 } |
| 115 return function(test) { |
| 116 var uuid = token(); |
| 117 var fetch_functions = []; |
| 118 for (var i = 0; i < requests.length; ++i) { |
| 119 fetch_functions.push({ |
| 120 code: function(idx) { |
| 121 var init = {}; |
| 122 var url = make_url(uuid, requests, idx); |
| 123 var config = requests[idx]; |
| 124 if ("request_method" in config) { |
| 125 init.method = config["request_method"]; |
| 126 } |
| 127 if ("request_headers" in config) { |
| 128 init.headers = config["request_headers"]; |
| 129 } |
| 130 if ("request_body" in config) { |
| 131 init.body = config["request_body"]; |
| 132 } |
| 133 if ("mode" in config) { |
| 134 init.mode = config["mode"]; |
| 135 } |
| 136 if ("credentials" in config) { |
| 137 init.mode = config["credentials"]; |
| 138 } |
| 139 if ("cache" in config) { |
| 140 init.cache = config["cache"]; |
| 141 } |
| 142 return fetch(url, init) |
| 143 .then(function(response) { |
| 144 var res_num = parseInt(response.headers.get("Server-Request-Count"
)); |
| 145 var req_num = idx + 1; |
| 146 if ("expected_type" in config) { |
| 147 if (config.expected_type === "error") { |
| 148 assert_true(false, "Request " + req_num + " should have been a
n error"); |
| 149 return [response.text(), response_status]; |
| 150 } |
| 151 if (config.expected_type === "cached") { |
| 152 assert_less_than(res_num, req_num, "Response used"); |
| 153 } |
| 154 if (config.expected_type === "not_cached") { |
| 155 assert_equals(res_num, req_num, "Response used"); |
| 156 } |
| 157 } |
| 158 if ("expected_status" in config) { |
| 159 assert_equals(response.status, config.expected_status, "Response
status"); |
| 160 } else if ("response_status" in config) { |
| 161 assert_equals(response.status, config.response_status[0], "Res
ponse status"); |
| 162 } else { |
| 163 assert_equals(response.status, 200, "Response status") |
| 164 } |
| 165 if ("response_headers" in config) { |
| 166 config.response_headers.forEach(function(header) { |
| 167 if (header.len < 3 || header[2] === true) { |
| 168 assert_equals(response.headers.get(header[0]), header[1], "R
esponse header") |
| 169 } |
| 170 }) |
| 171 } |
| 172 if ("expected_response_headers" in config) { |
| 173 config.expected_response_headers.forEach(function(header) { |
| 174 assert_equals(response.headers.get(header[0]), header[1], "Res
ponse header"); |
| 175 }); |
| 176 } |
| 177 return response.text(); |
| 178 }).then(function(res_body) { |
| 179 if ("expected_response_text" in config) { |
| 180 assert_equals(res_body, config.expected_response_text, "Response
body"); |
| 181 } else if ("response_body" in config) { |
| 182 assert_equals(res_body, config.response_body, "Response body"); |
| 183 } else { |
| 184 assert_equals(res_body, uuid, "Response body"); |
| 185 } |
| 186 }, function(reason) { |
| 187 if ("expected_type" in config && config.expected_type === "error")
{ |
| 188 assert_throws(new TypeError(), function() { throw reason; }); |
| 189 } else { |
| 190 throw reason; |
| 191 } |
| 192 }); |
| 193 }, |
| 194 pause_after: "pause_after" in requests[i] && true || false |
| 195 }); |
| 196 } |
| 197 |
| 198 function pause() { |
| 199 return new Promise(function(resolve, reject) { |
| 200 step_timeout(function() { |
| 201 return resolve() |
| 202 }, 3000); |
| 203 }); |
| 204 } |
| 205 |
| 206 // TODO: it would be nice if this weren't serialised. |
| 207 var idx = 0; |
| 208 function run_next_step() { |
| 209 if (fetch_functions.length) { |
| 210 var fetch_function = fetch_functions.shift(); |
| 211 if (fetch_function.pause_after > 0) { |
| 212 return fetch_function.code(idx++) |
| 213 .then(pause) |
| 214 .then(run_next_step); |
| 215 } else { |
| 216 return fetch_function.code(idx++) |
| 217 .then(run_next_step); |
| 218 } |
| 219 } else { |
| 220 return Promise.resolve(); |
| 221 } |
| 222 } |
| 223 |
| 224 return run_next_step() |
| 225 .then(function() { |
| 226 // Now, query the server state |
| 227 return server_state(uuid); |
| 228 }).then(function(state) { |
| 229 for (var i = 0; i < requests.length; ++i) { |
| 230 var expected_validating_headers = [] |
| 231 var req_num = i + 1; |
| 232 if ("expected_type" in requests[i]) { |
| 233 if (requests[i].expected_type === "cached") { |
| 234 assert_true(state.length <= i, "cached response used for request "
+ req_num); |
| 235 continue; // the server will not see the request, so we can't chec
k anything else. |
| 236 } |
| 237 if (requests[i].expected_type === "not_cached") { |
| 238 assert_false(state.length <= i, "cached response used for request
" + req_num); |
| 239 } |
| 240 if (requests[i].expected_type === "etag_validated") { |
| 241 expected_validating_headers.push('if-none-match') |
| 242 } |
| 243 if (requests[i].expected_type === "lm_validated") { |
| 244 expected_validating_headers.push('if-modified-since') |
| 245 } |
| 246 } |
| 247 for (var j in expected_validating_headers) { |
| 248 var vhdr = expected_validating_headers[j]; |
| 249 assert_own_property(state[i].request_headers, vhdr, " has " + vhdr +
" request header"); |
| 250 } |
| 251 if ("expected_request_headers" in requests[i]) { |
| 252 var expected_request_headers = requests[i].expected_request_headers; |
| 253 for (var j = 0; j < expected_request_headers.length; ++j) { |
| 254 var expected_header = expected_request_headers[j]; |
| 255 assert_equals(state[i].request_headers[expected_header[0].toLowerC
ase()], |
| 256 expected_header[1]); |
| 257 } |
| 258 } |
| 259 } |
| 260 }); |
| 261 }; |
| 262 } |
| 263 |
| 264 |
| 265 function run_tests(tests) |
| 266 { |
| 267 tests.forEach(function(info) { |
| 268 promise_test(make_test(info.requests), info.name); |
| 269 }); |
| 270 } |
| 271 |
| 272 function http_date(delta) { |
| 273 return new Date(Date.now() + (delta * 1000)).toGMTString(); |
| 274 } |
| 275 |
| 276 var content_store = {}; |
| 277 function http_content(cs_key) { |
| 278 if (cs_key in content_store) { |
| 279 return content_store[cs_key]; |
| 280 } else { |
| 281 var content = btoa(Math.random() * Date.now()); |
| 282 content_store[cs_key] = content; |
| 283 return content; |
| 284 } |
| 285 } |
OLD | NEW |