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

#include "content/browser/private_aggregation/private_aggregation_budget_key.h"

#include <optional>

#include "base/time/time.h"
#include "content/browser/private_aggregation/private_aggregation_caller_api.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"

namespace content {

namespace {

constexpr auto kExampleTime =
    base::Time::FromMillisecondsSinceUnixEpoch(1652984901234);

// `kExampleTime` floored to a minute boundary.
constexpr auto kExampleMinuteBoundary =
    base::Time::FromMillisecondsSinceUnixEpoch(1652984880000);

constexpr char kExampleOriginUrl[] = "https://origin.example";

}  // namespace

TEST(PrivateAggregationBudgetKeyTest, Fields_MatchInputs) {
  const url::Origin example_origin =
      url::Origin::Create(GURL(kExampleOriginUrl));

  std::optional<PrivateAggregationBudgetKey> protected_audience_key =
      PrivateAggregationBudgetKey::Create(
          example_origin, kExampleTime,
          PrivateAggregationCallerApi::kProtectedAudience);
  ASSERT_TRUE(protected_audience_key.has_value());
  EXPECT_EQ(protected_audience_key->origin(), example_origin);
  EXPECT_EQ(protected_audience_key->time_window().start_time(),
            kExampleMinuteBoundary);
  EXPECT_EQ(protected_audience_key->caller_api(),
            PrivateAggregationCallerApi::kProtectedAudience);

  std::optional<PrivateAggregationBudgetKey> shared_storage_key =
      PrivateAggregationBudgetKey::Create(
          example_origin, kExampleTime,
          PrivateAggregationCallerApi::kSharedStorage);
  ASSERT_TRUE(shared_storage_key.has_value());
  EXPECT_EQ(shared_storage_key->origin(), example_origin);
  EXPECT_EQ(shared_storage_key->time_window().start_time(),
            kExampleMinuteBoundary);
  EXPECT_EQ(shared_storage_key->caller_api(),
            PrivateAggregationCallerApi::kSharedStorage);
}

TEST(PrivateAggregationBudgetKeyTest, StartTimes_FlooredToTheMinute) {
  const url::Origin example_origin =
      url::Origin::Create(GURL(kExampleOriginUrl));

  std::optional<PrivateAggregationBudgetKey> example_key =
      PrivateAggregationBudgetKey::Create(
          example_origin, /*api_invocation_time=*/kExampleTime,
          PrivateAggregationCallerApi::kProtectedAudience);
  ASSERT_TRUE(example_key.has_value());
  EXPECT_EQ(example_key->time_window().start_time(), kExampleMinuteBoundary);

  std::optional<PrivateAggregationBudgetKey> on_the_minute =
      PrivateAggregationBudgetKey::Create(
          example_origin, /*api_invocation_time=*/kExampleMinuteBoundary,
          PrivateAggregationCallerApi::kProtectedAudience);
  ASSERT_TRUE(on_the_minute.has_value());
  EXPECT_EQ(on_the_minute->time_window().start_time(), kExampleMinuteBoundary);

  std::optional<PrivateAggregationBudgetKey> just_after_the_minute =
      PrivateAggregationBudgetKey::Create(
          example_origin,
          /*api_invocation_time=*/kExampleMinuteBoundary +
              base::Microseconds(1),
          PrivateAggregationCallerApi::kProtectedAudience);
  ASSERT_TRUE(just_after_the_minute.has_value());
  EXPECT_EQ(just_after_the_minute->time_window().start_time(),
            kExampleMinuteBoundary);

  std::optional<PrivateAggregationBudgetKey> just_before_the_minute =
      PrivateAggregationBudgetKey::Create(
          example_origin,
          /*api_invocation_time=*/kExampleMinuteBoundary -
              base::Microseconds(1),
          PrivateAggregationCallerApi::kProtectedAudience);
  ASSERT_TRUE(just_before_the_minute.has_value());
  EXPECT_EQ(just_before_the_minute->time_window().start_time(),
            kExampleMinuteBoundary - base::Minutes(1));
}

TEST(PrivateAggregationBudgetKeyTest, ExtremeStartTimes_HandledCorrectly) {
  // The earliest window should report an extreme start time as its 'conceptual'
  // start time can't be represented.
  EXPECT_EQ(
      PrivateAggregationBudgetKey::TimeWindow(base::Time::Min()).start_time(),
      base::Time::Min());
  EXPECT_EQ(PrivateAggregationBudgetKey::TimeWindow(base::Time::Min() +
                                                    base::Microseconds(1))
                .start_time(),
            base::Time::Min());

  // The second earliest window should report a start time 'on the minute'
  // again.
  PrivateAggregationBudgetKey::TimeWindow second_earliest_window(
      base::Time::Min() + PrivateAggregationBudgetKey::TimeWindow::kDuration);
  EXPECT_NE(second_earliest_window.start_time(), base::Time::Min());
  EXPECT_LE(
      second_earliest_window.start_time(),
      base::Time::Min() + PrivateAggregationBudgetKey::TimeWindow::kDuration);
  EXPECT_EQ(
      second_earliest_window.start_time().since_origin().InMicroseconds() %
          base::Time::kMicrosecondsPerMinute,
      0);

  // `base::Time::Max()` is disallowed, but otherwise the last window should
  // have no issue rounding down.
  PrivateAggregationBudgetKey::TimeWindow last_window(base::Time::Max() -
                                                      base::Microseconds(1));
  EXPECT_LT(last_window.start_time(), base::Time::Max());
  EXPECT_EQ(last_window.start_time().since_origin().InMicroseconds() %
                base::Time::kMicrosecondsPerMinute,
            0);
  EXPECT_LE(base::Time::Max() - last_window.start_time(),
            PrivateAggregationBudgetKey::TimeWindow::kDuration);
}

TEST(PrivateAggregationBudgetKeyTest, UntrustworthyOrigin_KeyCreationFailed) {
  std::optional<PrivateAggregationBudgetKey> opaque_origin_budget_key =
      PrivateAggregationBudgetKey::Create(
          url::Origin(), kExampleTime,
          PrivateAggregationCallerApi::kProtectedAudience);
  EXPECT_FALSE(opaque_origin_budget_key.has_value());

  std::optional<PrivateAggregationBudgetKey> insecure_origin_budget_key =
      PrivateAggregationBudgetKey::Create(
          url::Origin::Create(GURL("http://origin.example")), kExampleTime,
          PrivateAggregationCallerApi::kProtectedAudience);
  EXPECT_FALSE(insecure_origin_budget_key.has_value());
}

}  // namespace content