// META: global=window,dedicatedworker,sharedworker
// META: script=/common/subset-tests-by-key.js
// META: variant=?include=integrity-none
// META: variant=?include=integrity-pass
// META: variant=?include=integrity-fail
// META: timeout=long

// Given `{ digest: "...", body: "...", cors: true, type: "..." }`:
function resourceURL(data) {
  let params = new URLSearchParams(data);
  return "./resource.py?" + params.toString();
}

const hello_world = "hello world";
const hello_hashes = {
  "sha-256": "uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=",
  "sha-512": "MJ7MSJwS1utMxA9QyQLytNDtd+5RGnx6m808qG1M2G+YndNbxf9JlnDaNCVbRbDP2DDoH2Bdz33FVC6TrpzXbw=="
};

const dlrow_olleh = hello_world.split("").reverse().join("");
const dlrow_hashes = {
  "sha-256": "vT+a3uWsoxRxVJEINKfH4XZpLqsneOzhFVY98Y3iIz0=",
  "sha-512": "N/peuevAy3l8KpS0bB6VTS8vc0fdAvjBJKYjVo2xb6sB6LpDfY6YlrXkWeeXGrP07UXDXEu1K3+SaUqMNjEkxQ=="
};

const EXPECT_BLOCKED = "block";
const EXPECT_LOADED = "loaded";
function generate_test(data, expectation, desc) {
  subsetTestByKey("integrity-none", promise_test, test => {
    let fetcher = fetch(resourceURL(data));
    if (expectation == EXPECT_BLOCKED) {
      return promise_rejects_js(test, TypeError, fetcher);
    } else {
      return fetcher.then(async r => {
        assert_equals(r.status, 200, "Response status is 200.");
        assert_equals(await r.text(), data.body);
      });
    }
  }, "No integrity metadata + " + desc);

  subsetTestByKey("integrity-pass", promise_test, test => {
    // Force CORS for the integrity check below:
    const data_with_cors = structuredClone(data);
    data_with_cors.cors = true;

    // The integrity check should pass, and nothing about the
    // `Unencoded-Digest` check should be affected.
    let fetcher = fetch(resourceURL(data_with_cors), { integrity: `sha256-${hello_hashes['sha-256']}`, mode: "cors" });
    if (expectation == EXPECT_BLOCKED) {
      return promise_rejects_js(test, TypeError, fetcher);
    } else {
      return fetcher.then(async r => {
        assert_equals(r.status, 200, "Response status is 200.");
        assert_equals(await r.text(), data.body);
      });
    }
  }, "Good integrity metadata + " + desc);


  subsetTestByKey("integrity-fail", promise_test, test => {
    // Force CORS for the integrity check below:
    const data_with_cors = structuredClone(data);
    data_with_cors.cors = true;

    // The integrity check should fail, so the resource should be blocked,
    // even with matching `Unencoded-Digest`s.
    let fetcher = fetch(resourceURL(data_with_cors), { integrity: `sha256-${dlrow_hashes['sha-256']}`, mode: "cors" });
    return promise_rejects_js(test, TypeError, fetcher);
  }, "Bad integrity metadata blocks everything, even: " + desc);
}

// No header.
generate_test(
  { body: hello_world },
  EXPECT_LOADED,
  "No header: loads.");

let good_header_list = [];
let bad_header_list = [];
let mixed_header_list = [];
for (const key in hello_hashes) {
  let good_header = `${key}=:${hello_hashes[key]}:`;
  good_header_list.push(good_header);
  let bad_header = `${key}=:${dlrow_hashes[key]}:`;
  bad_header_list.push(bad_header);
  mixed_header_list.push(good_header, bad_header);

  // - Good single headers:
  generate_test({
      body: hello_world,
      digest: good_header
    },
    EXPECT_LOADED,
    `Good ${key} header: loads.`);

  // - Good multiple headers:
  generate_test({
      body: hello_world,
      digest: `${good_header},${good_header}`
    },
    EXPECT_LOADED,
    `Repeated ${key} header: loads.`);

  generate_test({
      body: hello_world,
      digest: good_header_list.join(",")
    },
    EXPECT_LOADED,
    `Multiple good headers (previous += ${key}): loads.`);

  // - Bad single headers:
  generate_test({
      body: hello_world,
      digest: bad_header
    },
    EXPECT_BLOCKED,
    `Bad ${key} header: blocked.`);

  // - Bad multiple headers:
  generate_test({
      body: hello_world,
      digest: `${bad_header},${bad_header}`
    },
    EXPECT_BLOCKED,
    `Repeated ${key} header: blocked.`);

  generate_test({
      body: hello_world,
      digest: bad_header_list.join(",")
    },
    EXPECT_BLOCKED,
    `Multiple bad headers (previous += ${key}): blocked.`);
}

// - Mixed headers.
generate_test({
    body: hello_world,
    digest: mixed_header_list.join(","),
  },
  EXPECT_BLOCKED,
  `Mixed good and bad headers: blocked.`);
