// META: script=/common/subset-tests-by-key.js
// META: script=/common/utils.js
// META: script=resources/support.sub.js
// META: script=/fenced-frame/resources/utils.js
// META: variant=?include=baseline
// META: variant=?include=from-local
// META: variant=?include=from-private
// META: variant=?include=from-public
// META: timeout=long
//
// Spec: https://wicg.github.io/private-network-access/#integration-fetch
//
// These tests verify that secure contexts can fetch subresources in fenced
// frames from all address spaces, provided that the target server, if more
// private than the initiator, respond affirmatively to preflight requests.
//

setup(() => {
  // Making sure we are in a secure context, as expected.
  assert_true(window.isSecureContext);
});

// Source: secure local context.
//
// All fetches unaffected by Private Network Access.

subsetTestByKey(
    'from-local', promise_test, t => fencedFrameFetchTest(t, {
                                  source: {server: Server.HTTPS_LOCAL},
                                  target: {server: Server.HTTPS_LOCAL},
                                  fetchOptions: {method: 'GET', mode: 'cors'},
                                  expected: FetchTestResult.SUCCESS,
                                }),
    'local to local: no preflight required.');

subsetTestByKey(
    'from-local', promise_test,
    t => fencedFrameFetchTest(t, {
      source: {server: Server.HTTPS_LOCAL},
      target: {
        server: Server.HTTPS_PRIVATE,
        behavior: {response: ResponseBehavior.allowCrossOrigin()},
      },
      fetchOptions: {method: 'GET', mode: 'cors'},
      expected: FetchTestResult.SUCCESS,
    }),
    'local to private: no preflight required.');


subsetTestByKey(
    'from-local', promise_test,
    t => fencedFrameFetchTest(t, {
      source: {server: Server.HTTPS_LOCAL},
      target: {
        server: Server.HTTPS_PUBLIC,
        behavior: {response: ResponseBehavior.allowCrossOrigin()},
      },
      fetchOptions: {method: 'GET', mode: 'cors'},
      expected: FetchTestResult.SUCCESS,
    }),
    'local to public: no preflight required.');

// Strictly speaking, the following two tests do not exercise PNA-specific
// logic, but they serve as a baseline for comparison, ensuring that non-PNA
// preflight requests are sent and handled as expected.

subsetTestByKey(
    'baseline', promise_test,
    t => fencedFrameFetchTest(t, {
      source: {server: Server.HTTPS_LOCAL},
      target: {
        server: Server.HTTPS_PUBLIC,
        behavior: {
          preflight: PreflightBehavior.failure(),
          response: ResponseBehavior.allowCrossOrigin(),
        },
      },
      fetchOptions: {method: 'PUT', mode: 'cors'},
      expected: FetchTestResult.FAILURE,
    }),
    'local to public: PUT preflight failure.');

subsetTestByKey(
    'baseline', promise_test,
    t => fencedFrameFetchTest(t, {
      source: {server: Server.HTTPS_LOCAL},
      target: {
        server: Server.HTTPS_PUBLIC,
        behavior: {
          preflight: PreflightBehavior.success(token()),
          response: ResponseBehavior.allowCrossOrigin(),
        }
      },
      fetchOptions: {method: 'PUT', mode: 'cors'},
      expected: FetchTestResult.SUCCESS,
    }),
    'local to public: PUT preflight success.');

