1// Copyright 2013 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 "base/bind.h"
6#include "base/message_loop/message_loop.h"
7#include "chrome/browser/local_discovery/privet_http_impl.h"
8#include "net/base/host_port_pair.h"
9#include "net/base/net_errors.h"
10#include "net/url_request/test_url_fetcher_factory.h"
11#include "net/url_request/url_request_test_util.h"
12#include "testing/gmock/include/gmock/gmock.h"
13#include "testing/gtest/include/gtest/gtest.h"
14
15using testing::StrictMock;
16using testing::NiceMock;
17
18namespace local_discovery {
19
20namespace {
21
22const char kSampleInfoResponse[] = "{"
23    "       \"version\": \"1.0\","
24    "       \"name\": \"Common printer\","
25    "       \"description\": \"Printer connected through Chrome connector\","
26    "       \"url\": \"https://www.google.com/cloudprint\","
27    "       \"type\": ["
28    "               \"printer\""
29    "       ],"
30    "       \"id\": \"11111111-2222-3333-4444-555555555555\","
31    "       \"device_state\": \"idle\","
32    "       \"connection_state\": \"online\","
33    "       \"manufacturer\": \"Google\","
34    "       \"model\": \"Google Chrome\","
35    "       \"serial_number\": \"1111-22222-33333-4444\","
36    "       \"firmware\": \"24.0.1312.52\","
37    "       \"uptime\": 600,"
38    "       \"setup_url\": \"http://support.google.com/\","
39    "       \"support_url\": \"http://support.google.com/cloudprint/?hl=en\","
40    "       \"update_url\": \"http://support.google.com/cloudprint/?hl=en\","
41    "       \"x-privet-token\": \"SampleTokenForTesting\","
42    "       \"api\": ["
43    "               \"/privet/accesstoken\","
44    "               \"/privet/capabilities\","
45    "               \"/privet/printer/submitdoc\","
46    "       ]"
47    "}";
48
49const char kSampleRegisterStartResponse[] = "{"
50    "\"user\": \"example@google.com\","
51    "\"action\": \"start\""
52    "}";
53
54const char kSampleRegisterGetClaimTokenResponse[] = "{"
55    "       \"action\": \"getClaimToken\","
56    "       \"user\": \"example@google.com\","
57    "       \"token\": \"MySampleToken\","
58    "       \"claim_url\": \"https://domain.com/SoMeUrL\""
59    "}";
60
61const char kSampleRegisterCompleteResponse[] = "{"
62    "\"user\": \"example@google.com\","
63    "\"action\": \"complete\","
64    "\"device_id\": \"MyDeviceID\""
65    "}";
66
67const char kSampleXPrivetErrorResponse[] =
68    "{ \"error\": \"invalid_x_privet_token\" }";
69
70const char kSampleRegisterErrorTransient[] =
71    "{ \"error\": \"device_busy\", \"timeout\": 1}";
72
73const char kSampleRegisterErrorPermanent[] =
74    "{ \"error\": \"user_cancel\" }";
75
76const char kSampleInfoResponseBadJson[] = "{";
77
78class MockTestURLFetcherFactoryDelegate
79    : public net::TestURLFetcher::DelegateForTests {
80 public:
81  // Callback issued correspondingly to the call to the |Start()| method.
82  MOCK_METHOD1(OnRequestStart, void(int fetcher_id));
83
84  // Callback issued correspondingly to the call to |AppendChunkToUpload|.
85  // Uploaded chunks can be retrieved with the |upload_chunks()| getter.
86  MOCK_METHOD1(OnChunkUpload, void(int fetcher_id));
87
88  // Callback issued correspondingly to the destructor.
89  MOCK_METHOD1(OnRequestEnd, void(int fetcher_id));
90};
91
92class PrivetHTTPTest : public ::testing::Test {
93 public:
94  PrivetHTTPTest() {
95    request_context_= new net::TestURLRequestContextGetter(
96        base::MessageLoopProxy::current());
97    privet_client_.reset(new PrivetHTTPClientImpl(
98        net::HostPortPair("10.0.0.8", 6006),
99        request_context_.get()));
100    fetcher_factory_.SetDelegateForTests(&fetcher_delegate_);
101  }
102  virtual ~PrivetHTTPTest() {
103  }
104
105 protected:
106  base::MessageLoop loop_;
107  scoped_refptr<net::TestURLRequestContextGetter> request_context_;
108  net::TestURLFetcherFactory fetcher_factory_;
109  scoped_ptr<PrivetHTTPClient> privet_client_;
110  NiceMock<MockTestURLFetcherFactoryDelegate> fetcher_delegate_;
111};
112
113class MockInfoDelegate : public PrivetInfoOperation::Delegate {
114 public:
115  MockInfoDelegate() {}
116  ~MockInfoDelegate() {}
117
118  virtual void OnPrivetInfoDone(int response_code,
119                                const base::DictionaryValue* value) OVERRIDE {
120    if (!value) {
121      value_.reset();
122    } else {
123      value_.reset(value->DeepCopy());
124    }
125
126    OnPrivetInfoDoneInternal(response_code);
127  }
128
129  MOCK_METHOD1(OnPrivetInfoDoneInternal, void(int response_code));
130
131  const base::DictionaryValue* value() { return value_.get(); }
132 protected:
133  scoped_ptr<base::DictionaryValue> value_;
134};
135
136class MockRegisterDelegate : public PrivetRegisterOperation::Delegate {
137 public:
138  MockRegisterDelegate() {
139  }
140  ~MockRegisterDelegate() {
141  }
142
143  MOCK_METHOD2(OnPrivetRegisterClaimToken, void(const std::string& token,
144                                                const GURL& url));
145
146  virtual void OnPrivetRegisterError(
147      const std::string& action,
148      PrivetRegisterOperation::FailureReason reason,
149      int printer_http_code,
150      const DictionaryValue* json) OVERRIDE {
151    // TODO(noamsml): Save and test for JSON?
152    OnPrivetRegisterErrorInternal(action, reason, printer_http_code);
153  }
154
155  MOCK_METHOD3(OnPrivetRegisterErrorInternal,
156               void(const std::string& action,
157                    PrivetRegisterOperation::FailureReason reason,
158                    int printer_http_code));
159
160  MOCK_METHOD1(OnPrivetRegisterDone, void(const std::string& device_id));
161};
162
163class PrivetInfoTest : public PrivetHTTPTest {
164 public:
165  PrivetInfoTest() {}
166
167  virtual ~PrivetInfoTest() {}
168
169  virtual void SetUp() OVERRIDE {
170    info_operation_ = privet_client_->CreateInfoOperation(&info_delegate_);
171  }
172
173 protected:
174  scoped_ptr<PrivetInfoOperation> info_operation_;
175  StrictMock<MockInfoDelegate> info_delegate_;
176};
177
178TEST_F(PrivetInfoTest, SuccessfulInfo) {
179  info_operation_->Start();
180
181  net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0);
182  ASSERT_TRUE(fetcher != NULL);
183  EXPECT_EQ(GURL("http://10.0.0.8:6006/privet/info"),
184            fetcher->GetOriginalURL());
185
186  fetcher->SetResponseString(kSampleInfoResponse);
187  fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::SUCCESS,
188                                            net::OK));
189  fetcher->set_response_code(200);
190
191  EXPECT_CALL(info_delegate_, OnPrivetInfoDoneInternal(200));
192  fetcher->delegate()->OnURLFetchComplete(fetcher);
193
194  std::string name;
195
196  privet_client_->GetCachedInfo()->GetString("name", &name);
197  EXPECT_EQ("Common printer", name);
198};
199
200TEST_F(PrivetInfoTest, InfoSaveToken) {
201  info_operation_->Start();
202
203  net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0);
204  ASSERT_TRUE(fetcher != NULL);
205  fetcher->SetResponseString(kSampleInfoResponse);
206  fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::SUCCESS,
207                                            net::OK));
208  fetcher->set_response_code(200);
209
210  EXPECT_CALL(info_delegate_, OnPrivetInfoDoneInternal(200));
211  fetcher->delegate()->OnURLFetchComplete(fetcher);
212
213  info_operation_ = privet_client_->CreateInfoOperation(&info_delegate_);
214  info_operation_->Start();
215
216  fetcher = fetcher_factory_.GetFetcherByID(0);
217  ASSERT_TRUE(fetcher != NULL);
218  net::HttpRequestHeaders headers;
219  fetcher->GetExtraRequestHeaders(&headers);
220  std::string header_token;
221  ASSERT_TRUE(headers.GetHeader("X-Privet-Token", &header_token));
222  EXPECT_EQ("SampleTokenForTesting", header_token);
223};
224
225TEST_F(PrivetInfoTest, InfoFailureHTTP) {
226  info_operation_->Start();
227
228  net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0);
229  ASSERT_TRUE(fetcher != NULL);
230  fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::SUCCESS,
231                                            net::OK));
232  fetcher->set_response_code(404);
233
234  EXPECT_CALL(info_delegate_, OnPrivetInfoDoneInternal(404));
235  fetcher->delegate()->OnURLFetchComplete(fetcher);
236  EXPECT_EQ(NULL, privet_client_->GetCachedInfo());
237};
238
239TEST_F(PrivetInfoTest, InfoFailureInternal) {
240  info_operation_->Start();
241
242  net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0);
243  ASSERT_TRUE(fetcher != NULL);
244  fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED,
245                                            net::OK));
246  fetcher->set_response_code(200);
247
248  EXPECT_CALL(info_delegate_, OnPrivetInfoDoneInternal(-1));
249  fetcher->delegate()->OnURLFetchComplete(fetcher);
250  EXPECT_EQ(NULL, privet_client_->GetCachedInfo());
251};
252
253class PrivetRegisterTest : public PrivetHTTPTest {
254 public:
255  PrivetRegisterTest() {
256  }
257  virtual ~PrivetRegisterTest() {
258  }
259
260  virtual void SetUp() OVERRIDE {
261    info_operation_ = privet_client_->CreateInfoOperation(&info_delegate_);
262    register_operation_ =
263        privet_client_->CreateRegisterOperation("example@google.com",
264                                                &register_delegate_);
265  }
266
267 protected:
268  bool SuccessfulResponseToURL(const GURL& url,
269                                         const std::string& response) {
270    net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0);
271    if (!fetcher || url != fetcher->GetOriginalURL())
272      return false;
273
274    fetcher->SetResponseString(response);
275    fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::SUCCESS,
276                                              net::OK));
277    fetcher->set_response_code(200);
278    fetcher->delegate()->OnURLFetchComplete(fetcher);
279    return true;
280  }
281
282  void RunFor(base::TimeDelta time_period) {
283    base::CancelableCallback<void()> callback(base::Bind(
284        &PrivetRegisterTest::Stop, base::Unretained(this)));
285    base::MessageLoop::current()->PostDelayedTask(
286        FROM_HERE, callback.callback(), time_period);
287
288    base::MessageLoop::current()->Run();
289    callback.Cancel();
290  }
291
292  void Stop() {
293    base::MessageLoop::current()->Quit();
294  }
295
296  scoped_ptr<PrivetInfoOperation> info_operation_;
297  NiceMock<MockInfoDelegate> info_delegate_;
298  scoped_ptr<PrivetRegisterOperation> register_operation_;
299  StrictMock<MockRegisterDelegate> register_delegate_;
300};
301
302TEST_F(PrivetRegisterTest, RegisterSuccessSimple) {
303  // Start with info request first to populate XSRF token.
304  info_operation_->Start();
305
306  EXPECT_TRUE(SuccessfulResponseToURL(
307      GURL("http://10.0.0.8:6006/privet/info"),
308      kSampleInfoResponse));
309
310  register_operation_->Start();
311
312  EXPECT_TRUE(SuccessfulResponseToURL(
313      GURL("http://10.0.0.8:6006/privet/register?"
314           "action=start&user=example@google.com"),
315      kSampleRegisterStartResponse));
316
317  EXPECT_CALL(register_delegate_, OnPrivetRegisterClaimToken(
318      "MySampleToken",
319      GURL("https://domain.com/SoMeUrL")));
320
321  EXPECT_TRUE(SuccessfulResponseToURL(
322      GURL("http://10.0.0.8:6006/privet/register?"
323           "action=getClaimToken&user=example@google.com"),
324      kSampleRegisterGetClaimTokenResponse));
325
326  register_operation_->CompleteRegistration();
327
328  EXPECT_CALL(register_delegate_, OnPrivetRegisterDone(
329      "MyDeviceID"));
330
331  EXPECT_TRUE(SuccessfulResponseToURL(
332      GURL("http://10.0.0.8:6006/privet/register?"
333           "action=complete&user=example@google.com"),
334      kSampleRegisterCompleteResponse));
335}
336
337TEST_F(PrivetRegisterTest, RegisterNoInfoCall) {
338  register_operation_->Start();
339
340  EXPECT_TRUE(SuccessfulResponseToURL(
341      GURL("http://10.0.0.8:6006/privet/info"),
342      kSampleInfoResponse));
343
344  EXPECT_TRUE(SuccessfulResponseToURL(
345      GURL("http://10.0.0.8:6006/privet/register?"
346           "action=start&user=example@google.com"),
347      kSampleRegisterStartResponse));
348}
349
350TEST_F(PrivetRegisterTest, RegisterXSRFFailure) {
351  register_operation_->Start();
352
353  EXPECT_TRUE(SuccessfulResponseToURL(
354      GURL("http://10.0.0.8:6006/privet/info"),
355      kSampleInfoResponse));
356
357  EXPECT_TRUE(SuccessfulResponseToURL(
358      GURL("http://10.0.0.8:6006/privet/register?"
359           "action=start&user=example@google.com"),
360      kSampleRegisterStartResponse));
361
362  EXPECT_TRUE(SuccessfulResponseToURL(
363      GURL("http://10.0.0.8:6006/privet/register?"
364           "action=getClaimToken&user=example@google.com"),
365      kSampleXPrivetErrorResponse));
366
367  EXPECT_TRUE(SuccessfulResponseToURL(
368      GURL("http://10.0.0.8:6006/privet/info"),
369      kSampleInfoResponse));
370
371  EXPECT_CALL(register_delegate_, OnPrivetRegisterClaimToken(
372      "MySampleToken", GURL("https://domain.com/SoMeUrL")));
373
374  EXPECT_TRUE(SuccessfulResponseToURL(
375      GURL("http://10.0.0.8:6006/privet/register?"
376           "action=getClaimToken&user=example@google.com"),
377      kSampleRegisterGetClaimTokenResponse));
378}
379
380TEST_F(PrivetRegisterTest, TransientFailure) {
381  register_operation_->Start();
382
383  EXPECT_TRUE(SuccessfulResponseToURL(
384      GURL("http://10.0.0.8:6006/privet/info"),
385      kSampleInfoResponse));
386
387  EXPECT_TRUE(SuccessfulResponseToURL(
388      GURL("http://10.0.0.8:6006/privet/register?"
389           "action=start&user=example@google.com"),
390      kSampleRegisterErrorTransient));
391
392  EXPECT_CALL(fetcher_delegate_, OnRequestStart(0));
393
394  RunFor(base::TimeDelta::FromSeconds(2));
395
396  testing::Mock::VerifyAndClearExpectations(&fetcher_delegate_);
397
398  EXPECT_TRUE(SuccessfulResponseToURL(
399      GURL("http://10.0.0.8:6006/privet/register?"
400           "action=start&user=example@google.com"),
401      kSampleRegisterStartResponse));
402}
403
404TEST_F(PrivetRegisterTest, PermanentFailure) {
405  register_operation_->Start();
406
407  EXPECT_TRUE(SuccessfulResponseToURL(
408      GURL("http://10.0.0.8:6006/privet/info"),
409      kSampleInfoResponse));
410
411  EXPECT_TRUE(SuccessfulResponseToURL(
412      GURL("http://10.0.0.8:6006/privet/register?"
413           "action=start&user=example@google.com"),
414      kSampleRegisterStartResponse));
415
416  EXPECT_CALL(register_delegate_,
417              OnPrivetRegisterErrorInternal(
418                  "getClaimToken",
419                  PrivetRegisterOperation::FAILURE_JSON_ERROR,
420                  200));
421
422  EXPECT_TRUE(SuccessfulResponseToURL(
423      GURL("http://10.0.0.8:6006/privet/register?"
424           "action=getClaimToken&user=example@google.com"),
425      kSampleRegisterErrorPermanent));
426}
427
428TEST_F(PrivetRegisterTest, InfoFailure) {
429  register_operation_->Start();
430
431  EXPECT_CALL(register_delegate_,
432              OnPrivetRegisterErrorInternal(
433                  "info",
434                  PrivetRegisterOperation::FAILURE_NETWORK,
435                  -1));
436
437
438  EXPECT_TRUE(SuccessfulResponseToURL(
439      GURL("http://10.0.0.8:6006/privet/info"),
440      kSampleInfoResponseBadJson));
441}
442
443}  // namespace
444
445}  // namespace local_discovery
446