blob: 85d2d1fa5324fa9a6a012b636d54046f94130e31 [file] [log] [blame]
fhorschigcb5d7fc02016-12-20 13:14:551// 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
treib9de525a2017-01-19 12:20:375#include "components/ntp_snippets/remote/json_request.h"
fhorschigcb5d7fc02016-12-20 13:14:556
7#include <set>
8#include <utility>
9
10#include "base/json/json_reader.h"
fhorschigcb5d7fc02016-12-20 13:14:5511#include "base/strings/stringprintf.h"
Alexei Svitkine1d820ef2019-06-20 20:12:3212#include "base/test/scoped_feature_list.h"
fhorschigcb5d7fc02016-12-20 13:14:5513#include "base/test/test_mock_time_task_runner.h"
Takashi Toyoshimaf3ceca92019-02-04 07:49:0514#include "base/threading/thread_task_runner_handle.h"
fhorschigcb5d7fc02016-12-20 13:14:5515#include "base/time/tick_clock.h"
16#include "base/time/time.h"
17#include "base/values.h"
18#include "components/ntp_snippets/features.h"
19#include "components/ntp_snippets/ntp_snippets_constants.h"
treib9de525a2017-01-19 12:20:3720#include "components/ntp_snippets/remote/request_params.h"
fhorschigcb5d7fc02016-12-20 13:14:5521#include "components/prefs/testing_pref_service.h"
Mark Pilgrimda33faa2018-06-06 14:29:4722#include "services/network/public/cpp/shared_url_loader_factory.h"
23#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
24#include "services/network/test/test_url_loader_factory.h"
fhorschigcb5d7fc02016-12-20 13:14:5525#include "testing/gmock/include/gmock/gmock.h"
26#include "testing/gtest/include/gtest/gtest.h"
27
Caleb Raittoc0a311ba2019-06-05 00:01:4128// TODO(crbug.com/961023): Fix memory leaks in tests and re-enable on LSAN.
29#ifdef LEAK_SANITIZER
30#define MAYBE_BuildRequestAuthenticated DISABLED_BuildRequestAuthenticated
31#else
32#define MAYBE_BuildRequestAuthenticated BuildRequestAuthenticated
33#endif
34
fhorschigcb5d7fc02016-12-20 13:14:5535namespace ntp_snippets {
36
37namespace internal {
38
39namespace {
40
41using testing::_;
42using testing::Eq;
43using testing::Not;
44using testing::NotNull;
45using testing::StrEq;
46
47MATCHER_P(EqualsJSON, json, "equals JSON") {
Nigel Tao684a1b2f2020-06-03 03:59:3048 base::Optional<base::Value> expected = base::JSONReader::Read(json);
fhorschigcb5d7fc02016-12-20 13:14:5549 if (!expected) {
50 *result_listener << "INTERNAL ERROR: couldn't parse expected JSON";
51 return false;
52 }
53
Nigel Tao684a1b2f2020-06-03 03:59:3054 base::JSONReader::ValueWithError actual =
55 base::JSONReader::ReadAndReturnValueWithError(arg);
56 if (!actual.value) {
57 *result_listener << "input:" << actual.error_line << ":"
58 << actual.error_column << ": "
59 << "parse error: " << actual.error_message;
fhorschigcb5d7fc02016-12-20 13:14:5560 return false;
61 }
Nigel Tao684a1b2f2020-06-03 03:59:3062 return *expected == *actual.value;
fhorschigcb5d7fc02016-12-20 13:14:5563}
64
65} // namespace
66
treib9de525a2017-01-19 12:20:3767class JsonRequestTest : public testing::Test {
fhorschigcb5d7fc02016-12-20 13:14:5568 public:
treib9de525a2017-01-19 12:20:3769 JsonRequestTest()
Alexei Svitkine1d820ef2019-06-20 20:12:3270 : pref_service_(std::make_unique<TestingPrefServiceSimple>()),
fhorschigcb5d7fc02016-12-20 13:14:5571 mock_task_runner_(new base::TestMockTimeTaskRunner()),
Takashi Toyoshimaf3ceca92019-02-04 07:49:0572 mock_runner_handle_(
73 std::make_unique<base::ThreadTaskRunnerHandle>(mock_task_runner_)),
Mark Pilgrimda33faa2018-06-06 14:29:4774 test_shared_loader_factory_(
75 base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
76 &test_url_loader_factory_)) {
Alexei Svitkine1d820ef2019-06-20 20:12:3277 scoped_feature_list_.InitAndEnableFeatureWithParameters(
78 kArticleSuggestionsFeature,
79 {{"send_top_languages", "true"}, {"send_user_class", "true"}});
Michael Martis98cd8732017-07-14 03:26:1980 language::UrlLanguageHistogram::RegisterProfilePrefs(
81 pref_service_->registry());
fhorschigcb5d7fc02016-12-20 13:14:5582 }
83
Michael Martis98cd8732017-07-14 03:26:1984 std::unique_ptr<language::UrlLanguageHistogram> MakeLanguageHistogram(
fhorschigcb5d7fc02016-12-20 13:14:5585 const std::set<std::string>& codes) {
Michael Martis98cd8732017-07-14 03:26:1986 std::unique_ptr<language::UrlLanguageHistogram> language_histogram =
Jinho Bangedffb4ee2018-01-02 15:38:3087 std::make_unique<language::UrlLanguageHistogram>(pref_service_.get());
fhorschigcb5d7fc02016-12-20 13:14:5588 // There must be at least 10 visits before the top languages are defined.
89 for (int i = 0; i < 10; i++) {
90 for (const std::string& code : codes) {
Michael Martis98cd8732017-07-14 03:26:1991 language_histogram->OnPageVisited(code);
fhorschigcb5d7fc02016-12-20 13:14:5592 }
93 }
Michael Martis98cd8732017-07-14 03:26:1994 return language_histogram;
fhorschigcb5d7fc02016-12-20 13:14:5595 }
96
treib9de525a2017-01-19 12:20:3797 JsonRequest::Builder CreateMinimalBuilder() {
98 JsonRequest::Builder builder;
fhorschigcb5d7fc02016-12-20 13:14:5599 builder.SetUrl(GURL("http://valid-url.test"))
tzik9fd63852018-03-08 06:14:18100 .SetClock(mock_task_runner_->GetMockClock())
Mark Pilgrimda33faa2018-06-06 14:29:47101 .SetUrlLoaderFactory(test_shared_loader_factory_);
fhorschigcb5d7fc02016-12-20 13:14:55102 return builder;
103 }
104
Justin DeWitt7943cde2019-07-24 17:28:52105 std::unique_ptr<base::test::ScopedFeatureList> ForceOptionalImagesSupport(
106 bool supported) {
107 auto feature_list = std::make_unique<base::test::ScopedFeatureList>();
108 if (supported) {
109 feature_list->InitWithFeatures({kOptionalImagesEnabledFeature}, {});
110 } else {
111 feature_list->InitWithFeatures({}, {kOptionalImagesEnabledFeature});
112 }
113 return feature_list;
114 }
115
fhorschigcb5d7fc02016-12-20 13:14:55116 private:
Alexei Svitkine1d820ef2019-06-20 20:12:32117 base::test::ScopedFeatureList scoped_feature_list_;
fhorschigcb5d7fc02016-12-20 13:14:55118 std::unique_ptr<TestingPrefServiceSimple> pref_service_;
119 scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_;
Takashi Toyoshimaf3ceca92019-02-04 07:49:05120 std::unique_ptr<base::ThreadTaskRunnerHandle> mock_runner_handle_;
Mark Pilgrimda33faa2018-06-06 14:29:47121 network::TestURLLoaderFactory test_url_loader_factory_;
122 scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
fhorschigcb5d7fc02016-12-20 13:14:55123
treib9de525a2017-01-19 12:20:37124 DISALLOW_COPY_AND_ASSIGN(JsonRequestTest);
fhorschigcb5d7fc02016-12-20 13:14:55125};
126
Caleb Raittoc0a311ba2019-06-05 00:01:41127TEST_F(JsonRequestTest, MAYBE_BuildRequestAuthenticated) {
treib9de525a2017-01-19 12:20:37128 JsonRequest::Builder builder = CreateMinimalBuilder();
129 RequestParams params;
fhorschigcb5d7fc02016-12-20 13:14:55130 params.excluded_ids = {"1234567890"};
131 params.count_to_fetch = 25;
132 params.interactive_request = false;
133 builder.SetParams(params)
134 .SetUrl(GURL("http://valid-url.test"))
Tanmoy Mollik40dfc6ee2019-08-12 12:12:34135 .SetAuthentication("headerstuff")
fhorschigcb5d7fc02016-12-20 13:14:55136 .SetUserClassForTesting("ACTIVE_NTP_USER")
fhorschigcb5d7fc02016-12-20 13:14:55137 .Build();
138
139 EXPECT_THAT(builder.PreviewRequestHeadersForTesting(),
140 StrEq("Content-Type: application/json; charset=UTF-8\r\n"
141 "Authorization: headerstuff\r\n"
142 "\r\n"));
143 EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
144 EqualsJSON("{"
fhorschigcb5d7fc02016-12-20 13:14:55145 " \"priority\": \"BACKGROUND_PREFETCH\","
146 " \"excludedSuggestionIds\": ["
147 " \"1234567890\""
148 " ],"
149 " \"userActivenessClass\": \"ACTIVE_NTP_USER\""
150 "}"));
151}
152
treib9de525a2017-01-19 12:20:37153TEST_F(JsonRequestTest, BuildRequestUnauthenticated) {
154 JsonRequest::Builder builder;
155 RequestParams params;
fhorschigcb5d7fc02016-12-20 13:14:55156 params.interactive_request = true;
157 params.count_to_fetch = 10;
treiba57f51e2017-03-23 14:47:52158 builder.SetParams(params).SetUserClassForTesting("ACTIVE_NTP_USER");
fhorschigcb5d7fc02016-12-20 13:14:55159
160 EXPECT_THAT(builder.PreviewRequestHeadersForTesting(),
161 StrEq("Content-Type: application/json; charset=UTF-8\r\n"
162 "\r\n"));
163 EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
164 EqualsJSON("{"
fhorschigcb5d7fc02016-12-20 13:14:55165 " \"priority\": \"USER_ACTION\","
166 " \"excludedSuggestionIds\": [],"
167 " \"userActivenessClass\": \"ACTIVE_NTP_USER\""
168 "}"));
169}
170
Justin DeWitt7943cde2019-07-24 17:28:52171TEST_F(JsonRequestTest, BuildRequestDisplayCapabilityDisabledByFeature) {
172 auto optional_images_feature_list = ForceOptionalImagesSupport(false);
173
174 JsonRequest::Builder builder;
175 builder.SetOptionalImagesCapability(true);
176
177 EXPECT_THAT(builder.PreviewRequestHeadersForTesting(),
178 StrEq("Content-Type: application/json; charset=UTF-8\r\n"
179 "\r\n"));
180
181 // The JSON should not contain any mention of displayCapability.
182 EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
183 EqualsJSON("{"
184 " \"excludedSuggestionIds\": [],"
185 " \"priority\": \"BACKGROUND_PREFETCH\""
186 "}"));
187}
188
189TEST_F(JsonRequestTest, BuildRequestDisplayCapabilityUnspecified) {
190 auto optional_images_feature_list = ForceOptionalImagesSupport(true);
191
192 JsonRequest::Builder builder;
193 builder.SetOptionalImagesCapability(false);
194
195 EXPECT_THAT(builder.PreviewRequestHeadersForTesting(),
196 StrEq("Content-Type: application/json; charset=UTF-8\r\n"
197 "\r\n"));
198 EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
199 EqualsJSON("{"
200 " \"excludedSuggestionIds\": [],"
201 " \"priority\": \"BACKGROUND_PREFETCH\""
202 "}"));
203}
204
205TEST_F(JsonRequestTest, BuildRequestOptionalImages) {
206 auto optional_images_feature_list = ForceOptionalImagesSupport(true);
207
208 JsonRequest::Builder builder;
209 builder.SetOptionalImagesCapability(true);
210
211 EXPECT_THAT(builder.PreviewRequestHeadersForTesting(),
212 StrEq("Content-Type: application/json; charset=UTF-8\r\n"
213 "\r\n"));
214 EXPECT_THAT(
215 builder.PreviewRequestBodyForTesting(),
216 EqualsJSON("{"
217 " \"displayCapability\": \"CAPABILITY_OPTIONAL_IMAGES\","
218 " \"excludedSuggestionIds\": [],"
219 " \"priority\": \"BACKGROUND_PREFETCH\""
220 "}"));
221}
222
Vitalii Iarko15d451cf2017-06-09 13:57:29223TEST_F(JsonRequestTest, ShouldNotTruncateExcludedIdsList) {
treib9de525a2017-01-19 12:20:37224 JsonRequest::Builder builder;
225 RequestParams params;
fhorschigcb5d7fc02016-12-20 13:14:55226 params.interactive_request = false;
227 for (int i = 0; i < 200; ++i) {
228 params.excluded_ids.insert(base::StringPrintf("%03d", i));
229 }
treiba57f51e2017-03-23 14:47:52230 builder.SetParams(params).SetUserClassForTesting("ACTIVE_NTP_USER");
fhorschigcb5d7fc02016-12-20 13:14:55231
232 EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
233 EqualsJSON("{"
234 " \"priority\": \"BACKGROUND_PREFETCH\","
235 " \"excludedSuggestionIds\": ["
236 " \"000\", \"001\", \"002\", \"003\", \"004\","
237 " \"005\", \"006\", \"007\", \"008\", \"009\","
238 " \"010\", \"011\", \"012\", \"013\", \"014\","
239 " \"015\", \"016\", \"017\", \"018\", \"019\","
240 " \"020\", \"021\", \"022\", \"023\", \"024\","
241 " \"025\", \"026\", \"027\", \"028\", \"029\","
242 " \"030\", \"031\", \"032\", \"033\", \"034\","
243 " \"035\", \"036\", \"037\", \"038\", \"039\","
244 " \"040\", \"041\", \"042\", \"043\", \"044\","
245 " \"045\", \"046\", \"047\", \"048\", \"049\","
246 " \"050\", \"051\", \"052\", \"053\", \"054\","
247 " \"055\", \"056\", \"057\", \"058\", \"059\","
248 " \"060\", \"061\", \"062\", \"063\", \"064\","
249 " \"065\", \"066\", \"067\", \"068\", \"069\","
250 " \"070\", \"071\", \"072\", \"073\", \"074\","
251 " \"075\", \"076\", \"077\", \"078\", \"079\","
252 " \"080\", \"081\", \"082\", \"083\", \"084\","
253 " \"085\", \"086\", \"087\", \"088\", \"089\","
254 " \"090\", \"091\", \"092\", \"093\", \"094\","
Vitalii Iarko15d451cf2017-06-09 13:57:29255 " \"095\", \"096\", \"097\", \"098\", \"099\","
256 " \"100\", \"101\", \"102\", \"103\", \"104\","
257 " \"105\", \"106\", \"107\", \"108\", \"109\","
258 " \"110\", \"111\", \"112\", \"113\", \"114\","
259 " \"115\", \"116\", \"117\", \"118\", \"119\","
260 " \"120\", \"121\", \"122\", \"123\", \"124\","
261 " \"125\", \"126\", \"127\", \"128\", \"129\","
262 " \"130\", \"131\", \"132\", \"133\", \"134\","
263 " \"135\", \"136\", \"137\", \"138\", \"139\","
264 " \"140\", \"141\", \"142\", \"143\", \"144\","
265 " \"145\", \"146\", \"147\", \"148\", \"149\","
266 " \"150\", \"151\", \"152\", \"153\", \"154\","
267 " \"155\", \"156\", \"157\", \"158\", \"159\","
268 " \"160\", \"161\", \"162\", \"163\", \"164\","
269 " \"165\", \"166\", \"167\", \"168\", \"169\","
270 " \"170\", \"171\", \"172\", \"173\", \"174\","
271 " \"175\", \"176\", \"177\", \"178\", \"179\","
272 " \"180\", \"181\", \"182\", \"183\", \"184\","
273 " \"185\", \"186\", \"187\", \"188\", \"189\","
274 " \"190\", \"191\", \"192\", \"193\", \"194\","
275 " \"195\", \"196\", \"197\", \"198\", \"199\""
fhorschigcb5d7fc02016-12-20 13:14:55276 " ],"
277 " \"userActivenessClass\": \"ACTIVE_NTP_USER\""
278 "}"));
279}
280
treib9de525a2017-01-19 12:20:37281TEST_F(JsonRequestTest, BuildRequestNoUserClass) {
282 JsonRequest::Builder builder;
283 RequestParams params;
fhorschigcb5d7fc02016-12-20 13:14:55284 params.interactive_request = false;
treiba57f51e2017-03-23 14:47:52285 builder.SetParams(params);
fhorschigcb5d7fc02016-12-20 13:14:55286
287 EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
288 EqualsJSON("{"
289 " \"priority\": \"BACKGROUND_PREFETCH\","
290 " \"excludedSuggestionIds\": []"
291 "}"));
292}
293
treib9de525a2017-01-19 12:20:37294TEST_F(JsonRequestTest, BuildRequestWithTwoLanguages) {
295 JsonRequest::Builder builder;
Michael Martis98cd8732017-07-14 03:26:19296 std::unique_ptr<language::UrlLanguageHistogram> language_histogram =
297 MakeLanguageHistogram({"de", "en"});
treib9de525a2017-01-19 12:20:37298 RequestParams params;
fhorschigcb5d7fc02016-12-20 13:14:55299 params.interactive_request = true;
300 params.language_code = "en";
Michael Martis98cd8732017-07-14 03:26:19301 builder.SetParams(params).SetLanguageHistogram(language_histogram.get());
fhorschigcb5d7fc02016-12-20 13:14:55302
303 EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
304 EqualsJSON("{"
305 " \"priority\": \"USER_ACTION\","
306 " \"uiLanguage\": \"en\","
307 " \"excludedSuggestionIds\": [],"
308 " \"topLanguages\": ["
309 " {"
310 " \"language\" : \"en\","
311 " \"frequency\" : 0.5"
312 " },"
313 " {"
314 " \"language\" : \"de\","
315 " \"frequency\" : 0.5"
316 " }"
317 " ]"
318 "}"));
319}
320
treib9de525a2017-01-19 12:20:37321TEST_F(JsonRequestTest, BuildRequestWithUILanguageOnly) {
322 JsonRequest::Builder builder;
Michael Martis98cd8732017-07-14 03:26:19323 std::unique_ptr<language::UrlLanguageHistogram> language_histogram =
324 MakeLanguageHistogram({"en"});
treib9de525a2017-01-19 12:20:37325 RequestParams params;
fhorschigcb5d7fc02016-12-20 13:14:55326 params.interactive_request = true;
327 params.language_code = "en";
Michael Martis98cd8732017-07-14 03:26:19328 builder.SetParams(params).SetLanguageHistogram(language_histogram.get());
fhorschigcb5d7fc02016-12-20 13:14:55329
330 EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
331 EqualsJSON("{"
332 " \"priority\": \"USER_ACTION\","
333 " \"uiLanguage\": \"en\","
334 " \"excludedSuggestionIds\": [],"
335 " \"topLanguages\": [{"
336 " \"language\" : \"en\","
337 " \"frequency\" : 1.0"
338 " }]"
339 "}"));
340}
341
Vitalii Iarko3fdb9f912017-09-21 08:28:25342TEST_F(JsonRequestTest,
343 ShouldPropagateCountToFetchWhenExclusiveCategoryPresent) {
344 JsonRequest::Builder builder;
345 RequestParams params;
346 params.interactive_request = true;
347 params.language_code = "en";
348 params.exclusive_category =
349 Category::FromKnownCategory(KnownCategories::ARTICLES);
350 params.count_to_fetch = 25;
351 builder.SetParams(params);
352
353 EXPECT_THAT(builder.PreviewRequestBodyForTesting(), EqualsJSON(R"(
354 {
355 "priority": "USER_ACTION",
356 "uiLanguage": "en",
357 "excludedSuggestionIds": [],
358 "categoryParameters": [{
359 "id": 1,
360 "numSuggestions": 25
361 }]
362 }
363 )"));
364}
365
366// TODO(vitaliii): Propagate count to fetch in this case as well and delete this
367// test. Currently the server does not support this.
368TEST_F(JsonRequestTest,
369 ShouldNotPropagateCountToFetchWhenExclusiveCategoryNotPresent) {
370 JsonRequest::Builder builder;
371 RequestParams params;
372 params.interactive_request = true;
373 params.language_code = "en";
374 params.count_to_fetch = 10;
375 builder.SetParams(params);
376
377 EXPECT_THAT(builder.PreviewRequestBodyForTesting(), EqualsJSON(R"(
378 {
379 "priority": "USER_ACTION",
380 "uiLanguage": "en",
381 "excludedSuggestionIds": []
382 }
383 )"));
384}
385
fhorschigcb5d7fc02016-12-20 13:14:55386} // namespace internal
387
388} // namespace ntp_snippets