chromium/third_party/blink/web_tests/wpt_internal/aggregation-service/test-internals.window.js

// META: script=../aggregation-service/support/aggregation-service.js

test(() => {
  // The `TextEncoder` approach to converting strings to Uint8Array is tempting
  // because it sometimes works. ASCII values (0 through 0x7f) pass through
  // unmodified, but values between 0x80 and 0xff incorrectly produce multiple
  // bytes!
  assert_array_equals(decodeBase64ToUint8Array(btoa('\x7f')), [0x7f]);
  assert_array_equals(decodeBase64ToUint8Array(btoa('\x80')), [0x80]);
}, 'decodeBase64ToUint8Array() handles non-ASCII bytes');

test(() => {
  const ONE_CONTRIBUTION_EXAMPLE = Object.freeze([{
    bucket: encodeBigInt(1n, 16),
    value: encodeBigInt(2n, 4),
    id: encodeBigInt(0n, 1),
  }]);

  assert_payload_equals(
      buildExpectedPayload(ONE_CONTRIBUTION_EXAMPLE, 20),
      buildExpectedPayload(ONE_CONTRIBUTION_EXAMPLE, 20));
}, 'assert_payload_equals() is reflexive');

test(() => {
  // Payload with contributions [{bucket: 1n, value: 2, filteringId: 3n}],
  // padded to 20 contributions
  const ONE_CONTRIBUTION_PADDED =
      'omRkYXRhlKNiaWRBA2V2YWx1ZUQAAAACZmJ1Y2tldFAAAAAAAAAAAAAAAAAAAAABo2JpZEEAZX' +
      'ZhbHVlRAAAAABmYnVja2V0UAAAAAAAAAAAAAAAAAAAAACjYmlkQQBldmFsdWVEAAAAAGZidWNr' +
      'ZXRQAAAAAAAAAAAAAAAAAAAAAKNiaWRBAGV2YWx1ZUQAAAAAZmJ1Y2tldFAAAAAAAAAAAAAAAA' +
      'AAAAAAo2JpZEEAZXZhbHVlRAAAAABmYnVja2V0UAAAAAAAAAAAAAAAAAAAAACjYmlkQQBldmFs' +
      'dWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKNiaWRBAGV2YWx1ZUQAAAAAZmJ1Y2tldF' +
      'AAAAAAAAAAAAAAAAAAAAAAo2JpZEEAZXZhbHVlRAAAAABmYnVja2V0UAAAAAAAAAAAAAAAAAAA' +
      'AACjYmlkQQBldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKNiaWRBAGV2YWx1ZU' +
      'QAAAAAZmJ1Y2tldFAAAAAAAAAAAAAAAAAAAAAAo2JpZEEAZXZhbHVlRAAAAABmYnVja2V0UAAA' +
      'AAAAAAAAAAAAAAAAAACjYmlkQQBldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAK' +
      'NiaWRBAGV2YWx1ZUQAAAAAZmJ1Y2tldFAAAAAAAAAAAAAAAAAAAAAAo2JpZEEAZXZhbHVlRAAA' +
      'AABmYnVja2V0UAAAAAAAAAAAAAAAAAAAAACjYmlkQQBldmFsdWVEAAAAAGZidWNrZXRQAAAAAA' +
      'AAAAAAAAAAAAAAAKNiaWRBAGV2YWx1ZUQAAAAAZmJ1Y2tldFAAAAAAAAAAAAAAAAAAAAAAo2Jp' +
      'ZEEAZXZhbHVlRAAAAABmYnVja2V0UAAAAAAAAAAAAAAAAAAAAACjYmlkQQBldmFsdWVEAAAAAG' +
      'ZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKNiaWRBAGV2YWx1ZUQAAAAAZmJ1Y2tldFAAAAAAAAAA' +
      'AAAAAAAAAAAAo2JpZEEAZXZhbHVlRAAAAABmYnVja2V0UAAAAAAAAAAAAAAAAAAAAABpb3Blcm' +
      'F0aW9uaWhpc3RvZ3JhbQ==';

  const payload = CborParser.parse(ONE_CONTRIBUTION_PADDED);

  const EXPECTED_EXAMPLE = {
    operation: 'histogram',
    data: [{
            bucket: encodeBigInt(1n, 16),
            value: encodeBigInt(2n, 4),
            id: encodeBigInt(3n, 1),
          }].concat(Array(19).fill(NULL_CONTRIBUTION)),
  };

  assert_payload_equals(payload, EXPECTED_EXAMPLE);
}, 'CborParser correctly parses a realistic input');

test(() => {
  // Hex bytes of a payload with contributions [{bucket: 1n, value:2, id: 3n}].
  const ONE_CONTRIBUTION_HEX = [
    0xa2,                                                  // map(2)
    0x69,                                                  // text(9)
    0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,  // "operation"
    0x69,                                                  // text(9)
    0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d,  // "histogram"
    0x64,                                                  // text(4)
    0x64, 0x61, 0x74, 0x61,                                // "data"
    0x81,                                                  // array(1)
    0xa3,                                                  // map(3)
    0x62,                                                  // text(2)
    0x69, 0x64,                                            // "id"
    0x41,                                                  // bytes(1)
    0x03,                                                  //
    0x65,                                                  // text(5)
    0x76, 0x61, 0x6c, 0x75, 0x65,                          // "value"
    0x44,                                                  // bytes(4)
    0x00, 0x00, 0x00, 0x02,                                //
    0x66,                                                  // text(6)
    0x62, 0x75, 0x63, 0x6b, 0x65, 0x74,                    // "bucket"
    0x50,                                                  // bytes(16)
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
  ];

  const payloadString = String.fromCharCode(...ONE_CONTRIBUTION_HEX);
  const payloadStringWithExtraByte =
      String.fromCharCode(...ONE_CONTRIBUTION_HEX, 0x42);

  // This should succeed without throwing an exception.
  const parsedPayload = CborParser.parse(btoa(payloadString));
  assert_payload_equals(parsedPayload, {
    operation: 'histogram',
    data: [{
      bucket: encodeBigInt(1n, 16),
      value: encodeBigInt(2n, 4),
      id: encodeBigInt(3n, 1),
    }],
  });

  // NOTE: We expect this to fail. See the expectation file that corresponds to
  // this source file.
  CborParser.parse(btoa(payloadStringWithExtraByte));
}, 'CborParser rejects input with a trailing, unparsed byte');

test(
    () =>
        assert_throws_dom('InvalidCharacterError', () => CborParser.parse('*')),
    'CborParser.parse rejects invalid base64 strings');

test(() => {
  assert_array_equals(
      CborParser.parse(btoa('\x98\x02\x65hello\x65world')), ['hello', 'world'],
      'Should parse CBOR array with one-byte length prefix');
  assert_array_equals(
      CborParser.parse(btoa('\x99\x00\x02\x65hello\x65world')),
      ['hello', 'world'],
      'Should parse CBOR array with two-byte length prefix');
  assert_array_equals(
      CborParser.parse(btoa('\x9a\x00\x00\x00\x02\x65hello\x65world')),
      ['hello', 'world'],
      'Should parse CBOR array with four-byte length prefix');
}, 'CborParser can parse arrays with length prefixes');