Home | History | Annotate | Download | only in local_discovery
      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 
     15 using testing::StrictMock;
     16 using testing::NiceMock;
     17 
     18 namespace local_discovery {
     19 
     20 namespace {
     21 
     22 const 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 
     49 const char kSampleRegisterStartResponse[] = "{"
     50     "\"user\": \"example (at) google.com\","
     51     "\"action\": \"start\""
     52     "}";
     53 
     54 const char kSampleRegisterGetClaimTokenResponse[] = "{"
     55     "       \"action\": \"getClaimToken\","
     56     "       \"user\": \"example (at) google.com\","
     57     "       \"token\": \"MySampleToken\","
     58     "       \"claim_url\": \"https://domain.com/SoMeUrL\""
     59     "}";
     60 
     61 const char kSampleRegisterCompleteResponse[] = "{"
     62     "\"user\": \"example (at) google.com\","
     63     "\"action\": \"complete\","
     64     "\"device_id\": \"MyDeviceID\""
     65     "}";
     66 
     67 const char kSampleXPrivetErrorResponse[] =
     68     "{ \"error\": \"invalid_x_privet_token\" }";
     69 
     70 const char kSampleRegisterErrorTransient[] =
     71     "{ \"error\": \"device_busy\", \"timeout\": 1}";
     72 
     73 const char kSampleRegisterErrorPermanent[] =
     74     "{ \"error\": \"user_cancel\" }";
     75 
     76 const char kSampleInfoResponseBadJson[] = "{";
     77 
     78 class 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 
     92 class 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 
    113 class 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 
    136 class 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 
    163 class 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 
    178 TEST_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 
    200 TEST_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 
    225 TEST_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 
    239 TEST_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 
    253 class 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 (at) 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 
    302 TEST_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 (at) 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 (at) 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 (at) google.com"),
    334       kSampleRegisterCompleteResponse));
    335 }
    336 
    337 TEST_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 (at) google.com"),
    347       kSampleRegisterStartResponse));
    348 }
    349 
    350 TEST_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 (at) google.com"),
    360       kSampleRegisterStartResponse));
    361 
    362   EXPECT_TRUE(SuccessfulResponseToURL(
    363       GURL("http://10.0.0.8:6006/privet/register?"
    364            "action=getClaimToken&user=example (at) 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 (at) google.com"),
    377       kSampleRegisterGetClaimTokenResponse));
    378 }
    379 
    380 TEST_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 (at) 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 (at) google.com"),
    401       kSampleRegisterStartResponse));
    402 }
    403 
    404 TEST_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 (at) 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 (at) google.com"),
    425       kSampleRegisterErrorPermanent));
    426 }
    427 
    428 TEST_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