910e62b5创建于 1月15日历史提交
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/attribution_reporting/aggregatable_trigger_config.h"

#include <optional>
#include <string>

#include "base/test/gmock_expected_support.h"
#include "base/test/values_test_util.h"
#include "base/values.h"
#include "components/attribution_reporting/aggregatable_filtering_id_max_bytes.h"
#include "components/attribution_reporting/source_registration_time_config.mojom.h"
#include "components/attribution_reporting/test_utils.h"
#include "components/attribution_reporting/trigger_registration_error.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace attribution_reporting {
namespace {

using ::attribution_reporting::mojom::SourceRegistrationTimeConfig;
using ::attribution_reporting::mojom::TriggerRegistrationError;
using ::base::test::ErrorIs;
using ::base::test::ValueIs;
using ::testing::Optional;
using ::testing::Property;

const AggregatableFilteringIdsMaxBytes kFilteringIdMaxBytes;

TEST(AggregatableTriggerConfigTest, ParseAggregatableSourceRegistrationTime) {
  const struct {
    const char* desc;
    const char* json;
    ::testing::Matcher<
        base::expected<AggregatableTriggerConfig, TriggerRegistrationError>>
        matches;
  } kTestCases[] = {
      {
          "empty",
          R"json({})json",
          ValueIs(AggregatableTriggerConfig()),
      },
      {
          "aggregatable_source_registration_time_include",
          R"json({"aggregatable_source_registration_time":"include"})json",
          ValueIs(Property(
              &AggregatableTriggerConfig::source_registration_time_config,
              SourceRegistrationTimeConfig::kInclude)),
      },
      {
          "aggregatable_source_registration_time_exclude",
          R"json({"aggregatable_source_registration_time":"exclude"})json",
          ValueIs(Property(
              &AggregatableTriggerConfig::source_registration_time_config,
              SourceRegistrationTimeConfig::kExclude)),
      },
      {
          "aggregatable_source_registration_time_wrong_type",
          R"json({"aggregatable_source_registration_time":123})json",
          ErrorIs(TriggerRegistrationError::
                      kAggregatableSourceRegistrationTimeValueInvalid),
      },
      {
          "aggregatable_source_registration_time_invalid_value",
          R"json({"aggregatable_source_registration_time":"unknown"})json",
          ErrorIs(TriggerRegistrationError::
                      kAggregatableSourceRegistrationTimeValueInvalid),
      },
  };

  for (const auto& test_case : kTestCases) {
    SCOPED_TRACE(test_case.desc);

    base::Value::Dict input = base::test::ParseJsonDict(test_case.json);

    EXPECT_THAT(AggregatableTriggerConfig::Parse(input), test_case.matches);
  }
}

TEST(AggregatableTriggerConfigTest, ParseTriggerContextId) {
  const struct {
    const char* desc;
    const char* json;
    ::testing::Matcher<
        base::expected<AggregatableTriggerConfig, TriggerRegistrationError>>
        enabled_matches;
    ::testing::Matcher<
        base::expected<AggregatableTriggerConfig, TriggerRegistrationError>>
        disabled_matches;
  } kTestCases[] = {
      {
          "empty",
          R"json({})json",
          ValueIs(AggregatableTriggerConfig()),
          ValueIs(AggregatableTriggerConfig()),
      },
      {
          "trigger_context_id_valid",
          R"json({"trigger_context_id":"123"})json",
          ValueIs(Property(&AggregatableTriggerConfig::trigger_context_id,
                           Optional(std::string("123")))),
          ValueIs(AggregatableTriggerConfig()),
      },
      {
          "trigger_context_id_wrong_type",
          R"json({"trigger_context_id":123})json",
          ErrorIs(TriggerRegistrationError::kTriggerContextIdInvalidValue),
          ValueIs(AggregatableTriggerConfig()),
      },
      {
          "trigger_context_id_disallowed",
          R"json({
            "aggregatable_source_registration_time":"include",
            "trigger_context_id":"123"
          })json",
          ErrorIs(TriggerRegistrationError::
                      kTriggerContextIdInvalidSourceRegistrationTimeConfig),
          ValueIs(*AggregatableTriggerConfig::Create(
              SourceRegistrationTimeConfig::kInclude,
              /*trigger_context_id=*/std::nullopt, kFilteringIdMaxBytes)),
      },
  };

  for (const auto& test_case : kTestCases) {
    SCOPED_TRACE(test_case.desc);

    base::Value::Dict input = base::test::ParseJsonDict(test_case.json);
    EXPECT_THAT(AggregatableTriggerConfig::Parse(input),
                test_case.enabled_matches);
  }
}

