blob: cc991c618e762f79adf95b107cd75e1fef0cbfae [file] [log] [blame]
// 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