per_user_topic_subscription_request_unittest.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. // Copyright 2018 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/invalidation/impl/per_user_topic_subscription_request.h"
  5. #include "base/bind.h"
  6. #include "base/memory/ptr_util.h"
  7. #include "base/run_loop.h"
  8. #include "base/strings/stringprintf.h"
  9. #include "base/test/bind.h"
  10. #include "base/test/gtest_util.h"
  11. #include "base/test/mock_callback.h"
  12. #include "base/test/scoped_feature_list.h"
  13. #include "base/test/task_environment.h"
  14. #include "base/test/test_simple_task_runner.h"
  15. #include "base/threading/thread_task_runner_handle.h"
  16. #include "base/values.h"
  17. #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
  18. #include "services/network/public/mojom/url_response_head.mojom.h"
  19. #include "services/network/test/test_url_loader_factory.h"
  20. #include "testing/gmock/include/gmock/gmock.h"
  21. #include "testing/gtest/include/gtest/gtest.h"
  22. #include "url/gurl.h"
  23. namespace invalidation {
  24. namespace {
  25. using testing::_;
  26. using testing::DoAll;
  27. using testing::SaveArg;
  28. network::mojom::URLResponseHeadPtr CreateHeadersForTest(int responce_code) {
  29. auto head = network::mojom::URLResponseHead::New();
  30. head->headers = new net::HttpResponseHeaders(base::StringPrintf(
  31. "HTTP/1.1 %d OK\nContent-type: text/html\n\n", responce_code));
  32. head->mime_type = "text/html";
  33. return head;
  34. }
  35. } // namespace
  36. class PerUserTopicSubscriptionRequestTest : public testing::Test {
  37. public:
  38. PerUserTopicSubscriptionRequestTest() = default;
  39. ~PerUserTopicSubscriptionRequestTest() override = default;
  40. GURL url(PerUserTopicSubscriptionRequest* request) {
  41. return request->GetUrlForTesting();
  42. }
  43. network::TestURLLoaderFactory* url_loader_factory() {
  44. return &url_loader_factory_;
  45. }
  46. private:
  47. base::test::SingleThreadTaskEnvironment task_environment_;
  48. data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
  49. network::TestURLLoaderFactory url_loader_factory_;
  50. };
  51. TEST_F(PerUserTopicSubscriptionRequestTest,
  52. ShouldNotInvokeCallbackWhenCancelled) {
  53. std::string token = "1234567890";
  54. std::string url = "http://valid-url.test";
  55. std::string topic = "test";
  56. std::string project_id = "smarty-pants-12345";
  57. PerUserTopicSubscriptionRequest::RequestType type =
  58. PerUserTopicSubscriptionRequest::SUBSCRIBE;
  59. base::MockCallback<PerUserTopicSubscriptionRequest::CompletedCallback>
  60. callback;
  61. EXPECT_CALL(callback, Run(_, _)).Times(0);
  62. PerUserTopicSubscriptionRequest::Builder builder;
  63. std::unique_ptr<PerUserTopicSubscriptionRequest> request =
  64. builder.SetInstanceIdToken(token)
  65. .SetScope(url)
  66. .SetPublicTopicName(topic)
  67. .SetProjectId(project_id)
  68. .SetType(type)
  69. .Build();
  70. request->Start(callback.Get(), url_loader_factory());
  71. base::RunLoop().RunUntilIdle();
  72. // Destroy the request before getting any response.
  73. request.reset();
  74. }
  75. TEST_F(PerUserTopicSubscriptionRequestTest, ShouldSubscribeWithoutErrors) {
  76. std::string token = "1234567890";
  77. std::string base_url = "http://valid-url.test";
  78. std::string topic = "test";
  79. std::string project_id = "smarty-pants-12345";
  80. PerUserTopicSubscriptionRequest::RequestType type =
  81. PerUserTopicSubscriptionRequest::SUBSCRIBE;
  82. base::MockCallback<PerUserTopicSubscriptionRequest::CompletedCallback>
  83. callback;
  84. Status status(StatusCode::FAILED, "initial");
  85. std::string private_topic;
  86. EXPECT_CALL(callback, Run(_, _))
  87. .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
  88. PerUserTopicSubscriptionRequest::Builder builder;
  89. std::unique_ptr<PerUserTopicSubscriptionRequest> request =
  90. builder.SetInstanceIdToken(token)
  91. .SetScope(base_url)
  92. .SetPublicTopicName(topic)
  93. .SetProjectId(project_id)
  94. .SetType(type)
  95. .Build();
  96. std::string response_body = R"(
  97. {
  98. "privateTopicName": "test-pr"
  99. }
  100. )";
  101. network::URLLoaderCompletionStatus response_status(net::OK);
  102. response_status.decoded_body_length = response_body.size();
  103. url_loader_factory()->AddResponse(url(request.get()),
  104. CreateHeadersForTest(net::HTTP_OK),
  105. response_body, response_status);
  106. request->Start(callback.Get(), url_loader_factory());
  107. base::RunLoop().RunUntilIdle();
  108. EXPECT_EQ(status.code, StatusCode::SUCCESS);
  109. EXPECT_EQ(private_topic, "test-pr");
  110. }
  111. TEST_F(PerUserTopicSubscriptionRequestTest,
  112. ShouleNotSubscribeWhenNetworkProblem) {
  113. std::string token = "1234567890";
  114. std::string base_url = "http://valid-url.test";
  115. std::string topic = "test";
  116. std::string project_id = "smarty-pants-12345";
  117. PerUserTopicSubscriptionRequest::RequestType type =
  118. PerUserTopicSubscriptionRequest::SUBSCRIBE;
  119. base::MockCallback<PerUserTopicSubscriptionRequest::CompletedCallback>
  120. callback;
  121. Status status(StatusCode::FAILED, "initial");
  122. std::string private_topic;
  123. EXPECT_CALL(callback, Run(_, _))
  124. .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
  125. PerUserTopicSubscriptionRequest::Builder builder;
  126. std::unique_ptr<PerUserTopicSubscriptionRequest> request =
  127. builder.SetInstanceIdToken(token)
  128. .SetScope(base_url)
  129. .SetPublicTopicName(topic)
  130. .SetProjectId(project_id)
  131. .SetType(type)
  132. .Build();
  133. std::string response_body = R"(
  134. {
  135. "privateTopicName": "test-pr"
  136. }
  137. )";
  138. network::URLLoaderCompletionStatus response_status(net::ERR_TIMED_OUT);
  139. response_status.decoded_body_length = response_body.size();
  140. url_loader_factory()->AddResponse(url(request.get()),
  141. CreateHeadersForTest(net::HTTP_OK),
  142. response_body, response_status);
  143. request->Start(callback.Get(), url_loader_factory());
  144. base::RunLoop().RunUntilIdle();
  145. EXPECT_EQ(status.code, StatusCode::FAILED);
  146. }
  147. TEST_F(PerUserTopicSubscriptionRequestTest,
  148. ShouldNotSubscribeWhenWrongResponse) {
  149. std::string token = "1234567890";
  150. std::string base_url = "http://valid-url.test";
  151. std::string topic = "test";
  152. std::string project_id = "smarty-pants-12345";
  153. PerUserTopicSubscriptionRequest::RequestType type =
  154. PerUserTopicSubscriptionRequest::SUBSCRIBE;
  155. base::MockCallback<PerUserTopicSubscriptionRequest::CompletedCallback>
  156. callback;
  157. Status status(StatusCode::SUCCESS, "initial");
  158. std::string private_topic;
  159. EXPECT_CALL(callback, Run(_, _))
  160. .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
  161. PerUserTopicSubscriptionRequest::Builder builder;
  162. std::unique_ptr<PerUserTopicSubscriptionRequest> request =
  163. builder.SetInstanceIdToken(token)
  164. .SetScope(base_url)
  165. .SetPublicTopicName(topic)
  166. .SetProjectId(project_id)
  167. .SetType(type)
  168. .Build();
  169. std::string response_body = R"(
  170. {}
  171. )";
  172. network::URLLoaderCompletionStatus response_status(net::OK);
  173. response_status.decoded_body_length = response_body.size();
  174. url_loader_factory()->AddResponse(url(request.get()),
  175. CreateHeadersForTest(net::HTTP_OK),
  176. response_body, response_status);
  177. request->Start(callback.Get(), url_loader_factory());
  178. base::RunLoop().RunUntilIdle();
  179. EXPECT_EQ(status.code, StatusCode::FAILED);
  180. EXPECT_EQ(status.message, "Missing topic name");
  181. }
  182. TEST_F(PerUserTopicSubscriptionRequestTest, ShouldUnsubscribe) {
  183. std::string token = "1234567890";
  184. std::string base_url = "http://valid-url.test";
  185. std::string topic = "test";
  186. std::string project_id = "smarty-pants-12345";
  187. PerUserTopicSubscriptionRequest::RequestType type =
  188. PerUserTopicSubscriptionRequest::UNSUBSCRIBE;
  189. base::MockCallback<PerUserTopicSubscriptionRequest::CompletedCallback>
  190. callback;
  191. Status status(StatusCode::FAILED, "initial");
  192. std::string private_topic;
  193. EXPECT_CALL(callback, Run(_, _))
  194. .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
  195. PerUserTopicSubscriptionRequest::Builder builder;
  196. std::unique_ptr<PerUserTopicSubscriptionRequest> request =
  197. builder.SetInstanceIdToken(token)
  198. .SetScope(base_url)
  199. .SetPublicTopicName(topic)
  200. .SetProjectId(project_id)
  201. .SetType(type)
  202. .Build();
  203. std::string response_body = R"(
  204. {}
  205. )";
  206. network::URLLoaderCompletionStatus response_status(net::OK);
  207. response_status.decoded_body_length = response_body.size();
  208. url_loader_factory()->AddResponse(url(request.get()),
  209. CreateHeadersForTest(net::HTTP_OK),
  210. response_body, response_status);
  211. request->Start(callback.Get(), url_loader_factory());
  212. base::RunLoop().RunUntilIdle();
  213. EXPECT_EQ(status.code, StatusCode::SUCCESS);
  214. EXPECT_EQ(status.message, std::string());
  215. }
  216. // Regression test for crbug.com/1054590, |completed_callback| destroys
  217. // |request|.
  218. TEST_F(PerUserTopicSubscriptionRequestTest, ShouldDestroyOnFailure) {
  219. std::string token = "1234567890";
  220. std::string base_url = "http://valid-url.test";
  221. std::string topic = "test";
  222. std::string project_id = "smarty-pants-12345";
  223. PerUserTopicSubscriptionRequest::RequestType type =
  224. PerUserTopicSubscriptionRequest::SUBSCRIBE;
  225. std::unique_ptr<PerUserTopicSubscriptionRequest> request;
  226. bool callback_called = false;
  227. auto completed_callback = base::BindLambdaForTesting(
  228. [&](const Status& status, const std::string& topic_name) {
  229. request.reset();
  230. callback_called = true;
  231. });
  232. PerUserTopicSubscriptionRequest::Builder builder;
  233. request = builder.SetInstanceIdToken(token)
  234. .SetScope(base_url)
  235. .SetPublicTopicName(topic)
  236. .SetProjectId(project_id)
  237. .SetType(type)
  238. .Build();
  239. std::string response_body = R"(
  240. {
  241. "privateTopicName": "test-pr"
  242. }
  243. )";
  244. network::URLLoaderCompletionStatus response_status(net::ERR_TIMED_OUT);
  245. response_status.decoded_body_length = response_body.size();
  246. url_loader_factory()->AddResponse(url(request.get()),
  247. CreateHeadersForTest(net::HTTP_OK),
  248. response_body, response_status);
  249. request->Start(std::move(completed_callback), url_loader_factory());
  250. base::RunLoop().RunUntilIdle();
  251. EXPECT_TRUE(callback_called);
  252. // The main expectation is that there is no crash.
  253. }
  254. class PerUserTopicSubscriptionRequestParamTest
  255. : public PerUserTopicSubscriptionRequestTest,
  256. public testing::WithParamInterface<net::HttpStatusCode> {
  257. public:
  258. PerUserTopicSubscriptionRequestParamTest() = default;
  259. PerUserTopicSubscriptionRequestParamTest(
  260. const PerUserTopicSubscriptionRequestParamTest&) = delete;
  261. PerUserTopicSubscriptionRequestParamTest& operator=(
  262. const PerUserTopicSubscriptionRequestParamTest&) = delete;
  263. ~PerUserTopicSubscriptionRequestParamTest() override = default;
  264. };
  265. TEST_P(PerUserTopicSubscriptionRequestParamTest,
  266. ShouldNotSubscribeWhenNonRepeatableError) {
  267. std::string token = "1234567890";
  268. std::string base_url = "http://valid-url.test";
  269. std::string topic = "test";
  270. std::string project_id = "smarty-pants-12345";
  271. PerUserTopicSubscriptionRequest::RequestType type =
  272. PerUserTopicSubscriptionRequest::SUBSCRIBE;
  273. base::MockCallback<PerUserTopicSubscriptionRequest::CompletedCallback>
  274. callback;
  275. Status status(StatusCode::FAILED, "initial");
  276. std::string private_topic;
  277. EXPECT_CALL(callback, Run(_, _))
  278. .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
  279. PerUserTopicSubscriptionRequest::Builder builder;
  280. std::unique_ptr<PerUserTopicSubscriptionRequest> request =
  281. builder.SetInstanceIdToken(token)
  282. .SetScope(base_url)
  283. .SetPublicTopicName(topic)
  284. .SetProjectId(project_id)
  285. .SetType(type)
  286. .Build();
  287. network::URLLoaderCompletionStatus response_status(net::OK);
  288. url_loader_factory()->AddResponse(
  289. url(request.get()), CreateHeadersForTest(GetParam()),
  290. /* response_body */ std::string(), response_status);
  291. request->Start(callback.Get(), url_loader_factory());
  292. base::RunLoop().RunUntilIdle();
  293. EXPECT_EQ(status.code, StatusCode::FAILED_NON_RETRIABLE);
  294. }
  295. INSTANTIATE_TEST_SUITE_P(All,
  296. PerUserTopicSubscriptionRequestParamTest,
  297. testing::Values(net::HTTP_BAD_REQUEST,
  298. net::HTTP_FORBIDDEN,
  299. net::HTTP_NOT_FOUND));
  300. } // namespace invalidation