// Generates tests of preflight behavior for a single (source, target) pair.
//
// Scenarios:
//
//  - cors mode:
//    - preflight response has non-2xx HTTP code
//    - preflight response is missing CORS headers
//    - preflight response is missing the PNA-specific `Access-Control` header
//    - final response is missing CORS headers
//    - success
//    - success with PUT method (non-"simple" request)
//  - no-cors mode:
//    - preflight response has non-2xx HTTP code
//    - preflight response is missing CORS headers
//    - preflight response is missing the PNA-specific `Access-Control` header
//    - success
//
function makePreflightTests({
  subsetKey,
  source,
  sourceDescription,
  targetServer,
  targetDescription,
}) {
  const prefix = `${sourceDescription} to ${targetDescription}: `;

  subsetTestByKey(
      subsetKey, promise_test,
      t => fencedFrameFetchTest(t, {
        source,
        target: {
          server: targetServer,
          behavior: {
            preflight: PreflightBehavior.failure(),
            response: ResponseBehavior.allowCrossOrigin(),
          },
        },
        fetchOptions: {method: 'GET', mode: 'cors'},
        expected: FetchTestResult.FAILURE,
      }),
      prefix + 'failed preflight.');

  subsetTestByKey(
      subsetKey, promise_test,
      t => fencedFrameFetchTest(t, {
        source,
        target: {
          server: targetServer,
          behavior: {
            preflight: PreflightBehavior.noCorsHeader(token()),
            response: ResponseBehavior.allowCrossOrigin(),
          },
        },
        fetchOptions: {method: 'GET', mode: 'cors'},
        expected: FetchTestResult.FAILURE,
      }),
      prefix + 'missing CORS headers on preflight response.');

  subsetTestByKey(
      subsetKey, promise_test,
      t => fencedFrameFetchTest(t, {
        source,
        target: {
          server: targetServer,
          behavior: {
            preflight: PreflightBehavior.noPnaHeader(token()),
            response: ResponseBehavior.allowCrossOrigin(),
          },
        },
        fetchOptions: {method: 'GET', mode: 'cors'},
        expected: FetchTestResult.FAILURE,
      }),
      prefix + 'missing PNA header on preflight response.');

  subsetTestByKey(
      subsetKey, promise_test,
      t => fencedFrameFetchTest(t, {
        source,
        target: {
          server: targetServer,
          behavior: {preflight: PreflightBehavior.success(token())},
        },
        fetchOptions: {method: 'GET', mode: 'cors'},
        expected: FetchTestResult.FAILURE,
      }),
      prefix + 'missing CORS headers on final response.');

  subsetTestByKey(
      subsetKey, promise_test,
      t => fencedFrameFetchTest(t, {
        source,
        target: {
          server: targetServer,
          behavior: {
            preflight: PreflightBehavior.success(token()),
            response: ResponseBehavior.allowCrossOrigin(),
          },
        },
        fetchOptions: {method: 'GET', mode: 'cors'},
        expected: FetchTestResult.SUCCESS,
      }),
      prefix + 'success.');

  subsetTestByKey(
      subsetKey, promise_test,
      t => fencedFrameFetchTest(t, {
        source,
        target: {
          server: targetServer,
          behavior: {
            preflight: PreflightBehavior.success(token()),
            response: ResponseBehavior.allowCrossOrigin(),
          },
        },
        fetchOptions: {method: 'PUT', mode: 'cors'},
        expected: FetchTestResult.SUCCESS,
      }),
      prefix + 'PUT success.');

  subsetTestByKey(
      subsetKey, promise_test, t => fencedFrameFetchTest(t, {
                                 source,
                                 target: {server: targetServer},
                                 fetchOptions: {method: 'GET', mode: 'no-cors'},
                                 expected: FetchTestResult.FAILURE,
                               }),
      prefix + 'no-CORS mode failed preflight.');

  subsetTestByKey(
      subsetKey, promise_test,
      t => fencedFrameFetchTest(t, {
        source,
        target: {
          server: targetServer,
          behavior: {preflight: PreflightBehavior.noCorsHeader(token())},
        },
        fetchOptions: {method: 'GET', mode: 'no-cors'},
        expected: FetchTestResult.FAILURE,
      }),
      prefix + 'no-CORS mode missing CORS headers on preflight response.');

  subsetTestByKey(
      subsetKey, promise_test,
      t => fencedFrameFetchTest(t, {
        source,
        target: {
          server: targetServer,
          behavior: {preflight: PreflightBehavior.noPnaHeader(token())},
        },
        fetchOptions: {method: 'GET', mode: 'no-cors'},
        expected: FetchTestResult.FAILURE,
      }),
      prefix + 'no-CORS mode missing PNA header on preflight response.');

  subsetTestByKey(
      subsetKey, promise_test,
      t => fencedFrameFetchTest(t, {
        source,
        target: {
          server: targetServer,
          behavior: {preflight: PreflightBehavior.success(token())},
        },
        fetchOptions: {method: 'GET', mode: 'no-cors'},
        expected: FetchTestResult.OPAQUE,
      }),
      prefix + 'no-CORS mode success.');
}

// Source: private secure context.
//
// Fetches to the local address space require a successful preflight response
// carrying a PNA-specific header.

makePreflightTests({
  subsetKey: 'from-private',
  source: {server: Server.HTTPS_PRIVATE},
  sourceDescription: 'private',
  targetServer: Server.HTTPS_LOCAL,
  targetDescription: 'local',
});

subsetTestByKey(
    'from-private', promise_test, t => fencedFrameFetchTest(t, {
                                    source: {server: Server.HTTPS_PRIVATE},
                                    target: {server: Server.HTTPS_PRIVATE},
                                    fetchOptions: {method: 'GET', mode: 'cors'},
                                    expected: FetchTestResult.SUCCESS,
                                  }),
    'private to private: no preflight required.');

subsetTestByKey(
    'from-private', promise_test,
    t => fencedFrameFetchTest(t, {
      source: {server: Server.HTTPS_PRIVATE},
      target: {
        server: Server.HTTPS_PRIVATE,
        behavior: {response: ResponseBehavior.allowCrossOrigin()},
      },
      fetchOptions: {method: 'GET', mode: 'cors'},
      expected: FetchTestResult.SUCCESS,
    }),
    'private to public: no preflight required.');

// Source: public secure context.
//
// Fetches to the local and private address spaces require a successful
// preflight response carrying a PNA-specific header.

makePreflightTests({
  subsetKey: 'from-public',
  source: {server: Server.HTTPS_PUBLIC},
  sourceDescription: 'public',
  targetServer: Server.HTTPS_LOCAL,
  targetDescription: 'local',
});

makePreflightTests({
  subsetKey: 'from-public',
  source: {server: Server.HTTPS_PUBLIC},
  sourceDescription: 'public',
  targetServer: Server.HTTPS_PRIVATE,
  targetDescription: 'private',
});

subsetTestByKey(
    'from-public', promise_test, t => fencedFrameFetchTest(t, {
                                   source: {server: Server.HTTPS_PUBLIC},
                                   target: {server: Server.HTTPS_PUBLIC},
                                   fetchOptions: {method: 'GET', mode: 'cors'},
                                   expected: FetchTestResult.SUCCESS,
                                 }),
    'public to public: no preflight required.');
