1// Copyright 2014 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
5#include <map>
6#include <string>
7#include <vector>
8
9#include "base/strings/string_number_conversions.h"
10#include "base/strings/string_tokenizer.h"
11#include "google_apis/gcm/engine/unregistration_request.h"
12#include "google_apis/gcm/monitoring/fake_gcm_stats_recorder.h"
13#include "net/url_request/test_url_fetcher_factory.h"
14#include "net/url_request/url_request_test_util.h"
15#include "testing/gtest/include/gtest/gtest.h"
16
17namespace gcm {
18
19namespace {
20const uint64 kAndroidId = 42UL;
21const char kLoginHeader[] = "AidLogin";
22const char kAppId[] = "TestAppId";
23const char kDeletedAppId[] = "deleted=TestAppId";
24const char kRegistrationURL[] = "http://foo.bar/register";
25const uint64 kSecurityToken = 77UL;
26
27// Backoff policy for testing registration request.
28const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
29  // Number of initial errors (in sequence) to ignore before applying
30  // exponential back-off rules.
31  // Explicitly set to 2 to skip the delay on the first retry, as we are not
32  // trying to test the backoff itself, but rather the fact that retry happens.
33  1,
34
35  // Initial delay for exponential back-off in ms.
36  15000,  // 15 seconds.
37
38  // Factor by which the waiting time will be multiplied.
39  2,
40
41  // Fuzzing percentage. ex: 10% will spread requests randomly
42  // between 90%-100% of the calculated time.
43  0.5,  // 50%.
44
45  // Maximum amount of time we are willing to delay our request in ms.
46  1000 * 60 * 5, // 5 minutes.
47
48  // Time to keep an entry from being discarded even when it
49  // has no significant state, -1 to never discard.
50  -1,
51
52  // Don't use initial delay unless the last request was an error.
53  false,
54};
55}  // namespace
56
57class UnregistrationRequestTest : public testing::Test {
58 public:
59  UnregistrationRequestTest();
60  virtual ~UnregistrationRequestTest();
61
62  void UnregistrationCallback(UnregistrationRequest::Status status);
63
64  void CreateRequest();
65  void SetResponseStatusAndString(net::HttpStatusCode status_code,
66                                  const std::string& response_body);
67  void CompleteFetch();
68
69 protected:
70  bool callback_called_;
71  UnregistrationRequest::Status status_;
72  scoped_ptr<UnregistrationRequest> request_;
73  base::MessageLoop message_loop_;
74  net::TestURLFetcherFactory url_fetcher_factory_;
75  scoped_refptr<net::TestURLRequestContextGetter> url_request_context_getter_;
76  FakeGCMStatsRecorder recorder_;
77};
78
79UnregistrationRequestTest::UnregistrationRequestTest()
80    : callback_called_(false),
81      status_(UnregistrationRequest::UNREGISTRATION_STATUS_COUNT),
82      url_request_context_getter_(new net::TestURLRequestContextGetter(
83          message_loop_.message_loop_proxy())) {}
84
85UnregistrationRequestTest::~UnregistrationRequestTest() {}
86
87void UnregistrationRequestTest::UnregistrationCallback(
88    UnregistrationRequest::Status status) {
89  callback_called_ = true;
90  status_ = status;
91}
92
93void UnregistrationRequestTest::CreateRequest() {
94  request_.reset(new UnregistrationRequest(
95      GURL(kRegistrationURL),
96      UnregistrationRequest::RequestInfo(kAndroidId,
97                                         kSecurityToken,
98                                         kAppId),
99      kDefaultBackoffPolicy,
100      base::Bind(&UnregistrationRequestTest::UnregistrationCallback,
101                 base::Unretained(this)),
102      url_request_context_getter_.get(),
103      &recorder_));
104}
105
106void UnregistrationRequestTest::SetResponseStatusAndString(
107    net::HttpStatusCode status_code,
108    const std::string& response_body) {
109  net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0);
110  ASSERT_TRUE(fetcher);
111  fetcher->set_response_code(status_code);
112  fetcher->SetResponseString(response_body);
113}
114
115void UnregistrationRequestTest::CompleteFetch() {
116  status_ = UnregistrationRequest::UNREGISTRATION_STATUS_COUNT;
117  callback_called_ = false;
118  net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0);
119  ASSERT_TRUE(fetcher);
120  fetcher->delegate()->OnURLFetchComplete(fetcher);
121}
122
123TEST_F(UnregistrationRequestTest, RequestDataPassedToFetcher) {
124  CreateRequest();
125  request_->Start();
126
127  // Get data sent by request.
128  net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0);
129  ASSERT_TRUE(fetcher);
130
131  EXPECT_EQ(GURL(kRegistrationURL), fetcher->GetOriginalURL());
132
133  // Verify that authorization header was put together properly.
134  net::HttpRequestHeaders headers;
135  fetcher->GetExtraRequestHeaders(&headers);
136  std::string auth_header;
137  headers.GetHeader(net::HttpRequestHeaders::kAuthorization, &auth_header);
138  base::StringTokenizer auth_tokenizer(auth_header, " :");
139  ASSERT_TRUE(auth_tokenizer.GetNext());
140  EXPECT_EQ(kLoginHeader, auth_tokenizer.token());
141  ASSERT_TRUE(auth_tokenizer.GetNext());
142  EXPECT_EQ(base::Uint64ToString(kAndroidId), auth_tokenizer.token());
143  ASSERT_TRUE(auth_tokenizer.GetNext());
144  EXPECT_EQ(base::Uint64ToString(kSecurityToken), auth_tokenizer.token());
145  std::string app_id_header;
146  headers.GetHeader("app", &app_id_header);
147  EXPECT_EQ(kAppId, app_id_header);
148
149  std::map<std::string, std::string> expected_pairs;
150  expected_pairs["app"] = kAppId;
151  expected_pairs["device"] = base::Uint64ToString(kAndroidId);
152  expected_pairs["delete"] = "true";
153  expected_pairs["gcm_unreg_caller"] = "false";
154
155  // Verify data was formatted properly.
156  std::string upload_data = fetcher->upload_data();
157  base::StringTokenizer data_tokenizer(upload_data, "&=");
158  while (data_tokenizer.GetNext()) {
159    std::map<std::string, std::string>::iterator iter =
160        expected_pairs.find(data_tokenizer.token());
161    ASSERT_TRUE(iter != expected_pairs.end()) << data_tokenizer.token();
162    ASSERT_TRUE(data_tokenizer.GetNext()) << data_tokenizer.token();
163    EXPECT_EQ(iter->second, data_tokenizer.token());
164    // Ensure that none of the keys appears twice.
165    expected_pairs.erase(iter);
166  }
167
168  EXPECT_EQ(0UL, expected_pairs.size());
169}
170
171TEST_F(UnregistrationRequestTest, SuccessfulUnregistration) {
172  CreateRequest();
173  request_->Start();
174
175  SetResponseStatusAndString(net::HTTP_OK, kDeletedAppId);
176  CompleteFetch();
177
178  EXPECT_TRUE(callback_called_);
179  EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
180}
181
182TEST_F(UnregistrationRequestTest, ResponseHttpStatusNotOK) {
183  CreateRequest();
184  request_->Start();
185
186  SetResponseStatusAndString(net::HTTP_UNAUTHORIZED, "");
187  CompleteFetch();
188
189  EXPECT_TRUE(callback_called_);
190  EXPECT_EQ(UnregistrationRequest::HTTP_NOT_OK, status_);
191}
192
193TEST_F(UnregistrationRequestTest, ResponseEmpty) {
194  CreateRequest();
195  request_->Start();
196
197  SetResponseStatusAndString(net::HTTP_OK, "");
198  CompleteFetch();
199
200  EXPECT_FALSE(callback_called_);
201
202  SetResponseStatusAndString(net::HTTP_OK, kDeletedAppId);
203  CompleteFetch();
204
205  EXPECT_TRUE(callback_called_);
206  EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
207}
208
209TEST_F(UnregistrationRequestTest, InvalidParametersError) {
210  CreateRequest();
211  request_->Start();
212
213  SetResponseStatusAndString(net::HTTP_OK, "Error=INVALID_PARAMETERS");
214  CompleteFetch();
215
216  EXPECT_TRUE(callback_called_);
217  EXPECT_EQ(UnregistrationRequest::INVALID_PARAMETERS, status_);
218}
219
220TEST_F(UnregistrationRequestTest, UnkwnownError) {
221  CreateRequest();
222  request_->Start();
223
224  SetResponseStatusAndString(net::HTTP_OK, "Error=XXX");
225  CompleteFetch();
226
227  EXPECT_TRUE(callback_called_);
228  EXPECT_EQ(UnregistrationRequest::UNKNOWN_ERROR, status_);
229}
230
231TEST_F(UnregistrationRequestTest, ServiceUnavailable) {
232  CreateRequest();
233  request_->Start();
234
235  SetResponseStatusAndString(net::HTTP_SERVICE_UNAVAILABLE, "");
236  CompleteFetch();
237
238  EXPECT_FALSE(callback_called_);
239
240  SetResponseStatusAndString(net::HTTP_OK, kDeletedAppId);
241  CompleteFetch();
242
243  EXPECT_TRUE(callback_called_);
244  EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
245}
246
247TEST_F(UnregistrationRequestTest, InternalServerError) {
248  CreateRequest();
249  request_->Start();
250
251  SetResponseStatusAndString(net::HTTP_INTERNAL_SERVER_ERROR, "");
252  CompleteFetch();
253
254  EXPECT_FALSE(callback_called_);
255
256  SetResponseStatusAndString(net::HTTP_OK, kDeletedAppId);
257  CompleteFetch();
258
259  EXPECT_TRUE(callback_called_);
260  EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
261}
262
263TEST_F(UnregistrationRequestTest, IncorrectAppId) {
264  CreateRequest();
265  request_->Start();
266
267  SetResponseStatusAndString(net::HTTP_OK, "deleted=OtherTestAppId");
268  CompleteFetch();
269
270  EXPECT_FALSE(callback_called_);
271
272  SetResponseStatusAndString(net::HTTP_OK, kDeletedAppId);
273  CompleteFetch();
274
275  EXPECT_TRUE(callback_called_);
276  EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
277}
278
279TEST_F(UnregistrationRequestTest, ResponseParsingFailed) {
280  CreateRequest();
281  request_->Start();
282
283  SetResponseStatusAndString(net::HTTP_OK, "some malformed response");
284  CompleteFetch();
285
286  EXPECT_FALSE(callback_called_);
287
288  SetResponseStatusAndString(net::HTTP_OK, kDeletedAppId);
289  CompleteFetch();
290
291  EXPECT_TRUE(callback_called_);
292  EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
293}
294
295}  // namespace gcm
296