chromium/chrome/browser/ash/app_list/search/arc/arc_recommend_apps_fetcher_impl_unittest.cc

// 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 "chrome/browser/ash/app_list/search/arc/recommend_apps_fetcher_impl.h"

#include <memory>

#include "base/run_loop.h"
#include "base/values.h"
#include "chrome/browser/ash/app_list/search/arc/fake_recommend_apps_fetcher_delegate.h"
#include "content/public/test/browser_task_environment.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace app_list::test {

class AppListRecommendAppsFetcherImplTest : public testing::Test {
 public:
  AppListRecommendAppsFetcherImplTest() = default;
  ~AppListRecommendAppsFetcherImplTest() override = default;

  void SetUp() override {
    test_url_loader_factory_.SetInterceptor(base::BindRepeating(
        &AppListRecommendAppsFetcherImplTest::InterceptRequest,
        base::Unretained(this)));

    std::unique_ptr<RecommendAppsFetcherImpl> temp =
        std::make_unique<RecommendAppsFetcherImpl>(&delegate_,
                                                   &test_url_loader_factory_);
    temp->SetAndroidIdStatusForTesting(true);
    recommend_apps_fetcher_ = std::move(temp);
  }

 protected:
  network::ResourceRequest* WaitForAppListRequest() {
    if (test_url_loader_factory_.pending_requests()->size() == 0) {
      request_waiter_ = std::make_unique<base::RunLoop>();
      request_waiter_->Run();
      request_waiter_.reset();
    }
    return &test_url_loader_factory_.GetPendingRequest(0)->request;
  }

  FakeRecommendAppsFetcherDelegate delegate_;
  network::TestURLLoaderFactory test_url_loader_factory_;
  std::unique_ptr<RecommendAppsFetcher> recommend_apps_fetcher_;

 private:
  void InterceptRequest(const network::ResourceRequest& request) {
    ASSERT_EQ(
        "https://android.clients.google.com/fdfe/chrome/"
        "getfastreinstallappslist?cfecu=false",
        request.url.spec());
    if (request_waiter_)
      request_waiter_->Quit();
  }

  content::BrowserTaskEnvironment task_environment_;
  std::unique_ptr<base::RunLoop> request_waiter_;
};

TEST_F(AppListRecommendAppsFetcherImplTest, EmptyResponse) {
  ASSERT_TRUE(recommend_apps_fetcher_);

  recommend_apps_fetcher_->StartDownload();

  network::ResourceRequest* request = WaitForAppListRequest();
  ASSERT_TRUE(request);
  test_url_loader_factory_.AddResponse(request->url.spec(), "");

  EXPECT_EQ(FakeRecommendAppsFetcherDelegate::Result::LOAD_ERROR,
            delegate_.WaitForResult());
}

TEST_F(AppListRecommendAppsFetcherImplTest, EmptyAppList) {
  ASSERT_TRUE(recommend_apps_fetcher_);

  recommend_apps_fetcher_->StartDownload();

  network::ResourceRequest* request = WaitForAppListRequest();
  ASSERT_TRUE(request);
  test_url_loader_factory_.AddResponse(request->url.spec(), "[]");

  EXPECT_EQ(FakeRecommendAppsFetcherDelegate::Result::PARSE_ERROR,
            delegate_.WaitForResult());
}

TEST_F(AppListRecommendAppsFetcherImplTest, ResponseWithLeadeingBrackets) {
  ASSERT_TRUE(recommend_apps_fetcher_);

  recommend_apps_fetcher_->StartDownload();

  network::ResourceRequest* request = WaitForAppListRequest();
  ASSERT_TRUE(request);

  const std::string response =
      R"()]}'[{
           "title_": {"name_": "Test app 1"},
           "id_": {"id_": "test.app1"},
           "icon_": {
             "url_": {
               "privateDoNotAccessOrElseSafeUrlWrappedValue_": "http://test.app"
              }
            }
         }])";

  test_url_loader_factory_.AddResponse(request->url.spec(), response);

  EXPECT_EQ(FakeRecommendAppsFetcherDelegate::Result::SUCCESS,
            delegate_.WaitForResult());
  base::Value::List expected_apps;
  base::Value::Dict app;
  app.Set("name", base::Value("Test app 1"));
  app.Set("icon", base::Value("http://test.app"));
  app.Set("package_name", base::Value("test.app1"));
  expected_apps.Append(std::move(app));

  EXPECT_EQ(expected_apps, delegate_.loaded_apps());
}

TEST_F(AppListRecommendAppsFetcherImplTest, MalformedJsonResponse) {
  ASSERT_TRUE(recommend_apps_fetcher_);

  recommend_apps_fetcher_->StartDownload();

  network::ResourceRequest* request = WaitForAppListRequest();
  ASSERT_TRUE(request);

  test_url_loader_factory_.AddResponse(request->url.spec(), ")}]'!2%^$");

  EXPECT_EQ(FakeRecommendAppsFetcherDelegate::Result::PARSE_ERROR,
            delegate_.WaitForResult());
}

TEST_F(AppListRecommendAppsFetcherImplTest, UnexpectedResponseType) {
  ASSERT_TRUE(recommend_apps_fetcher_);

  recommend_apps_fetcher_->StartDownload();

  network::ResourceRequest* request = WaitForAppListRequest();
  ASSERT_TRUE(request);

  test_url_loader_factory_.AddResponse(request->url.spec(), "\"abcd\"");

  EXPECT_EQ(FakeRecommendAppsFetcherDelegate::Result::PARSE_ERROR,
            delegate_.WaitForResult());
}

