blob: 705c015517697f61a37a85d879b780260ae44e61 [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 "google_apis/youtube_music/youtube_music_api_requests.h"
#include <string>
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "google_apis/common/dummy_auth_service.h"
#include "google_apis/common/request_sender.h"
#include "net/http/http_status_code.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/test/test_shared_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace google_apis::youtube_music {
namespace {
using ::testing::Eq;
using ::testing::Field;
using ::testing::NotNull;
using ::testing::Optional;
constexpr char kTestJson[] = R"(
{
"error": {
"code": 400,
"message": "Invalid Request.",
"status": "INVALID_REQUEST",
"details": [
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "UPDATE_REQUIRED",
"domain": "googleapis.com",
"metadata": {
"method": "google.youtube.mediaconnect.v1.TrackService.DownloadTrack",
"service": "youtubemediaconnect.googleapis.com",
"mediaConnectError": "UPDATE_REQUIRED"
}
},
{
"@type": "type.googleapis.com/google.rpc.LocalizedMessage",
"locale": "en-US",
"message": "Upgrade to a newer version of YouTube Music."
}
]
}
})";
constexpr char kPlaybackResponse[] = R"(
{
"playbackReportingToken": "IamAToken"
})";
// Returns an arbitrary but valid payload object.
std::unique_ptr<ReportPlaybackRequestPayload> TestPlaybackPayload() {
const std::vector<ReportPlaybackRequestPayload::WatchTimeSegment>
watch_time_segments;
const std::string reporting_token = "reportingToken";
ReportPlaybackRequestPayload::Params params(
/*initial_report=*/true,
/*playback_reporting_token=*/reporting_token,
/*client_time=*/base::Time(),
/*playback_start_offset=*/base::TimeDelta(),
/*media_time_current=*/base::TimeDelta(),
/*connection_type=*/ReportPlaybackRequestPayload::ConnectionType::kWired,
/*playback_state=*/ReportPlaybackRequestPayload::PlaybackState::kPlaying,
/*watch_time_segments=*/watch_time_segments);
return std::make_unique<ReportPlaybackRequestPayload>(params);
}
class YoutubeMusicApiRequestsTest : public testing::Test {
public:
YoutubeMusicApiRequestsTest()
: test_shared_loader_factory_(
base::MakeRefCounted<network::TestSharedURLLoaderFactory>(
/*network_service=*/nullptr,
/*is_trusted=*/true)) {}
void SetUp() override {
request_sender_ = std::make_unique<RequestSender>(
std::make_unique<DummyAuthService>(), test_shared_loader_factory_,
task_environment_.GetMainThreadTaskRunner(), "test-user-agent",
TRAFFIC_ANNOTATION_FOR_TESTS);
test_server_.RegisterRequestHandler(base::BindRepeating(
&YoutubeMusicApiRequestsTest::HandleRequest, base::Unretained(this)));
ASSERT_TRUE(test_server_.Start());
}
RequestSender* request_sender() { return request_sender_.get(); }
void set_response(net::HttpStatusCode code, std::string response) {
response_code_ = code;
response_content_ = std::move(response);
}
const GURL& TestServerUrl() const { return test_server_.base_url(); }
private:
std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
const net::test_server::HttpRequest& request) {
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_code(response_code_);
response->set_content(response_content_);
response->set_content_type("application/json");
return response;
}
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::MainThreadType::IO};
net::EmbeddedTestServer test_server_;
std::unique_ptr<RequestSender> request_sender_;
scoped_refptr<network::TestSharedURLLoaderFactory>
test_shared_loader_factory_;
net::test_server::HttpRequest last_request_;
net::HttpStatusCode response_code_;
std::string response_content_;
};
TEST_F(YoutubeMusicApiRequestsTest, ReportPlaybackResult) {
base::RunLoop loop;
set_response(net::HttpStatusCode::HTTP_OK, kPlaybackResponse);
std::unique_ptr<google_apis::youtube_music::ReportPlaybackResult> response;
auto request = std::make_unique<
google_apis::youtube_music::ReportPlaybackRequest>(
request_sender(), TestPlaybackPayload(),
base::BindLambdaForTesting(
[&](base::expected<
std::unique_ptr<google_apis::youtube_music::ReportPlaybackResult>,
google_apis::youtube_music::ApiError> result) {
response = std::move(result.value());
loop.Quit();
}));
set_response(net::HttpStatusCode::HTTP_OK, kPlaybackResponse);
request->SetBaseUrlForTesting(TestServerUrl());
// Add a signing header placeholder.
request->SetSigningHeaders({"Device-Info: device-info"});
request_sender()->StartRequestWithAuthRetry(std::move(request));
loop.Run();
EXPECT_THAT(response, NotNull());
}
TEST_F(YoutubeMusicApiRequestsTest, ClassifyUpdateRequiredError) {
set_response(net::HttpStatusCode::HTTP_BAD_REQUEST, kTestJson);
base::RunLoop loop;
std::optional<google_apis::youtube_music::ApiError> error;
std::unique_ptr<google_apis::youtube_music::ReportPlaybackResult> response;
auto request = std::make_unique<
google_apis::youtube_music::ReportPlaybackRequest>(
request_sender(), TestPlaybackPayload(),
base::BindLambdaForTesting(
[&](base::expected<
std::unique_ptr<google_apis::youtube_music::ReportPlaybackResult>,
google_apis::youtube_music::ApiError> result) {
ASSERT_FALSE(result.has_value());
error = result.error();
loop.Quit();
}));
request->SetBaseUrlForTesting(TestServerUrl());
// Add a signing header placeholder.
request->SetSigningHeaders({"Device-Info: device-info"});
request_sender()->StartRequestWithAuthRetry(std::move(request));
loop.Run();
EXPECT_THAT(error,
Optional(Field(&google_apis::youtube_music::ApiError::error_code,
Eq(YOUTUBE_MUSIC_UPDATE_REQUIRED))));
}
} // namespace
} // namespace google_apis::youtube_music