testing/web-platform/tests/fetch/api/request/request-cache.html
author Ehsan Akhgari <ehsan@mozilla.com>
Thu, 10 Mar 2016 16:42:52 -0500
changeset 288349 53638cfd818d5f31615c41287796bdda4beda854
parent 288157 0437702044317bce21795031d42d5a8e2edff807
child 301169 d325ced29e5bacf0811f6d4beb69a4700c707577
permissions -rw-r--r--
Bug 1120715 - Part 8: Expect a 304 response in the cases where we would do a successful revalidation in the RequestCache test; r=bkelly

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Request cache</title>
    <meta name="help" href="https://fetch.spec.whatwg.org/#request">
    <meta name="timeout" content="long">
    <script src="/resources/testharness.js"></script>
    <script src="/resources/testharnessreport.js"></script>
    <script src="/common/utils.js"></script>
  </head>
  <body>
    <script>
    var now = new Date();
    var tests = [
      {
        name: 'RequestCache "default" mode checks the cache for previously cached content and goes to the network for stale responses',
        state: "stale",
        request_cache: ["default", "default"],
        expected_validation_headers: [false, true],
        expected_no_cache_headers: [false, false],
      },
      {
        name: 'RequestCache "default" mode checks the cache for previously cached content and avoids going to the network if a fresh response exists',
        state: "fresh",
        request_cache: ["default", "default"],
        expected_validation_headers: [false],
        expected_no_cache_headers: [false],
      },
      {
        name: 'RequestCache "no-cache" mode revalidates stale responses found in the cache',
        state: "stale",
        request_cache: ["default", "no-cache"],
        expected_validation_headers: [false, true],
        expected_no_cache_headers: [false, false],
        expected_max_age_headers: [false, true],
      },
      {
        name: 'RequestCache "no-cache" mode revalidates fresh responses found in the cache',
        state: "fresh",
        request_cache: ["default", "no-cache"],
        expected_validation_headers: [false, true],
        expected_no_cache_headers: [false, false],
        expected_max_age_headers: [false, true],
      },
      {
        name: 'RequestCache "force-cache" mode checks the cache for previously cached content and avoid revalidation for stale responses',
        state: "stale",
        request_cache: ["default", "force-cache"],
        expected_validation_headers: [false],
        expected_no_cache_headers: [false],
      },
      {
        name: 'RequestCache "force-cache" mode checks the cache for previously cached content and avoid revalidation for fresh responses',
        state: "fresh",
        request_cache: ["default", "force-cache"],
        expected_validation_headers: [false],
        expected_no_cache_headers: [false],
      },
      {
        name: 'RequestCache "force-cache" mode checks the cache for previously cached content and goes to the network if a cached response is not found',
        state: "stale",
        request_cache: ["force-cache"],
        expected_validation_headers: [false],
        expected_no_cache_headers: [false],
      },
      {
        name: 'RequestCache "force-cache" mode checks the cache for previously cached content and goes to the network if a cached response is not found',
        state: "fresh",
        request_cache: ["force-cache"],
        expected_validation_headers: [false],
        expected_no_cache_headers: [false],
      },
      {
        name: 'RequestCache "force-cache" mode checks the cache for previously cached content and goes to the network if a cached response would vary',
        state: "stale",
        vary: "*",
        request_cache: ["default", "force-cache"],
        expected_validation_headers: [false, true],
        expected_no_cache_headers: [false, false],
      },
      {
        name: 'RequestCache "force-cache" mode checks the cache for previously cached content and goes to the network if a cached response would vary',
        state: "fresh",
        vary: "*",
        request_cache: ["default", "force-cache"],
        expected_validation_headers: [false, true],
        expected_no_cache_headers: [false, false],
      },
      {
        name: 'RequestCache "force-cache" stores the response in the cache if it goes to the network',
        state: "stale",
        request_cache: ["force-cache", "default"],
        expected_validation_headers: [false, true],
        expected_no_cache_headers: [false, false],
      },
      {
        name: 'RequestCache "force-cache" stores the response in the cache if it goes to the network',
        state: "fresh",
        request_cache: ["force-cache", "default"],
        expected_validation_headers: [false],
        expected_no_cache_headers: [false],
      },
      {
        name: 'RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless',
        state: "stale",
        request_cache: ["default", "no-store"],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, true],
      },
      {
        name: 'RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless',
        state: "fresh",
        request_cache: ["default", "no-store"],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, true],
      },
      {
        name: 'RequestCache "no-store" mode does not store the response in the cache',
        state: "stale",
        request_cache: ["no-store", "default"],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [true, false],
      },
      {
        name: 'RequestCache "no-store" mode does not store the response in the cache',
        state: "fresh",
        request_cache: ["no-store", "default"],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [true, false],
      },
      {
        name: 'RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store"',
        state: "stale",
        request_cache: ["default", "default"],
        request_headers: [{}, {"If-Modified-Since": now.toGMTString()}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, true],
      },
      {
        name: 'RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store"',
        state: "fresh",
        request_cache: ["default", "default"],
        request_headers: [{}, {"If-Modified-Since": now.toGMTString()}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, true],
      },
      {
        name: 'RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store"',
        state: "stale",
        request_cache: ["default", "default"],
        request_headers: [{"If-Modified-Since": now.toGMTString()}, {}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [true, false],
      },
      {
        name: 'RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store"',
        state: "fresh",
        request_cache: ["default", "default"],
        request_headers: [{"If-Modified-Since": now.toGMTString()}, {}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [true, false],
      },
      {
        name: 'RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store"',
        state: "stale",
        request_cache: ["default", "default"],
        request_headers: [{}, {"If-None-Match": '"foo"'}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, true],
      },
      {
        name: 'RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store"',
        state: "fresh",
        request_cache: ["default", "default"],
        request_headers: [{}, {"If-None-Match": '"foo"'}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, true],
      },
      {
        name: 'RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store"',
        state: "stale",
        request_cache: ["default", "default"],
        request_headers: [{"If-None-Match": '"foo"'}, {}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [true, false],
      },
      {
        name: 'RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store"',
        state: "fresh",
        request_cache: ["default", "default"],
        request_headers: [{"If-None-Match": '"foo"'}, {}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [true, false],
      },
      {
        name: 'RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store"',
        state: "stale",
        request_cache: ["default", "default"],
        request_headers: [{}, {"If-Unmodified-Since": now.toGMTString()}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, true],
      },
      {
        name: 'RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store"',
        state: "fresh",
        request_cache: ["default", "default"],
        request_headers: [{}, {"If-Unmodified-Since": now.toGMTString()}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, true],
      },
      {
        name: 'RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store"',
        state: "stale",
        request_cache: ["default", "default"],
        request_headers: [{"If-Unmodified-Since": now.toGMTString()}, {}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [true, false],
      },
      {
        name: 'RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store"',
        state: "fresh",
        request_cache: ["default", "default"],
        request_headers: [{"If-Unmodified-Since": now.toGMTString()}, {}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [true, false],
      },
      {
        name: 'RequestCache "default" mode with an If-Match header is treated similarly to "no-store"',
        state: "stale",
        request_cache: ["default", "default"],
        request_headers: [{}, {"If-Match": '"foo"'}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, true],
      },
      {
        name: 'RequestCache "default" mode with an If-Match header is treated similarly to "no-store"',
        state: "fresh",
        request_cache: ["default", "default"],
        request_headers: [{}, {"If-Match": '"foo"'}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, true],
      },
      {
        name: 'RequestCache "default" mode with an If-Match header is treated similarly to "no-store"',
        state: "stale",
        request_cache: ["default", "default"],
        request_headers: [{"If-Match": '"foo"'}, {}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [true, false],
      },
      {
        name: 'RequestCache "default" mode with an If-Match header is treated similarly to "no-store"',
        state: "fresh",
        request_cache: ["default", "default"],
        request_headers: [{"If-Match": '"foo"'}, {}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [true, false],
      },
      {
        name: 'RequestCache "default" mode with an If-Range header is treated similarly to "no-store"',
        state: "stale",
        request_cache: ["default", "default"],
        request_headers: [{}, {"If-Range": '"foo"'}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, true],
      },
      {
        name: 'RequestCache "default" mode with an If-Range header is treated similarly to "no-store"',
        state: "fresh",
        request_cache: ["default", "default"],
        request_headers: [{}, {"If-Range": '"foo"'}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, true],
      },
      {
        name: 'RequestCache "default" mode with an If-Range header is treated similarly to "no-store"',
        state: "stale",
        request_cache: ["default", "default"],
        request_headers: [{"If-Range": '"foo"'}, {}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [true, false],
      },
      {
        name: 'RequestCache "default" mode with an If-Range header is treated similarly to "no-store"',
        state: "fresh",
        request_cache: ["default", "default"],
        request_headers: [{"If-Range": '"foo"'}, {}],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [true, false],
      },
      {
        name: 'Responses with the "Cache-Control: no-store" header are not stored in the cache',
        state: "stale",
        cache_control: "no-store",
        request_cache: ["default", "default"],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, false],
      },
      {
        name: 'Responses with the "Cache-Control: no-store" header are not stored in the cache',
        state: "fresh",
        cache_control: "no-store",
        request_cache: ["default", "default"],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, false],
      },
      {
        name: 'RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless',
        state: "stale",
        request_cache: ["default", "reload"],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, true],
      },
      {
        name: 'RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless',
        state: "fresh",
        request_cache: ["default", "reload"],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, true],
      },
      {
        name: 'RequestCache "reload" mode does store the response in the cache',
        state: "stale",
        request_cache: ["reload", "default"],
        expected_validation_headers: [false, true],
        expected_no_cache_headers: [true, false],
      },
      {
        name: 'RequestCache "reload" mode does store the response in the cache',
        state: "fresh",
        request_cache: ["reload", "default"],
        expected_validation_headers: [false],
        expected_no_cache_headers: [true],
      },
      {
        name: 'RequestCache "reload" mode does store the response in the cache even if a previous response is already stored',
        state: "stale",
        request_cache: ["default", "reload", "default"],
        expected_validation_headers: [false, false, true],
        expected_no_cache_headers: [false, true, false],
      },
      {
        name: 'RequestCache "reload" mode does store the response in the cache even if a previous response is already stored',
        state: "fresh",
        request_cache: ["default", "reload", "default"],
        expected_validation_headers: [false, false],
        expected_no_cache_headers: [false, true],
      },
    ];
    function make_url(uuid, id, value, content, info) {
      var dates = {
        fresh: new Date(now.getFullYear() + 1, now.getMonth(), now.getDay()).toGMTString(),
        stale: new Date(now.getFullYear() - 1, now.getMonth(), now.getDay()).toGMTString(),
      };
      var vary = "";
      if ("vary" in info) {
        vary = "&vary=" + info.vary;
      }
      var cache_control = "";
      if ("cache_control" in info) {
        cache_control = "&cache_control=" + info.cache_control;
      }
      var ignore_request_headers = "";
      if ("request_headers" in info) {
        // Ignore the request headers that we send since they may be synthesized by the test.
        ignore_request_headers = "&ignore";
      }
      return "resources/cache.py?token=" + uuid +
             "&content=" + content +
             "&" + id + "=" + value +
             "&expires=" + dates[info.state] +
             vary + cache_control + ignore_request_headers;
    }
    function expected_status(type, identifier, init) {
      if (type == "date" &&
          init.headers &&
          init.headers["If-Modified-Since"] == identifier) {
        // The server will respond with a 304 in this case.
        return [304, "Not Modified"];
      }
      return [200, "OK"];
    }
    function expected_response_text(type, identifier, init, content) {
      if (type == "date" &&
          init.headers &&
          init.headers["If-Modified-Since"] == identifier) {
        // The server will respond with a 304 in this case.
        return "";
      }
      return content;
    }
    function server_state(uuid) {
      return fetch("resources/cache.py?querystate&token=" + uuid)
        .then(function(response) {
          return response.text();
        }).then(function(text) {
          return JSON.parse(text);
        });
    }
    function populate_cache(url, content, info, type, identifier) {
      var init = {cache: info.request_cache[0]};
      if ("request_headers" in info) {
        init.headers = info.request_headers[0];
      }
      return fetch(url, init)
        .then(function(response) {
          assert_array_equals([response.status, response.statusText],
                              expected_status(type, identifier, init));
          return response.text();
        }).then(function(text) {
          assert_equals(text, expected_response_text(type, identifier, init, content));
        });
    }
    function make_test(type, info) {
      return function(test) {
        var uuid = token();
        var identifier = (type == "tag" ? Math.random() : now.toGMTString());
        var content = Math.random().toString();
        var url = make_url(uuid, type, identifier, content, info);
        var fetch_functions = [function() {
          return populate_cache(url, content, info, type, identifier);
        }];
        for (var i = 1; i < info.request_cache.length; ++i) {
          fetch_functions.push(function(idx) {
            var init = {cache: info.request_cache[idx]};
            if ("request_headers" in info) {
              init.headers = info.request_headers[idx];
            }
            return fetch(url, init)
              .then(function(response) {
                assert_array_equals([response.status, response.statusText],
                                    expected_status(type, identifier, init));
                return response.text();
              }).then(function(text) {
                assert_equals(text, expected_response_text(type, identifier, init, content));
              });
          });
        }
        var i = 0;
        function run_next_step() {
          if (fetch_functions.length) {
            return fetch_functions.shift()(i++)
              .then(run_next_step);
          } else {
            return Promise.resolve();
          }
        }
        return run_next_step()
          .then(function() {
            // Now, query the server state
            return server_state(uuid);
          }).then(function(state) {
            var expectedState = [];
            info.expected_validation_headers.forEach(function (validate) {
              if (validate) {
                if (type == "tag") {
                  expectedState.push({"If-None-Match": '"' + identifier + '"'});
                } else {
                  expectedState.push({"If-Modified-Since": identifier});
                }
              } else {
                expectedState.push({});
              }
            });
            for (var i = 0; i < info.expected_no_cache_headers.length; ++i) {
              if (info.expected_no_cache_headers[i]) {
                expectedState[i]["Pragma"] = "no-cache";
                expectedState[i]["Cache-Control"] = "no-cache";
              }
            }
            if ("expected_max_age_headers" in info) {
              for (var i = 0; i < info.expected_max_age_headers.length; ++i) {
                if (info.expected_max_age_headers[i]) {
                  expectedState[i]["Cache-Control"] = "max-age=0";
                }
              }
            }
            assert_equals(state.length, expectedState.length);
            for (var i = 0; i < state.length; ++i) {
              for (var header in state[i]) {
                assert_equals(state[i][header], expectedState[i][header]);
                delete expectedState[i][header];
              }
              for (var header in expectedState[i]) {
                assert_false(header in state[i]);
              }
            }
          });
      };
    }
    tests.forEach(function(info) {
      promise_test(make_test("tag", info), info.name + " with Etag and " + info.state + " response");
      promise_test(make_test("date", info), info.name + " with date and " + info.state + " response");
    });
    </script>
  </body>
</html>