TEST(AggregatableTriggerConfigTest, ParseAggregatableFilteringIdMaxByte) {
  const struct {
    const char* desc;
    const char* json;
    ::testing::Matcher<
        base::expected<AggregatableTriggerConfig, TriggerRegistrationError>>
        matches;
  } kTestCases[] = {
      {
          "aggregatable_filtering_id_max_bytes",
          R"json({"aggregatable_filtering_id_max_bytes": 3})json",
          ValueIs(Property(
              &AggregatableTriggerConfig::aggregatable_filtering_id_max_bytes,
              *AggregatableFilteringIdsMaxBytes::Create(3))),
      },
      {
          "aggregatable_filtering_id_max_bytes_wrong_type",
          R"json({"aggregatable_filtering_id_max_bytes": "3"})json",
          ErrorIs(TriggerRegistrationError::
                      kAggregatableFilteringIdMaxBytesInvalidValue),
      },
      {
          "aggregatable_filtering_id_max_bytes_disallowed",
          R"json({
            "aggregatable_source_registration_time": "include",
            "aggregatable_filtering_id_max_bytes": 3
          })json",
          ErrorIs(
              TriggerRegistrationError::
                  kAggregatableFilteringIdsMaxBytesInvalidSourceRegistrationTimeConfig),
      },
      {
          "aggregatable_filtering_id_default_max_bytes_allowed",
          R"json({
            "aggregatable_source_registration_time": "include",
            "aggregatable_filtering_id_max_bytes": 1
          })json",
          ValueIs(Property(
              &AggregatableTriggerConfig::aggregatable_filtering_id_max_bytes,
              AggregatableFilteringIdsMaxBytes())),
      },
  };

  for (const auto& test_case : kTestCases) {
    SCOPED_TRACE(test_case.desc);

    base::Value::Dict input = base::test::ParseJsonDict(test_case.json);
    EXPECT_THAT(AggregatableTriggerConfig::Parse(input), test_case.matches);
  }
}

