json_request_unittest.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. // Copyright 2016 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/ntp_snippets/remote/json_request.h"
  5. #include <set>
  6. #include <utility>
  7. #include "base/json/json_reader.h"
  8. #include "base/strings/stringprintf.h"
  9. #include "base/test/scoped_feature_list.h"
  10. #include "base/test/test_mock_time_task_runner.h"
  11. #include "base/threading/thread_task_runner_handle.h"
  12. #include "base/time/tick_clock.h"
  13. #include "base/time/time.h"
  14. #include "base/values.h"
  15. #include "components/ntp_snippets/features.h"
  16. #include "components/ntp_snippets/ntp_snippets_constants.h"
  17. #include "components/ntp_snippets/remote/request_params.h"
  18. #include "components/prefs/testing_pref_service.h"
  19. #include "components/variations/scoped_variations_ids_provider.h"
  20. #include "services/network/public/cpp/shared_url_loader_factory.h"
  21. #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
  22. #include "services/network/test/test_url_loader_factory.h"
  23. #include "testing/gmock/include/gmock/gmock.h"
  24. #include "testing/gtest/include/gtest/gtest.h"
  25. // TODO(crbug.com/961023): Fix memory leaks in tests and re-enable on LSAN.
  26. #ifdef LEAK_SANITIZER
  27. #define MAYBE_BuildRequestAuthenticated DISABLED_BuildRequestAuthenticated
  28. #else
  29. #define MAYBE_BuildRequestAuthenticated BuildRequestAuthenticated
  30. #endif
  31. namespace ntp_snippets {
  32. namespace internal {
  33. namespace {
  34. using testing::_;
  35. using testing::Eq;
  36. using testing::Not;
  37. using testing::NotNull;
  38. using testing::StrEq;
  39. MATCHER_P(EqualsJSON, json, "equals JSON") {
  40. absl::optional<base::Value> expected = base::JSONReader::Read(json);
  41. if (!expected) {
  42. *result_listener << "INTERNAL ERROR: couldn't parse expected JSON";
  43. return false;
  44. }
  45. auto actual = base::JSONReader::ReadAndReturnValueWithError(arg);
  46. if (!actual.has_value()) {
  47. *result_listener << "input:" << actual.error().line << ":"
  48. << actual.error().column << ": "
  49. << "parse error: " << actual.error().message;
  50. return false;
  51. }
  52. return *expected == *actual;
  53. }
  54. } // namespace
  55. class JsonRequestTest : public testing::Test {
  56. public:
  57. JsonRequestTest()
  58. : pref_service_(std::make_unique<TestingPrefServiceSimple>()),
  59. mock_task_runner_(new base::TestMockTimeTaskRunner()),
  60. mock_runner_handle_(
  61. std::make_unique<base::ThreadTaskRunnerHandle>(mock_task_runner_)),
  62. test_shared_loader_factory_(
  63. base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
  64. &test_url_loader_factory_)) {
  65. scoped_feature_list_.InitAndEnableFeatureWithParameters(
  66. kArticleSuggestionsFeature,
  67. {{"send_top_languages", "true"}, {"send_user_class", "true"}});
  68. language::UrlLanguageHistogram::RegisterProfilePrefs(
  69. pref_service_->registry());
  70. }
  71. JsonRequestTest(const JsonRequestTest&) = delete;
  72. JsonRequestTest& operator=(const JsonRequestTest&) = delete;
  73. std::unique_ptr<language::UrlLanguageHistogram> MakeLanguageHistogram(
  74. const std::set<std::string>& codes) {
  75. std::unique_ptr<language::UrlLanguageHistogram> language_histogram =
  76. std::make_unique<language::UrlLanguageHistogram>(pref_service_.get());
  77. // There must be at least 10 visits before the top languages are defined.
  78. for (int i = 0; i < 10; i++) {
  79. for (const std::string& code : codes) {
  80. language_histogram->OnPageVisited(code);
  81. }
  82. }
  83. return language_histogram;
  84. }
  85. JsonRequest::Builder CreateMinimalBuilder() {
  86. JsonRequest::Builder builder;
  87. builder.SetUrl(GURL("http://valid-url.test"))
  88. .SetClock(mock_task_runner_->GetMockClock())
  89. .SetUrlLoaderFactory(test_shared_loader_factory_);
  90. return builder;
  91. }
  92. std::unique_ptr<base::test::ScopedFeatureList> ForceOptionalImagesSupport(
  93. bool supported) {
  94. auto feature_list = std::make_unique<base::test::ScopedFeatureList>();
  95. if (supported) {
  96. feature_list->InitWithFeatures({kOptionalImagesEnabledFeature}, {});
  97. } else {
  98. feature_list->InitWithFeatures({}, {kOptionalImagesEnabledFeature});
  99. }
  100. return feature_list;
  101. }
  102. private:
  103. base::test::ScopedFeatureList scoped_feature_list_;
  104. variations::ScopedVariationsIdsProvider scoped_variations_ids_provider_{
  105. variations::VariationsIdsProvider::Mode::kUseSignedInState};
  106. std::unique_ptr<TestingPrefServiceSimple> pref_service_;
  107. scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_;
  108. std::unique_ptr<base::ThreadTaskRunnerHandle> mock_runner_handle_;
  109. network::TestURLLoaderFactory test_url_loader_factory_;
  110. scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
  111. };
  112. TEST_F(JsonRequestTest, MAYBE_BuildRequestAuthenticated) {
  113. JsonRequest::Builder builder = CreateMinimalBuilder();
  114. RequestParams params;
  115. params.excluded_ids = {"1234567890"};
  116. params.count_to_fetch = 25;
  117. params.interactive_request = false;
  118. builder.SetParams(params)
  119. .SetUrl(GURL("http://valid-url.test"))
  120. .SetAuthentication("headerstuff")
  121. .SetUserClassForTesting("ACTIVE_NTP_USER")
  122. .Build();
  123. EXPECT_THAT(builder.PreviewRequestHeadersForTesting(),
  124. StrEq("Content-Type: application/json; charset=UTF-8\r\n"
  125. "Authorization: headerstuff\r\n"
  126. "\r\n"));
  127. EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
  128. EqualsJSON("{"
  129. " \"priority\": \"BACKGROUND_PREFETCH\","
  130. " \"excludedSuggestionIds\": ["
  131. " \"1234567890\""
  132. " ],"
  133. " \"userActivenessClass\": \"ACTIVE_NTP_USER\""
  134. "}"));
  135. }
  136. TEST_F(JsonRequestTest, BuildRequestUnauthenticated) {
  137. JsonRequest::Builder builder;
  138. RequestParams params;
  139. params.interactive_request = true;
  140. params.count_to_fetch = 10;
  141. builder.SetParams(params).SetUserClassForTesting("ACTIVE_NTP_USER");
  142. EXPECT_THAT(builder.PreviewRequestHeadersForTesting(),
  143. StrEq("Content-Type: application/json; charset=UTF-8\r\n"
  144. "\r\n"));
  145. EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
  146. EqualsJSON("{"
  147. " \"priority\": \"USER_ACTION\","
  148. " \"excludedSuggestionIds\": [],"
  149. " \"userActivenessClass\": \"ACTIVE_NTP_USER\""
  150. "}"));
  151. }
  152. TEST_F(JsonRequestTest, BuildRequestDisplayCapabilityDisabledByFeature) {
  153. auto optional_images_feature_list = ForceOptionalImagesSupport(false);
  154. JsonRequest::Builder builder;
  155. builder.SetOptionalImagesCapability(true);
  156. EXPECT_THAT(builder.PreviewRequestHeadersForTesting(),
  157. StrEq("Content-Type: application/json; charset=UTF-8\r\n"
  158. "\r\n"));
  159. // The JSON should not contain any mention of displayCapability.
  160. EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
  161. EqualsJSON("{"
  162. " \"excludedSuggestionIds\": [],"
  163. " \"priority\": \"BACKGROUND_PREFETCH\""
  164. "}"));
  165. }
  166. TEST_F(JsonRequestTest, BuildRequestDisplayCapabilityUnspecified) {
  167. auto optional_images_feature_list = ForceOptionalImagesSupport(true);
  168. JsonRequest::Builder builder;
  169. builder.SetOptionalImagesCapability(false);
  170. EXPECT_THAT(builder.PreviewRequestHeadersForTesting(),
  171. StrEq("Content-Type: application/json; charset=UTF-8\r\n"
  172. "\r\n"));
  173. EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
  174. EqualsJSON("{"
  175. " \"excludedSuggestionIds\": [],"
  176. " \"priority\": \"BACKGROUND_PREFETCH\""
  177. "}"));
  178. }
  179. TEST_F(JsonRequestTest, BuildRequestOptionalImages) {
  180. auto optional_images_feature_list = ForceOptionalImagesSupport(true);
  181. JsonRequest::Builder builder;
  182. builder.SetOptionalImagesCapability(true);
  183. EXPECT_THAT(builder.PreviewRequestHeadersForTesting(),
  184. StrEq("Content-Type: application/json; charset=UTF-8\r\n"
  185. "\r\n"));
  186. EXPECT_THAT(
  187. builder.PreviewRequestBodyForTesting(),
  188. EqualsJSON("{"
  189. " \"displayCapability\": \"CAPABILITY_OPTIONAL_IMAGES\","
  190. " \"excludedSuggestionIds\": [],"
  191. " \"priority\": \"BACKGROUND_PREFETCH\""
  192. "}"));
  193. }
  194. TEST_F(JsonRequestTest, ShouldNotTruncateExcludedIdsList) {
  195. JsonRequest::Builder builder;
  196. RequestParams params;
  197. params.interactive_request = false;
  198. for (int i = 0; i < 200; ++i) {
  199. params.excluded_ids.insert(base::StringPrintf("%03d", i));
  200. }
  201. builder.SetParams(params).SetUserClassForTesting("ACTIVE_NTP_USER");
  202. EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
  203. EqualsJSON("{"
  204. " \"priority\": \"BACKGROUND_PREFETCH\","
  205. " \"excludedSuggestionIds\": ["
  206. " \"000\", \"001\", \"002\", \"003\", \"004\","
  207. " \"005\", \"006\", \"007\", \"008\", \"009\","
  208. " \"010\", \"011\", \"012\", \"013\", \"014\","
  209. " \"015\", \"016\", \"017\", \"018\", \"019\","
  210. " \"020\", \"021\", \"022\", \"023\", \"024\","
  211. " \"025\", \"026\", \"027\", \"028\", \"029\","
  212. " \"030\", \"031\", \"032\", \"033\", \"034\","
  213. " \"035\", \"036\", \"037\", \"038\", \"039\","
  214. " \"040\", \"041\", \"042\", \"043\", \"044\","
  215. " \"045\", \"046\", \"047\", \"048\", \"049\","
  216. " \"050\", \"051\", \"052\", \"053\", \"054\","
  217. " \"055\", \"056\", \"057\", \"058\", \"059\","
  218. " \"060\", \"061\", \"062\", \"063\", \"064\","
  219. " \"065\", \"066\", \"067\", \"068\", \"069\","
  220. " \"070\", \"071\", \"072\", \"073\", \"074\","
  221. " \"075\", \"076\", \"077\", \"078\", \"079\","
  222. " \"080\", \"081\", \"082\", \"083\", \"084\","
  223. " \"085\", \"086\", \"087\", \"088\", \"089\","
  224. " \"090\", \"091\", \"092\", \"093\", \"094\","
  225. " \"095\", \"096\", \"097\", \"098\", \"099\","
  226. " \"100\", \"101\", \"102\", \"103\", \"104\","
  227. " \"105\", \"106\", \"107\", \"108\", \"109\","
  228. " \"110\", \"111\", \"112\", \"113\", \"114\","
  229. " \"115\", \"116\", \"117\", \"118\", \"119\","
  230. " \"120\", \"121\", \"122\", \"123\", \"124\","
  231. " \"125\", \"126\", \"127\", \"128\", \"129\","
  232. " \"130\", \"131\", \"132\", \"133\", \"134\","
  233. " \"135\", \"136\", \"137\", \"138\", \"139\","
  234. " \"140\", \"141\", \"142\", \"143\", \"144\","
  235. " \"145\", \"146\", \"147\", \"148\", \"149\","
  236. " \"150\", \"151\", \"152\", \"153\", \"154\","
  237. " \"155\", \"156\", \"157\", \"158\", \"159\","
  238. " \"160\", \"161\", \"162\", \"163\", \"164\","
  239. " \"165\", \"166\", \"167\", \"168\", \"169\","
  240. " \"170\", \"171\", \"172\", \"173\", \"174\","
  241. " \"175\", \"176\", \"177\", \"178\", \"179\","
  242. " \"180\", \"181\", \"182\", \"183\", \"184\","
  243. " \"185\", \"186\", \"187\", \"188\", \"189\","
  244. " \"190\", \"191\", \"192\", \"193\", \"194\","
  245. " \"195\", \"196\", \"197\", \"198\", \"199\""
  246. " ],"
  247. " \"userActivenessClass\": \"ACTIVE_NTP_USER\""
  248. "}"));
  249. }
  250. TEST_F(JsonRequestTest, BuildRequestNoUserClass) {
  251. JsonRequest::Builder builder;
  252. RequestParams params;
  253. params.interactive_request = false;
  254. builder.SetParams(params);
  255. EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
  256. EqualsJSON("{"
  257. " \"priority\": \"BACKGROUND_PREFETCH\","
  258. " \"excludedSuggestionIds\": []"
  259. "}"));
  260. }
  261. TEST_F(JsonRequestTest, BuildRequestWithTwoLanguages) {
  262. JsonRequest::Builder builder;
  263. std::unique_ptr<language::UrlLanguageHistogram> language_histogram =
  264. MakeLanguageHistogram({"de", "en"});
  265. RequestParams params;
  266. params.interactive_request = true;
  267. params.language_code = "en";
  268. builder.SetParams(params).SetLanguageHistogram(language_histogram.get());
  269. EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
  270. EqualsJSON("{"
  271. " \"priority\": \"USER_ACTION\","
  272. " \"uiLanguage\": \"en\","
  273. " \"excludedSuggestionIds\": [],"
  274. " \"topLanguages\": ["
  275. " {"
  276. " \"language\" : \"en\","
  277. " \"frequency\" : 0.5"
  278. " },"
  279. " {"
  280. " \"language\" : \"de\","
  281. " \"frequency\" : 0.5"
  282. " }"
  283. " ]"
  284. "}"));
  285. }
  286. TEST_F(JsonRequestTest, BuildRequestWithUILanguageOnly) {
  287. JsonRequest::Builder builder;
  288. std::unique_ptr<language::UrlLanguageHistogram> language_histogram =
  289. MakeLanguageHistogram({"en"});
  290. RequestParams params;
  291. params.interactive_request = true;
  292. params.language_code = "en";
  293. builder.SetParams(params).SetLanguageHistogram(language_histogram.get());
  294. EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
  295. EqualsJSON("{"
  296. " \"priority\": \"USER_ACTION\","
  297. " \"uiLanguage\": \"en\","
  298. " \"excludedSuggestionIds\": [],"
  299. " \"topLanguages\": [{"
  300. " \"language\" : \"en\","
  301. " \"frequency\" : 1.0"
  302. " }]"
  303. "}"));
  304. }
  305. TEST_F(JsonRequestTest,
  306. ShouldPropagateCountToFetchWhenExclusiveCategoryPresent) {
  307. JsonRequest::Builder builder;
  308. RequestParams params;
  309. params.interactive_request = true;
  310. params.language_code = "en";
  311. params.exclusive_category =
  312. Category::FromKnownCategory(KnownCategories::ARTICLES);
  313. params.count_to_fetch = 25;
  314. builder.SetParams(params);
  315. EXPECT_THAT(builder.PreviewRequestBodyForTesting(), EqualsJSON(R"(
  316. {
  317. "priority": "USER_ACTION",
  318. "uiLanguage": "en",
  319. "excludedSuggestionIds": [],
  320. "categoryParameters": [{
  321. "id": 1,
  322. "numSuggestions": 25
  323. }]
  324. }
  325. )"));
  326. }
  327. // TODO(vitaliii): Propagate count to fetch in this case as well and delete this
  328. // test. Currently the server does not support this.
  329. TEST_F(JsonRequestTest,
  330. ShouldNotPropagateCountToFetchWhenExclusiveCategoryNotPresent) {
  331. JsonRequest::Builder builder;
  332. RequestParams params;
  333. params.interactive_request = true;
  334. params.language_code = "en";
  335. params.count_to_fetch = 10;
  336. builder.SetParams(params);
  337. EXPECT_THAT(builder.PreviewRequestBodyForTesting(), EqualsJSON(R"(
  338. {
  339. "priority": "USER_ACTION",
  340. "uiLanguage": "en",
  341. "excludedSuggestionIds": []
  342. }
  343. )"));
  344. }
  345. } // namespace internal
  346. } // namespace ntp_snippets