TEST_F(AppListRecommendAppsFetcherImplTest, ResponseWithMultipleApps) {
  ASSERT_TRUE(recommend_apps_fetcher_);

  recommend_apps_fetcher_->StartDownload();

  network::ResourceRequest* request = WaitForAppListRequest();
  ASSERT_TRUE(request);

  const std::string response =
      R"([{
           "title_": {"name_": "Test app 1"},
           "id_": {"id_": "test.app1"},
           "icon_": {
             "url_": {
               "privateDoNotAccessOrElseSafeUrlWrappedValue_": "http://test.app"
              }
            }
         }, {
           "id_": {"id_": "test.app2"}
         }])";

  test_url_loader_factory_.AddResponse(request->url.spec(), response);

  EXPECT_EQ(FakeRecommendAppsFetcherDelegate::Result::SUCCESS,
            delegate_.WaitForResult());
  base::Value::List expected_apps;
  base::Value::Dict app1;
  app1.Set("name", base::Value("Test app 1"));
  app1.Set("icon", base::Value("http://test.app"));
  app1.Set("package_name", base::Value("test.app1"));
  expected_apps.Append(std::move(app1));

  base::Value::Dict app2;
  app2.Set("package_name", base::Value("test.app2"));
  expected_apps.Append(std::move(app2));

  EXPECT_EQ(expected_apps, delegate_.loaded_apps());
}

TEST_F(AppListRecommendAppsFetcherImplTest, InvalidAppItemsIgnored) {
  ASSERT_TRUE(recommend_apps_fetcher_);

  recommend_apps_fetcher_->StartDownload();

  network::ResourceRequest* request = WaitForAppListRequest();
  ASSERT_TRUE(request);

  const std::string response =
      R"([{
           "title_": {"name_": "Test app 1"},
           "id_": {"id_": "test.app1"},
           "icon_": {
             "url_": {
               "privateDoNotAccessOrElseSafeUrlWrappedValue_": "http://test.app"
              }
            }
         }, [], 2, {"id_": {"id_": "test.app2"}}, {"a": "b"}])";

  test_url_loader_factory_.AddResponse(request->url.spec(), response);

  EXPECT_EQ(FakeRecommendAppsFetcherDelegate::Result::SUCCESS,
            delegate_.WaitForResult());
  base::Value::List expected_apps;
  base::Value::Dict app1;
  app1.Set("name", base::Value("Test app 1"));
  app1.Set("icon", base::Value("http://test.app"));
  app1.Set("package_name", base::Value("test.app1"));
  expected_apps.Append(std::move(app1));

  base::Value::Dict app2;
  app2.Set("package_name", base::Value("test.app2"));
  expected_apps.Append(std::move(app2));

  EXPECT_EQ(expected_apps, delegate_.loaded_apps());
}

TEST_F(AppListRecommendAppsFetcherImplTest, DictionaryResponse) {
  ASSERT_TRUE(recommend_apps_fetcher_);

  recommend_apps_fetcher_->StartDownload();

  network::ResourceRequest* request = WaitForAppListRequest();
  ASSERT_TRUE(request);

  test_url_loader_factory_.AddResponse(request->url.spec(), "{}");

  EXPECT_EQ(FakeRecommendAppsFetcherDelegate::Result::PARSE_ERROR,
            delegate_.WaitForResult());
}

TEST_F(AppListRecommendAppsFetcherImplTest, InvalidErrorCodeType) {
  ASSERT_TRUE(recommend_apps_fetcher_);

  recommend_apps_fetcher_->StartDownload();

  network::ResourceRequest* request = WaitForAppListRequest();
  ASSERT_TRUE(request);

  test_url_loader_factory_.AddResponse(request->url.spec(),
                                       R"({"Error code": ""})");

  EXPECT_EQ(FakeRecommendAppsFetcherDelegate::Result::PARSE_ERROR,
            delegate_.WaitForResult());
}

TEST_F(AppListRecommendAppsFetcherImplTest, NotEnoughAppsError) {
  ASSERT_TRUE(recommend_apps_fetcher_);

  recommend_apps_fetcher_->StartDownload();

  network::ResourceRequest* request = WaitForAppListRequest();
  ASSERT_TRUE(request);

  test_url_loader_factory_.AddResponse(request->url.spec(),
                                       R"({"Error code": "5"})");

  EXPECT_EQ(FakeRecommendAppsFetcherDelegate::Result::PARSE_ERROR,
            delegate_.WaitForResult());
}

TEST_F(AppListRecommendAppsFetcherImplTest, AppListRequestFailure) {
  ASSERT_TRUE(recommend_apps_fetcher_);

  recommend_apps_fetcher_->StartDownload();

  network::ResourceRequest* request = WaitForAppListRequest();
  ASSERT_TRUE(request);

  test_url_loader_factory_.AddResponse(request->url.spec(), "",
                                       net::HTTP_BAD_REQUEST);

  EXPECT_EQ(FakeRecommendAppsFetcherDelegate::Result::LOAD_ERROR,
            delegate_.WaitForResult());
}

}  // namespace app_list::test