OLD | NEW |
(Empty) | |
| 1 /** |
| 2 * Each test is run twice: once using etag/If-None-Match and once with |
| 3 * date/If-Modified-Since. Each test run gets its own URL and randomized |
| 4 * content and operates independently. |
| 5 * |
| 6 * The test steps are run with request_cache.length fetch requests issued |
| 7 * and their immediate results sanity-checked. The cache.py server script |
| 8 * stashes an entry containing any If-None-Match, If-Modified-Since, Pragma, |
| 9 * and Cache-Control observed headers for each request it receives. When |
| 10 * the test fetches have run, this state is retrieved from cache.py and the |
| 11 * expected_* lists are checked, including their length. |
| 12 * |
| 13 * This means that if a request_* fetch is expected to hit the cache and not |
| 14 * touch the network, then there will be no entry for it in the expect_* |
| 15 * lists. AKA (request_cache.length - expected_validation_headers.length) |
| 16 * should equal the number of cache hits that didn't touch the network. |
| 17 * |
| 18 * Test dictionary keys: |
| 19 * - state: required string that determines whether the Expires response for |
| 20 * the fetched document should be set in the future ("fresh") or past |
| 21 * ("stale"). |
| 22 * - vary: optional string to be passed to the server for it to quote back |
| 23 * in a Vary header on the response to us. |
| 24 * - cache_control: optional string to be passed to the server for it to |
| 25 * quote back in a Cache-Control header on the response to us. |
| 26 * - redirect: optional string "same-origin" or "cross-origin". If |
| 27 * provided, the server will issue an absolute redirect to the script on |
| 28 * the same or a different origin, as appropriate. The redirected |
| 29 * location is the script with the redirect parameter removed, so the |
| 30 * content/state/etc. will be as if you hadn't specified a redirect. |
| 31 * - request_cache: required array of cache modes to use (via `cache`). |
| 32 * - request_headers: optional array of explicit fetch `headers` arguments. |
| 33 * If provided, the server will log an empty dictionary for each request |
| 34 * instead of the request headers it would normally log. |
| 35 * - response: optional array of specialized response handling. Right now, |
| 36 * "error" array entries indicate a network error response is expected |
| 37 * which will reject with a TypeError. |
| 38 * - expected_validation_headers: required boolean array indicating whether |
| 39 * the server should have seen an If-None-Match/If-Modified-Since header |
| 40 * in the request. |
| 41 * - expected_no_cache_headers: required boolean array indicating whether |
| 42 * the server should have seen Pragma/Cache-control:no-cache headers in |
| 43 * the request. |
| 44 * - expected_max_age_headers: optional boolean array indicating whether |
| 45 * the server should have seen a Cache-Control:max-age=0 header in the |
| 46 * request. |
| 47 */ |
| 48 |
| 49 var now = new Date(); |
| 50 |
| 51 function base_path() { |
| 52 return location.pathname.replace(/\/[^\/]*$/, '/'); |
| 53 } |
| 54 function make_url(uuid, id, value, content, info) { |
| 55 var dates = { |
| 56 fresh: new Date(now.getFullYear() + 1, now.getMonth(), now.getDay()).toGMTSt
ring(), |
| 57 stale: new Date(now.getFullYear() - 1, now.getMonth(), now.getDay()).toGMTSt
ring(), |
| 58 }; |
| 59 var vary = ""; |
| 60 if ("vary" in info) { |
| 61 vary = "&vary=" + info.vary; |
| 62 } |
| 63 var cache_control = ""; |
| 64 if ("cache_control" in info) { |
| 65 cache_control = "&cache_control=" + info.cache_control; |
| 66 } |
| 67 var redirect = ""; |
| 68 |
| 69 var ignore_request_headers = ""; |
| 70 if ("request_headers" in info) { |
| 71 // Ignore the request headers that we send since they may be synthesized by
the test. |
| 72 ignore_request_headers = "&ignore"; |
| 73 } |
| 74 var url_sans_redirect = "resources/cache.py?token=" + uuid + |
| 75 "&content=" + content + |
| 76 "&" + id + "=" + value + |
| 77 "&expires=" + dates[info.state] + |
| 78 vary + cache_control + ignore_request_headers; |
| 79 // If there's a redirect, the target is the script without any redirect at |
| 80 // either the same domain or a different domain. |
| 81 if ("redirect" in info) { |
| 82 var host_info = get_host_info(); |
| 83 var origin; |
| 84 switch (info.redirect) { |
| 85 case "same-origin": |
| 86 origin = host_info['HTTP_ORIGIN']; |
| 87 break; |
| 88 case "cross-origin": |
| 89 origin = host_info['HTTP_REMOTE_ORIGIN']; |
| 90 break; |
| 91 } |
| 92 var redirected_url = origin + base_path() + url_sans_redirect; |
| 93 return url_sans_redirect + "&redirect=" + encodeURIComponent(redirected_url)
; |
| 94 } else { |
| 95 return url_sans_redirect; |
| 96 } |
| 97 } |
| 98 function expected_status(type, identifier, init) { |
| 99 if (type == "date" && |
| 100 init.headers && |
| 101 init.headers["If-Modified-Since"] == identifier) { |
| 102 // The server will respond with a 304 in this case. |
| 103 return [304, "Not Modified"]; |
| 104 } |
| 105 return [200, "OK"]; |
| 106 } |
| 107 function expected_response_text(type, identifier, init, content) { |
| 108 if (type == "date" && |
| 109 init.headers && |
| 110 init.headers["If-Modified-Since"] == identifier) { |
| 111 // The server will respond with a 304 in this case. |
| 112 return ""; |
| 113 } |
| 114 return content; |
| 115 } |
| 116 function server_state(uuid) { |
| 117 return fetch("resources/cache.py?querystate&token=" + uuid) |
| 118 .then(function(response) { |
| 119 return response.text(); |
| 120 }).then(function(text) { |
| 121 // null will be returned if the server never received any requests |
| 122 // for the given uuid. Normalize that to an empty list consistent |
| 123 // with our representation. |
| 124 return JSON.parse(text) || []; |
| 125 }); |
| 126 } |
| 127 function make_test(type, info) { |
| 128 return function(test) { |
| 129 var uuid = token(); |
| 130 var identifier = (type == "tag" ? Math.random() : now.toGMTString()); |
| 131 var content = Math.random().toString(); |
| 132 var url = make_url(uuid, type, identifier, content, info); |
| 133 var fetch_functions = []; |
| 134 for (var i = 0; i < info.request_cache.length; ++i) { |
| 135 fetch_functions.push(function(idx) { |
| 136 var init = {cache: info.request_cache[idx]}; |
| 137 if ("request_headers" in info) { |
| 138 init.headers = info.request_headers[idx]; |
| 139 } |
| 140 if (init.cache === "only-if-cached") { |
| 141 // only-if-cached requires we use same-origin mode. |
| 142 init.mode = "same-origin"; |
| 143 } |
| 144 return fetch(url, init) |
| 145 .then(function(response) { |
| 146 if ("response" in info && info.response[idx] === "error") { |
| 147 assert_true(false, "fetch should have been an error"); |
| 148 return; |
| 149 } |
| 150 assert_array_equals([response.status, response.statusText], |
| 151 expected_status(type, identifier, init)); |
| 152 return response.text(); |
| 153 }).then(function(text) { |
| 154 assert_equals(text, expected_response_text(type, identifier, init, c
ontent)); |
| 155 }, function(reason) { |
| 156 if ("response" in info && info.response[idx] === "error") { |
| 157 assert_throws(new TypeError(), function() { throw reason; }); |
| 158 } else { |
| 159 throw reason; |
| 160 } |
| 161 }); |
| 162 }); |
| 163 } |
| 164 var i = 0; |
| 165 function run_next_step() { |
| 166 if (fetch_functions.length) { |
| 167 return fetch_functions.shift()(i++) |
| 168 .then(run_next_step); |
| 169 } else { |
| 170 return Promise.resolve(); |
| 171 } |
| 172 } |
| 173 return run_next_step() |
| 174 .then(function() { |
| 175 // Now, query the server state |
| 176 return server_state(uuid); |
| 177 }).then(function(state) { |
| 178 var expectedState = []; |
| 179 info.expected_validation_headers.forEach(function (validate) { |
| 180 if (validate) { |
| 181 if (type == "tag") { |
| 182 expectedState.push({"If-None-Match": '"' + identifier + '"'}); |
| 183 } else { |
| 184 expectedState.push({"If-Modified-Since": identifier}); |
| 185 } |
| 186 } else { |
| 187 expectedState.push({}); |
| 188 } |
| 189 }); |
| 190 for (var i = 0; i < info.expected_no_cache_headers.length; ++i) { |
| 191 if (info.expected_no_cache_headers[i]) { |
| 192 expectedState[i]["Pragma"] = "no-cache"; |
| 193 expectedState[i]["Cache-Control"] = "no-cache"; |
| 194 } |
| 195 } |
| 196 if ("expected_max_age_headers" in info) { |
| 197 for (var i = 0; i < info.expected_max_age_headers.length; ++i) { |
| 198 if (info.expected_max_age_headers[i]) { |
| 199 expectedState[i]["Cache-Control"] = "max-age=0"; |
| 200 } |
| 201 } |
| 202 } |
| 203 assert_equals(state.length, expectedState.length); |
| 204 for (var i = 0; i < state.length; ++i) { |
| 205 for (var header in state[i]) { |
| 206 assert_equals(state[i][header], expectedState[i][header]); |
| 207 delete expectedState[i][header]; |
| 208 } |
| 209 for (var header in expectedState[i]) { |
| 210 assert_false(header in state[i]); |
| 211 } |
| 212 } |
| 213 }); |
| 214 }; |
| 215 } |
| 216 |
| 217 function run_tests(tests) |
| 218 { |
| 219 tests.forEach(function(info) { |
| 220 promise_test(make_test("tag", info), info.name + " with Etag and " + info.st
ate + " response"); |
| 221 promise_test(make_test("date", info), info.name + " with Last-Modified and "
+ info.state + " response"); |
| 222 }); |
| 223 } |
OLD | NEW |