TEST(AggregatableTriggerConfigTest, Create) {
  const struct {
    const char* desc;
    SourceRegistrationTimeConfig source_registration_time_config;
    std::optional<std::string> trigger_context_id;
    AggregatableFilteringIdsMaxBytes filtering_id_max_bytes;
    std::optional<AggregatableTriggerConfig> expected;
    std::optional<bool> should_cause_a_report_to_be_sent_unconditionally;
  } kTestCases[] = {
      {
          "valid_exclude_source_registration_time_with_trigger_context_id",
          SourceRegistrationTimeConfig::kExclude,
          "123",
          kFilteringIdMaxBytes,
          *AggregatableTriggerConfig::Create(
              SourceRegistrationTimeConfig::kExclude, "123",
              kFilteringIdMaxBytes),
          /*should_cause_a_report_to_be_sent_unconditionally=*/true,
      },
      {
          "valid_exclude_source_registration_time_without_trigger_context_id",
          SourceRegistrationTimeConfig::kExclude,
          /*trigger_context_id=*/std::nullopt,
          kFilteringIdMaxBytes,
          AggregatableTriggerConfig(),
          /*should_cause_a_report_to_be_sent_unconditionally=*/false,
      },
      {
          "valid_include_source_registration_time_without_trigger_context_id",
          SourceRegistrationTimeConfig::kInclude,
          /*trigger_context_id=*/std::nullopt,
          kFilteringIdMaxBytes,
          *AggregatableTriggerConfig::Create(
              SourceRegistrationTimeConfig::kInclude, std::nullopt,
              kFilteringIdMaxBytes),
          /*should_cause_a_report_to_be_sent_unconditionally=*/false,
      },
      {
          "trigger_context_id_too_long",
          SourceRegistrationTimeConfig::kExclude,
          std::string(65, 'a'),
          kFilteringIdMaxBytes,
          /*expected=*/std::nullopt,
          /*should_cause_a_report_to_be_sent_unconditionally=*/std::nullopt,
      },
      {
          "trigger_context_id_disallowed",
          SourceRegistrationTimeConfig::kInclude,
          "123",
          kFilteringIdMaxBytes,
          /*expected=*/std::nullopt,
          /*should_cause_a_report_to_be_sent_unconditionally=*/std::nullopt,
      },
      {
          "non_default_filtering_id_max_bytes_disallowed",
          SourceRegistrationTimeConfig::kInclude,
          /*trigger_context_id=*/std::nullopt,
          *AggregatableFilteringIdsMaxBytes::Create(2),
          /*expected=*/std::nullopt,
          /*should_cause_a_report_to_be_sent_unconditionally=*/std::nullopt,
      },
      {
          "valid_non_default_filtering_id_max_bytes",
          SourceRegistrationTimeConfig::kExclude,
          /*trigger_context_id=*/std::nullopt,
          *AggregatableFilteringIdsMaxBytes::Create(2),
          /*expected=*/
          *AggregatableTriggerConfig::Create(
              SourceRegistrationTimeConfig::kExclude, std::nullopt,
              *AggregatableFilteringIdsMaxBytes::Create(2)),
          /*should_cause_a_report_to_be_sent_unconditionally=*/true,
      },
      {
          "valid_non_default_filtering_id_max_bytes_and_triger_context_id",
          SourceRegistrationTimeConfig::kExclude,
          /*trigger_context_id=*/"123",
          *AggregatableFilteringIdsMaxBytes::Create(2),
          /*expected=*/
          *AggregatableTriggerConfig::Create(
              SourceRegistrationTimeConfig::kExclude, "123",
              *AggregatableFilteringIdsMaxBytes::Create(2)),
          /*should_cause_a_report_to_be_sent_unconditionally=*/true,
      },
  };

  for (const auto& test_case : kTestCases) {
    SCOPED_TRACE(test_case.desc);

    ASSERT_EQ(
        test_case.expected.has_value(),
        test_case.should_cause_a_report_to_be_sent_unconditionally.has_value());

    auto actual = AggregatableTriggerConfig::Create(
        test_case.source_registration_time_config, test_case.trigger_context_id,
        test_case.filtering_id_max_bytes);
    EXPECT_EQ(actual, test_case.expected);
    if (test_case.expected.has_value()) {
      EXPECT_EQ(actual->ShouldCauseAReportToBeSentUnconditionally(),
                test_case.should_cause_a_report_to_be_sent_unconditionally);
    }
  }
}

TEST(AggregatableTriggerConfigTest, Parse_TriggerContextIdLength) {
  constexpr char kTriggerContextId[] = "trigger_context_id";

  base::Value::Dict dict;
  dict.Set(kTriggerContextId, std::string(64, 'a'));
  EXPECT_THAT(AggregatableTriggerConfig::Parse(dict),
              ValueIs(Property(&AggregatableTriggerConfig::trigger_context_id,
                               Optional(std::string(64, 'a')))));

  dict.Set(kTriggerContextId, std::string(65, 'a'));
  EXPECT_THAT(AggregatableTriggerConfig::Parse(dict),
              ErrorIs(TriggerRegistrationError::kTriggerContextIdInvalidValue));
}

TEST(AggregatableTriggerConfigTest, Serialize) {
  const struct {
    AggregatableTriggerConfig input;
    const char* expected_json;
  } kTestCases[] = {
      {
          AggregatableTriggerConfig(),
          R"json({
            "aggregatable_filtering_id_max_bytes": 1,
            "aggregatable_source_registration_time": "exclude"
          })json",
      },
      {
          *AggregatableTriggerConfig::Create(
              SourceRegistrationTimeConfig::kInclude,
              /*trigger_context_id=*/std::nullopt, kFilteringIdMaxBytes),
          R"json({
            "aggregatable_filtering_id_max_bytes": 1,
            "aggregatable_source_registration_time": "include"
          })json",
      },
      {
          *AggregatableTriggerConfig::Create(
              SourceRegistrationTimeConfig::kExclude,
              /*trigger_context_id=*/"123",
              *AggregatableFilteringIdsMaxBytes::Create(3u)),
          R"json({
            "aggregatable_source_registration_time":"exclude",
            "aggregatable_filtering_id_max_bytes": 3,
            "trigger_context_id": "123"
          })json",
      },
  };

  for (const auto& test_case : kTestCases) {
    SCOPED_TRACE(test_case.input);

    base::Value::Dict dict;
    test_case.input.Serialize(dict);
    EXPECT_THAT(dict, base::test::IsJson(test_case.expected_json));
  }
}

}  // namespace
}  // namespace attribution_reporting