blob: 20a54488ecc9b21d215911a37dc80f64388e7491 [file] [log] [blame]
// Copyright 2024 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/plus_addresses/plus_address_preallocator.h"
#include "base/json/values_util.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "components/plus_addresses/features.h"
#include "components/plus_addresses/mock_plus_address_http_client.h"
#include "components/plus_addresses/plus_address_http_client.h"
#include "components/plus_addresses/plus_address_prefs.h"
#include "components/plus_addresses/plus_address_types.h"
#include "components/plus_addresses/settings/fake_plus_address_setting_service.h"
#include "components/prefs/testing_pref_service.h"
#include "net/http/http_status_code.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace plus_addresses {
namespace {
using base::test::RunOnceCallback;
using ::testing::_;
using ::testing::InSequence;
using ::testing::IsEmpty;
using ::testing::MockFunction;
using ::testing::NiceMock;
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;
base::Value CreatePreallocatedPlusAddress(base::Time end_of_life,
std::string address = "") {
return base::Value(
base::Value::Dict()
.Set(PlusAddressPreallocator::kEndOfLifeKey,
base::TimeToValue(end_of_life))
.Set(PlusAddressPreallocator::kPlusAddressKey, std::move(address)));
}
MATCHER_P2(IsPreallocatedPlusAddress, end_of_life, address, "") {
if (!arg.is_dict()) {
return false;
}
const base::Value::Dict& d = arg.GetDict();
const std::string* plus_address =
d.FindString(PlusAddressPreallocator::kPlusAddressKey);
return plus_address && *plus_address == address &&
base::ValueToTime(d.Find(PlusAddressPreallocator::kEndOfLifeKey)) ==
end_of_life;
}
} // namespace
class PlusAddressPreallocatorTest : public ::testing::Test {
public:
PlusAddressPreallocatorTest() {
prefs::RegisterProfilePrefs(pref_service_.registry());
// By default, assume that the notice has been accepted and plus addresses
// are enabled.
setting_service().set_has_accepted_notice(true);
setting_service().set_is_plus_addresses_enabled(true);
// Forward to make sure we can subtract from `base::Time::Now()` without
// running into negative values.
task_environment_.FastForwardBy(base::Days(100));
}
protected:
MockPlusAddressHttpClient& http_client() { return http_client_; }
PrefService& pref_service() { return pref_service_; }
FakePlusAddressSettingService& setting_service() { return setting_service_; }
base::test::TaskEnvironment& task_environment() { return task_environment_; }
const base::Value::List& GetPreallocatedAddresses() {
return pref_service_.GetList(prefs::kPreallocatedAddresses);
}
void SetPreallocatedAddresses(base::Value::List addresses) {
pref_service_.SetList(prefs::kPreallocatedAddresses, std::move(addresses));
}
int GetPreallocatedAddressesNext() {
return pref_service_.GetInteger(prefs::kPreallocatedAddressesNext);
}
void SetPreallocatedAddressesNext(int next_index) {
pref_service_.SetInteger(prefs::kPreallocatedAddressesNext, next_index);
}
private:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
TestingPrefServiceSimple pref_service_;
NiceMock<MockPlusAddressHttpClient> http_client_;
FakePlusAddressSettingService setting_service_;
};
// Tests that plus addresses with an end of life in the future are not pruned on
// creation of the `PlusAddressPreallocator`.
TEST_F(PlusAddressPreallocatorTest,
PrunePreallocatedPlusAddressesWithEolInFuture) {
SetPreallocatedAddresses(base::Value::List()
.Append(CreatePreallocatedPlusAddress(
base::Time::Now() + base::Days(1)))
.Append(CreatePreallocatedPlusAddress(
base::Time::Now() + base::Days(2))));
SetPreallocatedAddressesNext(1);
PlusAddressPreallocator allocator(&pref_service(), &setting_service(),
&http_client());
EXPECT_THAT(GetPreallocatedAddresses(), SizeIs(2));
EXPECT_EQ(GetPreallocatedAddressesNext(), 1);
}
// Tests that plus addresses with an end of life in the past are pruned on
// creation of the `PlusAddressPreallocator` and the index of the next plus
// address is set to 0 if no entries remain.
TEST_F(PlusAddressPreallocatorTest,
PrunePreallocatedPlusAddressesWithEolInPast) {
SetPreallocatedAddresses(base::Value::List()
.Append(CreatePreallocatedPlusAddress(
base::Time::Now() - base::Days(1)))
.Append(CreatePreallocatedPlusAddress(
base::Time::Now() - base::Days(2))));
SetPreallocatedAddressesNext(1);
PlusAddressPreallocator allocator(&pref_service(), &setting_service(),
&http_client());
EXPECT_THAT(GetPreallocatedAddresses(), IsEmpty());
EXPECT_EQ(GetPreallocatedAddressesNext(), 0);
}
// Tests that plus addresses with an end of life in the past are pruned on
// creation of the `PlusAddressPreallocator` and the index of the next plus
// address is in bounds.
TEST_F(PlusAddressPreallocatorTest,
PrunePreallocatedPlusAddressesWithMixedEols) {
SetPreallocatedAddresses(
base::Value::List()
.Append(
CreatePreallocatedPlusAddress(base::Time::Now() - base::Days(1)))
.Append(
CreatePreallocatedPlusAddress(base::Time::Now() - base::Days(2)))
.Append(
CreatePreallocatedPlusAddress(base::Time::Now() + base::Days(2)))
.Append(
CreatePreallocatedPlusAddress(base::Time::Now() + base::Days(3)))
.Append(CreatePreallocatedPlusAddress(base::Time::Now() +
base::Days(4))));
SetPreallocatedAddressesNext(4);
PlusAddressPreallocator allocator(&pref_service(), &setting_service(),
&http_client());
EXPECT_THAT(GetPreallocatedAddresses(), SizeIs(3));
EXPECT_EQ(GetPreallocatedAddressesNext(), 1);
}
// Tests that preallocated plus addresses are requested on startup if there are
// fewer than the minimum size present.
TEST_F(PlusAddressPreallocatorTest, RequestPreallocatedAddressesOnStartup) {
base::test::ScopedFeatureList feature;
feature.InitAndEnableFeatureWithParameters(
features::kPlusAddressPreallocation,
{{features::kPlusAddressPreallocationMinimumSize.name, "10"}});
SetPreallocatedAddresses(base::Value::List().Append(
CreatePreallocatedPlusAddress(base::Time::Now() + base::Days(1))));
MockFunction<void()> check;
{
InSequence s;
EXPECT_CALL(check, Call);
EXPECT_CALL(http_client(), PreallocatePlusAddresses)
.WillOnce(RunOnceCallback<0>(
PlusAddressHttpClient::PreallocatePlusAddressesResult(
{PlusAddressHttpClient::PreallocatedPlusAddress{
.plus_address = "plus@plus.com",
.lifetime = base::Days(1)},
PlusAddressHttpClient::PreallocatedPlusAddress{
.plus_address = "plus2@plus.com",
.lifetime = base::Days(3)}})));
EXPECT_CALL(check, Call);
}
PlusAddressPreallocator allocator(&pref_service(), &setting_service(),
&http_client());
check.Call();
task_environment().FastForwardBy(
PlusAddressPreallocator::kDelayUntilServerRequestAfterStartup);
check.Call();
EXPECT_THAT(GetPreallocatedAddresses(),
UnorderedElementsAre(
_,
IsPreallocatedPlusAddress(base::Time::Now() + base::Days(1),
"plus@plus.com"),
IsPreallocatedPlusAddress(base::Time::Now() + base::Days(3),
"plus2@plus.com")));
}
// Tests that no addresses are requested on startup if there are already enough
// present.
TEST_F(PlusAddressPreallocatorTest,
DoNotRequestPreallocatedAddressesOnStartup) {
base::test::ScopedFeatureList feature;
feature.InitAndEnableFeatureWithParameters(
features::kPlusAddressPreallocation,
{{features::kPlusAddressPreallocationMinimumSize.name, "1"}});
SetPreallocatedAddresses(base::Value::List().Append(
CreatePreallocatedPlusAddress(base::Time::Now() + base::Days(1))));
EXPECT_CALL(http_client(), PreallocatePlusAddresses).Times(0);
PlusAddressPreallocator allocator(&pref_service(), &setting_service(),
&http_client());
task_environment().FastForwardBy(
PlusAddressPreallocator::kDelayUntilServerRequestAfterStartup);
}
// Tests that errors returned from the preallocation call are handled.
TEST_F(PlusAddressPreallocatorTest, HandleNetworkError) {
base::test::ScopedFeatureList feature;
feature.InitAndEnableFeatureWithParameters(
features::kPlusAddressPreallocation,
{{features::kPlusAddressPreallocationMinimumSize.name, "10"}});
SetPreallocatedAddresses(base::Value::List().Append(
CreatePreallocatedPlusAddress(base::Time::Now() + base::Days(1))));
MockFunction<void()> check;
{
InSequence s;
PlusAddressHttpClient::PreallocatePlusAddressesResult result =
base::unexpected(
PlusAddressRequestError::AsNetworkError(net::HTTP_NOT_FOUND));
EXPECT_CALL(http_client(), PreallocatePlusAddresses)
.WillOnce(RunOnceCallback<0>(result));
EXPECT_CALL(check, Call);
}
PlusAddressPreallocator allocator(&pref_service(), &setting_service(),
&http_client());
task_environment().FastForwardBy(
PlusAddressPreallocator::kDelayUntilServerRequestAfterStartup);
check.Call();
EXPECT_THAT(GetPreallocatedAddresses(), SizeIs(1));
}
} // namespace plus_addresses