[go: nahoru, domu]

blob: 23ec4c5b06d5b8d4f53367a9c5dad95ee4c312aa [file] [log] [blame]
// Copyright 2021 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/aggregation_service/aggregatable_report_assembler.h"
#include <stddef.h>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/gmock_move_support.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "content/browser/aggregation_service/aggregatable_report.h"
#include "content/browser/aggregation_service/aggregation_service_key_fetcher.h"
#include "content/browser/aggregation_service/aggregation_service_test_utils.h"
#include "content/browser/aggregation_service/public_key.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/private_aggregation/aggregatable_report.mojom.h"
#include "url/gurl.h"
namespace content {
namespace {
using ::testing::_;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::Return;
// Will be used to verify the sequence of expected function calls.
using Checkpoint = ::testing::MockFunction<void(int)>;
using FetchCallback = AggregationServiceKeyFetcher::FetchCallback;
using AssemblyCallback = AggregatableReportAssembler::AssemblyCallback;
using PublicKeyFetchStatus = AggregationServiceKeyFetcher::PublicKeyFetchStatus;
using AssemblyStatus = AggregatableReportAssembler::AssemblyStatus;
constexpr char kReportAssemblerStatusHistogramName[] =
"PrivacySandbox.AggregationService.ReportAssembler.Status";
auto CloneRequestAndReturnReport(std::optional<AggregatableReportRequest>* out,
AggregatableReport report) {
return [out, report = std::move(report)](
const AggregatableReportRequest& report_request,
std::vector<PublicKey> public_keys) {
*out = aggregation_service::CloneReportRequest(report_request);
return std::move(report);
};
}
class MockAggregationServiceKeyFetcher : public AggregationServiceKeyFetcher {
public:
MockAggregationServiceKeyFetcher()
: AggregationServiceKeyFetcher(/*storage_context=*/nullptr,
/*network_fetcher=*/nullptr) {}
MOCK_METHOD(void,
GetPublicKey,
(const GURL& url, FetchCallback callback),
(override));
};
class MockAggregatableReportProvider : public AggregatableReport::Provider {
public:
MOCK_METHOD(std::optional<AggregatableReport>,
CreateFromRequestAndPublicKeys,
(const AggregatableReportRequest&, std::vector<PublicKey>),
(const, override));
};
} // namespace
class AggregatableReportAssemblerTest : public testing::Test {
public:
AggregatableReportAssemblerTest() = default;
void SetUp() override {
auto fetcher = std::make_unique<MockAggregationServiceKeyFetcher>();
auto report_provider = std::make_unique<MockAggregatableReportProvider>();
fetcher_ = fetcher.get();
report_provider_ = report_provider.get();
assembler_ = AggregatableReportAssembler::CreateForTesting(
std::move(fetcher), std::move(report_provider));
}
void ResetAssembler() {
fetcher_ = nullptr;
report_provider_ = nullptr;
assembler_.reset();
}
AggregatableReportAssembler* assembler() { return assembler_.get(); }
MockAggregationServiceKeyFetcher* fetcher() { return fetcher_; }
MockAggregatableReportProvider* report_provider() { return report_provider_; }
base::MockCallback<AssemblyCallback>& callback() { return callback_; }
private:
std::unique_ptr<AggregatableReportAssembler> assembler_;
// These objects are owned by `assembler_`.
raw_ptr<MockAggregationServiceKeyFetcher> fetcher_;
raw_ptr<MockAggregatableReportProvider> report_provider_;
base::MockCallback<AssemblyCallback> callback_;
};
TEST_F(AggregatableReportAssemblerTest, BothKeyFetchesFail_ErrorReturned) {
base::HistogramTester histograms;
AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
blink::mojom::AggregationServiceMode::kExperimentalPoplar);
std::vector<GURL> processing_urls = request.processing_urls();
EXPECT_CALL(*fetcher(), GetPublicKey(processing_urls[0], _))
.WillOnce(base::test::RunOnceCallback<1>(
std::nullopt, PublicKeyFetchStatus::kPublicKeyFetchFailed));
EXPECT_CALL(*fetcher(), GetPublicKey(processing_urls[1], _))
.WillOnce(base::test::RunOnceCallback<1>(
std::nullopt, PublicKeyFetchStatus::kPublicKeyFetchFailed));
EXPECT_CALL(callback(),
Run(_, Eq(std::nullopt), AssemblyStatus::kPublicKeyFetchFailed));
EXPECT_CALL(*report_provider(), CreateFromRequestAndPublicKeys(_, _))
.Times(0);
assembler()->AssembleReport(std::move(request), callback().Get());
histograms.ExpectUniqueSample(
kReportAssemblerStatusHistogramName,
AggregatableReportAssembler::AssemblyStatus::kPublicKeyFetchFailed, 1);
}
TEST_F(AggregatableReportAssemblerTest, FirstKeyFetchFails_ErrorReturned) {
base::HistogramTester histograms;
AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
blink::mojom::AggregationServiceMode::kExperimentalPoplar);
std::vector<GURL> processing_urls = request.processing_urls();
EXPECT_CALL(*fetcher(), GetPublicKey(processing_urls[0], _))
.WillOnce(base::test::RunOnceCallback<1>(
std::nullopt, PublicKeyFetchStatus::kPublicKeyFetchFailed));
EXPECT_CALL(*fetcher(), GetPublicKey(processing_urls[1], _))
.WillOnce(base::test::RunOnceCallback<1>(
aggregation_service::TestHpkeKey().GetPublicKey(),
PublicKeyFetchStatus::kOk));
EXPECT_CALL(callback(),
Run(_, Eq(std::nullopt), AssemblyStatus::kPublicKeyFetchFailed));
EXPECT_CALL(*report_provider(), CreateFromRequestAndPublicKeys(_, _))
.Times(0);
assembler()->AssembleReport(std::move(request), callback().Get());
histograms.ExpectUniqueSample(
kReportAssemblerStatusHistogramName,
AggregatableReportAssembler::AssemblyStatus::kPublicKeyFetchFailed, 1);
}
TEST_F(AggregatableReportAssemblerTest, SecondKeyFetchFails_ErrorReturned) {
base::HistogramTester histograms;
AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
blink::mojom::AggregationServiceMode::kExperimentalPoplar);
std::vector<GURL> processing_urls = request.processing_urls();
EXPECT_CALL(*fetcher(), GetPublicKey(processing_urls[0], _))
.WillOnce(base::test::RunOnceCallback<1>(
aggregation_service::TestHpkeKey().GetPublicKey(),
PublicKeyFetchStatus::kOk));
EXPECT_CALL(*fetcher(), GetPublicKey(processing_urls[1], _))
.WillOnce(base::test::RunOnceCallback<1>(
std::nullopt, PublicKeyFetchStatus::kPublicKeyFetchFailed));
EXPECT_CALL(callback(),
Run(_, Eq(std::nullopt), AssemblyStatus::kPublicKeyFetchFailed));
EXPECT_CALL(*report_provider(), CreateFromRequestAndPublicKeys(_, _))
.Times(0);
assembler()->AssembleReport(std::move(request), callback().Get());
histograms.ExpectUniqueSample(
kReportAssemblerStatusHistogramName,
AggregatableReportAssembler::AssemblyStatus::kPublicKeyFetchFailed, 1);
}
TEST_F(AggregatableReportAssemblerTest,
BothKeyFetchesSucceed_ValidReportReturned) {
base::HistogramTester histograms;
AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
blink::mojom::AggregationServiceMode::kExperimentalPoplar);
std::vector<GURL> processing_urls = request.processing_urls();
std::vector<PublicKey> public_keys = {
aggregation_service::TestHpkeKey("id123").GetPublicKey(),
aggregation_service::TestHpkeKey("456abc").GetPublicKey()};
std::optional<AggregatableReport> report =
AggregatableReport::Provider().CreateFromRequestAndPublicKeys(
request, public_keys);
ASSERT_TRUE(report.has_value());
EXPECT_CALL(*fetcher(), GetPublicKey(processing_urls[0], _))
.WillOnce(base::test::RunOnceCallback<1>(public_keys[0],
PublicKeyFetchStatus::kOk));
EXPECT_CALL(*fetcher(), GetPublicKey(processing_urls[1], _))
.WillOnce(base::test::RunOnceCallback<1>(public_keys[1],
PublicKeyFetchStatus::kOk));
EXPECT_CALL(callback(), Run(_, report, AssemblyStatus::kOk));
std::optional<AggregatableReportRequest> actual_request;
EXPECT_CALL(*report_provider(),
CreateFromRequestAndPublicKeys(_, public_keys))
.WillOnce(CloneRequestAndReturnReport(&actual_request,
std::move(report.value())));
assembler()->AssembleReport(aggregation_service::CloneReportRequest(request),
callback().Get());
ASSERT_TRUE(actual_request.has_value());
EXPECT_TRUE(aggregation_service::ReportRequestsEqual(actual_request.value(),
request));
histograms.ExpectUniqueSample(
kReportAssemblerStatusHistogramName,
AggregatableReportAssembler::AssemblyStatus::kOk, 1);
}
TEST_F(AggregatableReportAssemblerTest,
OnlyKeyFetchSucceeds_ValidReportReturned) {
base::HistogramTester histograms;
AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
blink::mojom::AggregationServiceMode::kTeeBased);
PublicKey public_key =
aggregation_service::TestHpkeKey("id123").GetPublicKey();
std::optional<AggregatableReport> report =
AggregatableReport::Provider().CreateFromRequestAndPublicKeys(
request, {public_key});
ASSERT_TRUE(report.has_value());
EXPECT_CALL(*fetcher(), GetPublicKey)
.WillOnce(base::test::RunOnceCallback<1>(public_key,
PublicKeyFetchStatus::kOk));
EXPECT_CALL(callback(), Run(_, report, AssemblyStatus::kOk));
std::optional<AggregatableReportRequest> actual_request;
EXPECT_CALL(*report_provider(), CreateFromRequestAndPublicKeys(
_, std::vector<PublicKey>{public_key}))
.WillOnce(CloneRequestAndReturnReport(&actual_request,
std::move(report.value())));
assembler()->AssembleReport(aggregation_service::CloneReportRequest(request),
callback().Get());
ASSERT_TRUE(actual_request.has_value());
EXPECT_TRUE(aggregation_service::ReportRequestsEqual(actual_request.value(),
request));
histograms.ExpectUniqueSample(
kReportAssemblerStatusHistogramName,
AggregatableReportAssembler::AssemblyStatus::kOk, 1);
}
TEST_F(AggregatableReportAssemblerTest, OnlyKeyFetchFails_ErrorReturned) {
base::HistogramTester histograms;
AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
blink::mojom::AggregationServiceMode::kTeeBased);
EXPECT_CALL(*fetcher(), GetPublicKey)
.WillOnce(base::test::RunOnceCallback<1>(
std::nullopt, PublicKeyFetchStatus::kPublicKeyFetchFailed));
EXPECT_CALL(callback(),
Run(_, Eq(std::nullopt), AssemblyStatus::kPublicKeyFetchFailed));
EXPECT_CALL(*report_provider(), CreateFromRequestAndPublicKeys(_, _))
.Times(0);
assembler()->AssembleReport(std::move(request), callback().Get());
histograms.ExpectUniqueSample(
kReportAssemblerStatusHistogramName,
AggregatableReportAssembler::AssemblyStatus::kPublicKeyFetchFailed, 1);
}
TEST_F(AggregatableReportAssemblerTest,
TwoKeyFetchesReturnInSwappedOrder_ValidReportReturned) {
base::HistogramTester histograms;
AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
blink::mojom::AggregationServiceMode::kExperimentalPoplar);
std::vector<GURL> processing_urls = request.processing_urls();
std::vector<PublicKey> public_keys = {
aggregation_service::TestHpkeKey("id123").GetPublicKey(),
aggregation_service::TestHpkeKey("456abc").GetPublicKey()};
std::optional<AggregatableReport> report =
AggregatableReport::Provider().CreateFromRequestAndPublicKeys(
request, public_keys);
ASSERT_TRUE(report.has_value());
std::vector<FetchCallback> pending_callbacks(2);
EXPECT_CALL(*fetcher(), GetPublicKey(processing_urls[0], _))
.WillOnce(MoveArg<1>(&pending_callbacks.front()));
EXPECT_CALL(*fetcher(), GetPublicKey(processing_urls[1], _))
.WillOnce(MoveArg<1>(&pending_callbacks.back()));
EXPECT_CALL(callback(), Run(_, report, AssemblyStatus::kOk));
std::optional<AggregatableReportRequest> actual_request;
EXPECT_CALL(*report_provider(),
CreateFromRequestAndPublicKeys(_, public_keys))
.WillOnce(CloneRequestAndReturnReport(&actual_request,
std::move(report.value())));
assembler()->AssembleReport(aggregation_service::CloneReportRequest(request),
callback().Get());
// Swap order of responses
std::move(pending_callbacks.back())
.Run(public_keys[1], PublicKeyFetchStatus::kOk);
std::move(pending_callbacks.front())
.Run(public_keys[0], PublicKeyFetchStatus::kOk);
histograms.ExpectUniqueSample(
kReportAssemblerStatusHistogramName,
AggregatableReportAssembler::AssemblyStatus::kOk, 1);
}
TEST_F(AggregatableReportAssemblerTest,
AssemblerDeleted_PendingRequestsNotRun) {
base::HistogramTester histograms;
AggregatableReportRequest request =
aggregation_service::CreateExampleRequest();
std::vector<GURL> processing_urls = request.processing_urls();
EXPECT_CALL(callback(), Run).Times(0);
EXPECT_CALL(*fetcher(), GetPublicKey);
assembler()->AssembleReport(std::move(request), callback().Get());
ResetAssembler();
histograms.ExpectTotalCount(kReportAssemblerStatusHistogramName, 0);
}
TEST_F(AggregatableReportAssemblerTest,
MultipleSimultaneousRequests_BothSucceed) {
base::HistogramTester histograms;
AggregatableReportRequest request =
aggregation_service::CreateExampleRequest();
std::vector<GURL> processing_urls = request.processing_urls();
PublicKey public_key =
aggregation_service::TestHpkeKey("id123").GetPublicKey();
std::optional<AggregatableReport> report =
AggregatableReport::Provider().CreateFromRequestAndPublicKeys(
request, {public_key});
ASSERT_TRUE(report.has_value());
std::vector<FetchCallback> pending_callbacks(2);
EXPECT_CALL(*fetcher(), GetPublicKey(processing_urls[0], _))
.WillOnce(MoveArg<1>(&pending_callbacks.front()))
.WillOnce(MoveArg<1>(&pending_callbacks.back()));
EXPECT_CALL(callback(), Run(_, report, AssemblyStatus::kOk)).Times(2);
std::optional<AggregatableReportRequest> first_request;
std::optional<AggregatableReportRequest> second_request;
EXPECT_CALL(*report_provider(), CreateFromRequestAndPublicKeys(
_, std::vector<PublicKey>{public_key}))
.WillOnce(CloneRequestAndReturnReport(&first_request, report.value()))
.WillOnce(CloneRequestAndReturnReport(&second_request,
std::move(report.value())));
assembler()->AssembleReport(aggregation_service::CloneReportRequest(request),
callback().Get());
assembler()->AssembleReport(aggregation_service::CloneReportRequest(request),
callback().Get());
std::move(pending_callbacks.front())
.Run(public_key, PublicKeyFetchStatus::kOk);
std::move(pending_callbacks.back())
.Run(public_key, PublicKeyFetchStatus::kOk);
ASSERT_TRUE(first_request.has_value());
EXPECT_TRUE(
aggregation_service::ReportRequestsEqual(first_request.value(), request));
ASSERT_TRUE(second_request.has_value());
EXPECT_TRUE(aggregation_service::ReportRequestsEqual(second_request.value(),
request));
histograms.ExpectUniqueSample(
kReportAssemblerStatusHistogramName,
AggregatableReportAssembler::AssemblyStatus::kOk, 2);
}
TEST_F(AggregatableReportAssemblerTest,
TooManySimultaneousRequests_ErrorCausedForNewRequests) {
base::HistogramTester histograms;
PublicKey public_key =
aggregation_service::TestHpkeKey("id123").GetPublicKey();
std::optional<AggregatableReport> report =
AggregatableReport::Provider().CreateFromRequestAndPublicKeys(
aggregation_service::CreateExampleRequest(), {std::move(public_key)});
ASSERT_TRUE(report.has_value());
std::vector<FetchCallback> pending_callbacks;
Checkpoint checkpoint;
int current_call = 1;
{
testing::InSequence seq;
int current_check = 1;
EXPECT_CALL(*fetcher(), GetPublicKey)
.Times(AggregatableReportAssembler::kMaxSimultaneousRequests)
.WillRepeatedly(Invoke([&](const GURL& url, FetchCallback callback) {
pending_callbacks.push_back(std::move(callback));
}));
EXPECT_CALL(callback(), Run).Times(0);
EXPECT_CALL(checkpoint, Call(current_check++));
EXPECT_CALL(callback(), Run(_, Eq(std::nullopt),
AssemblyStatus::kTooManySimultaneousRequests))
.Times(1);
EXPECT_CALL(checkpoint, Call(current_check++));
for (size_t i = 0;
i < AggregatableReportAssembler::kMaxSimultaneousRequests; i++) {
EXPECT_CALL(checkpoint, Call(current_check++));
EXPECT_CALL(*report_provider(), CreateFromRequestAndPublicKeys)
.WillOnce(Return(report));
EXPECT_CALL(callback(), Run(_, report, AssemblyStatus::kOk));
}
}
for (size_t i = 0; i < AggregatableReportAssembler::kMaxSimultaneousRequests;
++i) {
assembler()->AssembleReport(aggregation_service::CreateExampleRequest(),
callback().Get());
}
checkpoint.Call(current_call++);
assembler()->AssembleReport(aggregation_service::CreateExampleRequest(),
callback().Get());
checkpoint.Call(current_call++);
for (FetchCallback& pending_callback : pending_callbacks) {
checkpoint.Call(current_call++);
std::move(pending_callback)
.Run(aggregation_service::TestHpkeKey("id123").GetPublicKey(),
PublicKeyFetchStatus::kOk);
}
histograms.ExpectBucketCount(
kReportAssemblerStatusHistogramName,
AggregatableReportAssembler::AssemblyStatus::kOk,
AggregatableReportAssembler::kMaxSimultaneousRequests);
histograms.ExpectBucketCount(
kReportAssemblerStatusHistogramName,
AggregatableReportAssembler::AssemblyStatus::kTooManySimultaneousRequests,
1);
}
} // namespace content