blob: b25fdc13adf7477721bda66c82d8029c189dafeb [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/manta/scanner_provider.h"
#include <memory>
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/protobuf_matchers.h"
#include "components/manta/base_provider.h"
#include "components/manta/base_provider_test_helper.h"
#include "components/manta/manta_status.h"
#include "components/manta/proto/common.pb.h"
#include "components/manta/proto/manta.pb.h"
#include "components/manta/proto/rpc_status.pb.h"
#include "components/manta/proto/scanner.pb.h"
#include "components/manta/provider_params.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "net/base/net_errors.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace manta {
namespace {
using ::base::test::EqualsProto;
using ::testing::Pointee;
constexpr char kMockEndpoint[] = "https://my-endpoint.com";
proto::ScannerInput CreateValidScannerInput() {
proto::ScannerInput scanner_input;
scanner_input.set_image("image");
return scanner_input;
}
proto::ScannerOutput CreateValidScannerOutput() {
// Create a ScannerOutput proto.
proto::ScannerOutput scanner_output;
proto::ScannerObject* scanner_object = scanner_output.add_objects();
proto::ScannerAction* scanner_action = scanner_object->add_actions();
proto::NewContactAction* new_contact_action =
scanner_action->mutable_new_contact();
new_contact_action->set_given_name("Foo");
new_contact_action->set_family_name("Bar");
return scanner_output;
}
std::unique_ptr<proto::Response> CreateValidResponseWithScannerOutput() {
auto manta_response = std::make_unique<proto::Response>();
// Populate a custom data within the Manta response.
proto::OutputData* output_data = manta_response->add_output_data();
proto::Proto3Any& custom_data = *output_data->mutable_custom();
custom_data.set_type_url(kScannerOutputTypeUrl);
auto scanner_output = CreateValidScannerOutput();
custom_data.set_value(scanner_output.SerializeAsString());
return manta_response;
}
}
class FakeScannerProvider : public ScannerProvider, public FakeBaseProvider {
public:
FakeScannerProvider(
scoped_refptr<network::SharedURLLoaderFactory> test_url_loader_factory,
signin::IdentityManager* identity_manager)
: BaseProvider(test_url_loader_factory, identity_manager),
ScannerProvider(test_url_loader_factory,
identity_manager,
ProviderParams()),
FakeBaseProvider(test_url_loader_factory, identity_manager) {}
};
class ScannerProviderTest : public BaseProviderTest {
public:
ScannerProviderTest() = default;
ScannerProviderTest(const ScannerProviderTest&) = delete;
ScannerProviderTest& operator=(const ScannerProviderTest&) = delete;
~ScannerProviderTest() override = default;
std::unique_ptr<FakeScannerProvider> CreateScannerProvider() {
return std::make_unique<FakeScannerProvider>(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_),
identity_test_env_->identity_manager());
}
};
TEST_F(ScannerProviderTest, ParseSuccessfulResponse) {
auto scanner_input = CreateValidScannerInput();
auto manta_response = CreateValidResponseWithScannerOutput();
base::HistogramTester histogram_tester;
std::unique_ptr<FakeScannerProvider> scanner_provider =
CreateScannerProvider();
SetEndpointMockResponse(GURL{kMockEndpoint},
/*response_data=*/manta_response->SerializeAsString(),
net::HTTP_OK, net::OK);
scanner_provider->Call(
scanner_input,
base::BindLambdaForTesting(
[quit_closure = task_environment_.QuitClosure()](
std::unique_ptr<proto::ScannerOutput> scanner_output,
MantaStatus manta_status) {
EXPECT_EQ(manta_status.status_code, MantaStatusCode::kOk);
EXPECT_THAT(scanner_output,
Pointee(EqualsProto(CreateValidScannerOutput())));
quit_closure.Run();
}));
task_environment_.RunUntilQuit();
histogram_tester.ExpectTotalCount("Ash.MantaService.ScannerProvider.TimeCost",
1);
}
TEST_F(ScannerProviderTest, InvalidInputErrorIfNoImageProvided) {
auto scanner_input = CreateValidScannerInput();
scanner_input.clear_image();
std::unique_ptr<FakeScannerProvider> scanner_provider =
CreateScannerProvider();
SetEndpointMockResponse(GURL{kMockEndpoint},
/*response_data=*/"", net::HTTP_OK, net::OK);
scanner_provider->Call(
scanner_input,
base::BindLambdaForTesting(
[quit_closure = task_environment_.QuitClosure()](
std::unique_ptr<proto::ScannerOutput> scanner_output,
MantaStatus manta_status) {
EXPECT_EQ(manta_status.status_code, MantaStatusCode::kInvalidInput);
EXPECT_EQ(scanner_output, nullptr);
quit_closure.Run();
}));
task_environment_.RunUntilQuit();
}
TEST_F(ScannerProviderTest, BlockedOutputsErrorIfNoOutputData) {
auto scanner_input = CreateValidScannerInput();
auto manta_response = CreateValidResponseWithScannerOutput();
manta_response->clear_output_data();
auto* filtered_data = manta_response->add_filtered_data();
filtered_data->set_is_output_data(true);
filtered_data->set_reason(proto::FilteredReason::TEXT_SAFETY);
std::unique_ptr<FakeScannerProvider> scanner_provider =
CreateScannerProvider();
SetEndpointMockResponse(GURL{kMockEndpoint},
/*response_data=*/manta_response->SerializeAsString(),
net::HTTP_OK, net::OK);
scanner_provider->Call(
scanner_input,
base::BindLambdaForTesting(
[quit_closure = task_environment_.QuitClosure()](
std::unique_ptr<proto::ScannerOutput> scanner_output,
MantaStatus manta_status) {
EXPECT_EQ(manta_status.status_code,
MantaStatusCode::kBlockedOutputs);
EXPECT_EQ(manta_status.message, "filtered output for: TEXT_SAFETY");
EXPECT_EQ(scanner_output, nullptr);
quit_closure.Run();
}));
task_environment_.RunUntilQuit();
}
TEST_F(ScannerProviderTest, MalformedResponseErrorIfMoreThanOneOutputData) {
auto scanner_input = CreateValidScannerInput();
auto manta_response = CreateValidResponseWithScannerOutput();
manta_response->add_output_data();
std::unique_ptr<FakeScannerProvider> scanner_provider =
CreateScannerProvider();
SetEndpointMockResponse(GURL{kMockEndpoint},
/*response_data=*/manta_response->SerializeAsString(),
net::HTTP_OK, net::OK);
scanner_provider->Call(
scanner_input,
base::BindLambdaForTesting(
[quit_closure = task_environment_.QuitClosure()](
std::unique_ptr<proto::ScannerOutput> scanner_output,
MantaStatus manta_status) {
EXPECT_EQ(manta_status.status_code,
MantaStatusCode::kMalformedResponse);
EXPECT_EQ(scanner_output, nullptr);
quit_closure.Run();
}));
task_environment_.RunUntilQuit();
}
TEST_F(ScannerProviderTest, MalformedResponseErrorIfNoCustomOutputData) {
auto scanner_input = CreateValidScannerInput();
auto manta_response = CreateValidResponseWithScannerOutput();
// Clear the custom field.
manta_response->mutable_output_data(0)->clear_custom();
std::unique_ptr<FakeScannerProvider> scanner_provider =
CreateScannerProvider();
SetEndpointMockResponse(GURL{kMockEndpoint},
/*response_data=*/manta_response->SerializeAsString(),
net::HTTP_OK, net::OK);
scanner_provider->Call(
scanner_input,
base::BindLambdaForTesting(
[quit_closure = task_environment_.QuitClosure()](
std::unique_ptr<proto::ScannerOutput> scanner_output,
MantaStatus manta_status) {
EXPECT_EQ(manta_status.status_code,
MantaStatusCode::kMalformedResponse);
EXPECT_EQ(scanner_output, nullptr);
quit_closure.Run();
}));
task_environment_.RunUntilQuit();
}
TEST_F(ScannerProviderTest, MalformedResponseErrorIfInvalidProtoTypeUrl) {
auto scanner_input = CreateValidScannerInput();
auto manta_response = CreateValidResponseWithScannerOutput();
// Clear the custom field.
manta_response->mutable_output_data(0)->mutable_custom()->set_type_url("foo");
std::unique_ptr<FakeScannerProvider> scanner_provider =
CreateScannerProvider();
SetEndpointMockResponse(GURL{kMockEndpoint},
/*response_data=*/manta_response->SerializeAsString(),
net::HTTP_OK, net::OK);
scanner_provider->Call(
scanner_input,
base::BindLambdaForTesting(
[quit_closure = task_environment_.QuitClosure()](
std::unique_ptr<proto::ScannerOutput> scanner_output,
MantaStatus manta_status) {
EXPECT_EQ(manta_status.status_code,
MantaStatusCode::kMalformedResponse);
EXPECT_EQ(scanner_output, nullptr);
quit_closure.Run();
}));
task_environment_.RunUntilQuit();
}
// Test that responses with http_status_code != net::HTTP_OK are captured.
TEST_F(ScannerProviderTest, CaptureUnexpectedStatusCode) {
auto scanner_input = CreateValidScannerInput();
std::unique_ptr<FakeScannerProvider> scanner_provider =
CreateScannerProvider();
SetEndpointMockResponse(GURL{kMockEndpoint},
/*response_data=*/"", net::HTTP_BAD_REQUEST, net::OK);
scanner_provider->Call(
scanner_input,
base::BindLambdaForTesting(
[quit_closure = task_environment_.QuitClosure()](
std::unique_ptr<proto::ScannerOutput> scanner_output,
MantaStatus manta_status) {
EXPECT_EQ(manta_status.status_code,
MantaStatusCode::kBackendFailure);
EXPECT_EQ(scanner_output, nullptr);
quit_closure.Run();
}));
task_environment_.RunUntilQuit();
}
TEST_F(ScannerProviderTest, CaptureNetError) {
auto scanner_input = CreateValidScannerInput();
std::unique_ptr<FakeScannerProvider> scanner_provider =
CreateScannerProvider();
SetEndpointMockResponse(GURL{kMockEndpoint}, /*response_data=*/"",
net::HTTP_OK, net::ERR_FAILED);
scanner_provider->Call(
scanner_input,
base::BindLambdaForTesting(
[quit_closure = task_environment_.QuitClosure()](
std::unique_ptr<proto::ScannerOutput> scanner_output,
MantaStatus manta_status) {
EXPECT_EQ(manta_status.status_code,
MantaStatusCode::kNoInternetConnection);
EXPECT_EQ(scanner_output, nullptr);
quit_closure.Run();
}));
task_environment_.RunUntilQuit();
}
// Test that an unexpected response with a serialized RpcStatus proto can be
// handled properly.
TEST_F(ScannerProviderTest, ParseRpcStatusFromFailedResponse) {
auto scanner_input = CreateValidScannerInput();
proto::RpcStatus rpc_status;
rpc_status.set_code(3); // Will be mapped to kInvalidInput.
rpc_status.set_message("foo");
proto::RpcLocalizedMessage localize_message;
localize_message.set_message("bar");
localize_message.set_locale("en");
auto* detail = rpc_status.add_details();
detail->set_type_url("type.googleapis.com/google.rpc.LocalizedMessage");
detail->set_value(localize_message.SerializeAsString());
std::unique_ptr<FakeScannerProvider> scanner_provider =
CreateScannerProvider();
SetEndpointMockResponse(GURL{kMockEndpoint},
/*response_data=*/rpc_status.SerializeAsString(),
net::HTTP_BAD_REQUEST, net::OK);
scanner_provider->Call(
scanner_input,
base::BindLambdaForTesting(
[quit_closure = task_environment_.QuitClosure()](
std::unique_ptr<proto::ScannerOutput> scanner_output,
MantaStatus manta_status) {
EXPECT_EQ(manta_status.status_code, MantaStatusCode::kInvalidInput);
EXPECT_EQ(manta_status.message, "bar");
EXPECT_EQ(manta_status.locale, "en");
EXPECT_EQ(scanner_output, nullptr);
quit_closure.Run();
}));
task_environment_.RunUntilQuit();
// ErrorInfo.reason can be mapped to kRestrictedCountry and override the manta
// status code.
proto::RpcErrorInfo error_info;
error_info.set_reason("RESTRICTED_COUNTRY");
error_info.set_domain("aratea-pa.googleapis.com");
detail = rpc_status.add_details();
detail->set_type_url("type.googleapis.com/google.rpc.ErrorInfo");
detail->set_value(error_info.SerializeAsString());
SetEndpointMockResponse(GURL{kMockEndpoint},
/*response_data=*/rpc_status.SerializeAsString(),
net::HTTP_BAD_REQUEST, net::OK);
scanner_provider->Call(
scanner_input,
base::BindLambdaForTesting(
[quit_closure = task_environment_.QuitClosure()](
std::unique_ptr<proto::ScannerOutput> scanner_output,
MantaStatus manta_status) {
EXPECT_EQ(manta_status.status_code,
MantaStatusCode::kRestrictedCountry);
EXPECT_EQ(manta_status.message, "bar");
EXPECT_EQ(manta_status.locale, "en");
EXPECT_EQ(scanner_output, nullptr);
quit_closure.Run();
}));
task_environment_.RunUntilQuit();
}
TEST_F(ScannerProviderTest, EmptyResponseAfterIdentityManagerShutdown) {
auto scanner_input = CreateValidScannerInput();
std::unique_ptr<FakeScannerProvider> scanner_provider =
CreateScannerProvider();
identity_test_env_.reset();
scanner_provider->Call(
scanner_input,
base::BindLambdaForTesting(
[quit_closure = task_environment_.QuitClosure()](
std::unique_ptr<proto::ScannerOutput> scanner_output,
MantaStatus manta_status) {
ASSERT_EQ(MantaStatusCode::kNoIdentityManager,
manta_status.status_code);
EXPECT_EQ(scanner_output, nullptr);
quit_closure.Run();
}));
task_environment_.RunUntilQuit();
}
} // namespace manta