Home | History | Annotate | Download | only in privet
      1 // Copyright 2015 The Weave 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 "src/privet/privet_handler.h"
      6 
      7 #include <set>
      8 #include <string>
      9 #include <utility>
     10 
     11 #include <base/bind.h>
     12 #include <base/json/json_reader.h>
     13 #include <base/json/json_writer.h>
     14 #include <base/strings/string_util.h>
     15 #include <base/values.h>
     16 #include <gmock/gmock.h>
     17 #include <gtest/gtest.h>
     18 #include <weave/test/unittest_utils.h>
     19 
     20 #include "src/privet/constants.h"
     21 #include "src/privet/mock_delegates.h"
     22 #include "src/test/mock_clock.h"
     23 
     24 using testing::_;
     25 using testing::DoAll;
     26 using testing::Invoke;
     27 using testing::Return;
     28 using testing::SetArgPointee;
     29 using testing::SaveArg;
     30 using testing::WithArgs;
     31 
     32 namespace weave {
     33 namespace privet {
     34 
     35 namespace {
     36 
     37 void LoadTestJson(const std::string& test_json,
     38                   base::DictionaryValue* dictionary) {
     39   std::string json = test_json;
     40   base::ReplaceChars(json, "'", "\"", &json);
     41   int error = 0;
     42   std::string message;
     43   std::unique_ptr<base::Value> value(
     44       base::JSONReader::ReadAndReturnError(json, base::JSON_PARSE_RFC, &error,
     45                                            &message)
     46           .release());
     47   EXPECT_TRUE(value.get()) << "\nError: " << message << "\n" << json;
     48   base::DictionaryValue* dictionary_ptr = nullptr;
     49   if (value->GetAsDictionary(&dictionary_ptr))
     50     dictionary->MergeDictionary(dictionary_ptr);
     51 }
     52 
     53 struct CodeWithReason {
     54   CodeWithReason(int code_in, const std::string& reason_in)
     55       : code(code_in), reason(reason_in) {}
     56   int code;
     57   std::string reason;
     58 };
     59 
     60 std::ostream& operator<<(std::ostream& stream, const CodeWithReason& error) {
     61   return stream << "{" << error.code << ", " << error.reason << "}";
     62 }
     63 
     64 bool IsEqualError(const CodeWithReason& expected,
     65                   const base::DictionaryValue& dictionary) {
     66   std::string reason;
     67   int code = 0;
     68   return dictionary.GetInteger("error.http_status", &code) &&
     69          code == expected.code && dictionary.GetString("error.code", &reason) &&
     70          reason == expected.reason;
     71 }
     72 
     73 // Some error sections in response JSON objects contained debugging information
     74 // which is of no interest for this test. So, remove the debug info from the
     75 // JSON before running validation logic on it.
     76 std::unique_ptr<base::DictionaryValue> StripDebugErrorDetails(
     77     const std::string& path_to_error_object,
     78     const base::DictionaryValue& value) {
     79   std::unique_ptr<base::DictionaryValue> result{value.DeepCopy()};
     80   base::DictionaryValue* error_dict = nullptr;
     81   EXPECT_TRUE(result->GetDictionary(path_to_error_object, &error_dict));
     82   scoped_ptr<base::Value> dummy;
     83   error_dict->RemovePath("error.debugInfo", &dummy);
     84   error_dict->RemovePath("error.message", &dummy);
     85   return result;
     86 }
     87 
     88 }  // namespace
     89 
     90 class PrivetHandlerTest : public testing::Test {
     91  public:
     92   PrivetHandlerTest() {}
     93 
     94  protected:
     95   void SetUp() override {
     96     EXPECT_CALL(clock_, Now())
     97         .WillRepeatedly(Return(base::Time::FromTimeT(1410000001)));
     98 
     99     auth_header_ = "Privet anonymous";
    100     handler_.reset(
    101         new PrivetHandler(&cloud_, &device_, &security_, &wifi_, &clock_));
    102   }
    103 
    104   const base::DictionaryValue& HandleRequest(
    105       const std::string& api,
    106       const base::DictionaryValue* input) {
    107     output_.Clear();
    108     handler_->HandleRequest(api, auth_header_, input,
    109                             base::Bind(&PrivetHandlerTest::HandlerCallback,
    110                                        base::Unretained(this)));
    111     return output_;
    112   }
    113 
    114   const base::DictionaryValue& HandleRequest(const std::string& api,
    115                                              const std::string& json_input) {
    116     base::DictionaryValue dictionary;
    117     LoadTestJson(json_input, &dictionary);
    118     return HandleRequest(api, &dictionary);
    119   }
    120 
    121   void HandleUnknownRequest(const std::string& api) {
    122     output_.Clear();
    123     base::DictionaryValue dictionary;
    124     handler_->HandleRequest(api, auth_header_, &dictionary,
    125                             base::Bind(&PrivetHandlerTest::HandlerNoFound));
    126   }
    127 
    128   const base::DictionaryValue& GetResponse() const { return output_; }
    129   int GetResponseCount() const { return response_count_; }
    130 
    131   void SetNoWifiAndGcd() {
    132     handler_.reset(
    133         new PrivetHandler(&cloud_, &device_, &security_, nullptr, &clock_));
    134     EXPECT_CALL(cloud_, GetCloudId()).WillRepeatedly(Return(""));
    135     EXPECT_CALL(cloud_, GetConnectionState())
    136         .WillRepeatedly(ReturnRef(gcd_disabled_state_));
    137     auto set_error = [](const std::string&, const std::string&,
    138                         ErrorPtr* error) {
    139       Error::AddTo(error, FROM_HERE, "setupUnavailable", "");
    140     };
    141     EXPECT_CALL(cloud_, Setup(_, _, _))
    142         .WillRepeatedly(DoAll(Invoke(set_error), Return(false)));
    143   }
    144 
    145   test::MockClock clock_;
    146   testing::StrictMock<MockCloudDelegate> cloud_;
    147   testing::StrictMock<MockDeviceDelegate> device_;
    148   testing::StrictMock<MockSecurityDelegate> security_;
    149   testing::StrictMock<MockWifiDelegate> wifi_;
    150   std::string auth_header_;
    151 
    152  private:
    153   void HandlerCallback(int status, const base::DictionaryValue& output) {
    154     output_.Clear();
    155     ++response_count_;
    156     output_.MergeDictionary(&output);
    157     if (!output_.HasKey("error")) {
    158       EXPECT_EQ(200, status);
    159       return;
    160     }
    161     EXPECT_NE(200, status);
    162     output_.SetInteger("error.http_status", status);
    163   }
    164 
    165   static void HandlerNoFound(int status, const base::DictionaryValue&) {
    166     EXPECT_EQ(404, status);
    167   }
    168 
    169   std::unique_ptr<PrivetHandler> handler_;
    170   base::DictionaryValue output_;
    171   int response_count_{0};
    172   ConnectionState gcd_disabled_state_{ConnectionState::kDisabled};
    173 };
    174 
    175 TEST_F(PrivetHandlerTest, UnknownApi) {
    176   HandleUnknownRequest("/privet/foo");
    177 }
    178 
    179 TEST_F(PrivetHandlerTest, InvalidFormat) {
    180   auth_header_ = "";
    181   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidFormat"),
    182                HandleRequest("/privet/info", nullptr));
    183 }
    184 
    185 TEST_F(PrivetHandlerTest, MissingAuth) {
    186   auth_header_ = "";
    187   EXPECT_PRED2(IsEqualError, CodeWithReason(401, "missingAuthorization"),
    188                HandleRequest("/privet/info", "{}"));
    189 }
    190 
    191 TEST_F(PrivetHandlerTest, InvalidAuth) {
    192   auth_header_ = "foo";
    193   EXPECT_PRED2(IsEqualError, CodeWithReason(401, "invalidAuthorization"),
    194                HandleRequest("/privet/info", "{}"));
    195 }
    196 
    197 TEST_F(PrivetHandlerTest, ExpiredAuth) {
    198   auth_header_ = "Privet 123";
    199   EXPECT_CALL(security_, ParseAccessToken(_, _, _))
    200       .WillRepeatedly(WithArgs<2>(Invoke([](ErrorPtr* error) {
    201         return Error::AddTo(error, FROM_HERE, "authorizationExpired", "");
    202       })));
    203   EXPECT_PRED2(IsEqualError, CodeWithReason(403, "authorizationExpired"),
    204                HandleRequest("/privet/info", "{}"));
    205 }
    206 
    207 TEST_F(PrivetHandlerTest, InvalidAuthScope) {
    208   EXPECT_PRED2(IsEqualError, CodeWithReason(403, "invalidAuthorizationScope"),
    209                HandleRequest("/privet/v3/setup/start", "{}"));
    210 }
    211 
    212 TEST_F(PrivetHandlerTest, InfoMinimal) {
    213   SetNoWifiAndGcd();
    214   EXPECT_CALL(security_, GetPairingTypes())
    215       .WillRepeatedly(Return(std::set<PairingType>{}));
    216   EXPECT_CALL(security_, GetCryptoTypes())
    217       .WillRepeatedly(Return(std::set<CryptoType>{}));
    218   EXPECT_CALL(security_, GetAuthTypes())
    219       .WillRepeatedly(Return(std::set<AuthType>{}));
    220 
    221   const char kExpected[] = R"({
    222     'version': '3.0',
    223     'id': 'TestId',
    224     'name': 'TestDevice',
    225     'services': [ "developmentBoard" ],
    226     'modelManifestId': "ABMID",
    227     'basicModelManifest': {
    228       'uiDeviceKind': 'developmentBoard',
    229       'oemName': 'Chromium',
    230       'modelName': 'Brillo'
    231     },
    232     'endpoints': {
    233       'httpPort': 0,
    234       'httpUpdatesPort': 0,
    235       'httpsPort': 0,
    236       'httpsUpdatesPort': 0
    237     },
    238     'authentication': {
    239       'anonymousMaxScope': 'user',
    240       'mode': [
    241       ],
    242       'pairing': [
    243       ],
    244       'crypto': [
    245       ]
    246     },
    247     'gcd': {
    248       'id': '',
    249       'status': 'disabled'
    250     },
    251     'time': 1410000001000.0,
    252     'sessionId': 'SessionId'
    253   })";
    254   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/info", "{}"));
    255 }
    256 
    257 TEST_F(PrivetHandlerTest, Info) {
    258   EXPECT_CALL(cloud_, GetDescription())
    259       .WillRepeatedly(Return("TestDescription"));
    260   EXPECT_CALL(cloud_, GetLocation()).WillRepeatedly(Return("TestLocation"));
    261   EXPECT_CALL(device_, GetHttpEnpoint())
    262       .WillRepeatedly(Return(std::make_pair(80, 10080)));
    263   EXPECT_CALL(device_, GetHttpsEnpoint())
    264       .WillRepeatedly(Return(std::make_pair(443, 10443)));
    265   EXPECT_CALL(wifi_, GetHostedSsid())
    266       .WillRepeatedly(Return("Test_device.BBABCLAprv"));
    267 
    268   const char kExpected[] = R"({
    269     'version': '3.0',
    270     'id': 'TestId',
    271     'name': 'TestDevice',
    272     'description': 'TestDescription',
    273     'location': 'TestLocation',
    274     'services': [ "developmentBoard" ],
    275     'modelManifestId': "ABMID",
    276     'basicModelManifest': {
    277       'uiDeviceKind': 'developmentBoard',
    278       'oemName': 'Chromium',
    279       'modelName': 'Brillo'
    280     },
    281     'endpoints': {
    282       'httpPort': 80,
    283       'httpUpdatesPort': 10080,
    284       'httpsPort': 443,
    285       'httpsUpdatesPort': 10443
    286     },
    287     'authentication': {
    288       'anonymousMaxScope': 'none',
    289       'mode': [
    290         'anonymous',
    291         'pairing',
    292         'local'
    293       ],
    294       'pairing': [
    295         'pinCode',
    296         'embeddedCode'
    297       ],
    298       'crypto': [
    299         'p224_spake2'
    300       ]
    301     },
    302     'wifi': {
    303       'capabilities': [
    304         '2.4GHz'
    305       ],
    306       'ssid': 'TestSsid',
    307       'hostedSsid': 'Test_device.BBABCLAprv',
    308       'status': 'offline'
    309     },
    310     'gcd': {
    311       'id': 'TestCloudId',
    312       'status': 'online'
    313     },
    314     'time': 1410000001000.0,
    315     'sessionId': 'SessionId'
    316   })";
    317   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/info", "{}"));
    318 }
    319 
    320 TEST_F(PrivetHandlerTest, PairingStartInvalidParams) {
    321   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidParams"),
    322                HandleRequest("/privet/v3/pairing/start",
    323                              "{'pairing':'embeddedCode','crypto':'crypto'}"));
    324 
    325   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidParams"),
    326                HandleRequest("/privet/v3/pairing/start",
    327                              "{'pairing':'code','crypto':'p224_spake2'}"));
    328 }
    329 
    330 TEST_F(PrivetHandlerTest, PairingStart) {
    331   EXPECT_JSON_EQ(
    332       "{'deviceCommitment': 'testCommitment', 'sessionId': 'testSession'}",
    333       HandleRequest("/privet/v3/pairing/start",
    334                     "{'pairing': 'embeddedCode', 'crypto': 'p224_spake2'}"));
    335 }
    336 
    337 TEST_F(PrivetHandlerTest, PairingConfirm) {
    338   EXPECT_JSON_EQ(
    339       "{'certFingerprint':'testFingerprint','certSignature':'testSignature'}",
    340       HandleRequest(
    341           "/privet/v3/pairing/confirm",
    342           "{'sessionId':'testSession','clientCommitment':'testCommitment'}"));
    343 }
    344 
    345 TEST_F(PrivetHandlerTest, PairingCancel) {
    346   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/pairing/cancel",
    347                                      "{'sessionId': 'testSession'}"));
    348 }
    349 
    350 TEST_F(PrivetHandlerTest, AuthErrorNoType) {
    351   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidAuthMode"),
    352                HandleRequest("/privet/v3/auth", "{}"));
    353 }
    354 
    355 TEST_F(PrivetHandlerTest, AuthErrorInvalidType) {
    356   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidAuthMode"),
    357                HandleRequest("/privet/v3/auth", "{'mode':'unknown'}"));
    358 }
    359 
    360 TEST_F(PrivetHandlerTest, AuthErrorNoScope) {
    361   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidRequestedScope"),
    362                HandleRequest("/privet/v3/auth", "{'mode':'anonymous'}"));
    363 }
    364 
    365 TEST_F(PrivetHandlerTest, AuthErrorInvalidScope) {
    366   EXPECT_PRED2(
    367       IsEqualError, CodeWithReason(400, "invalidRequestedScope"),
    368       HandleRequest("/privet/v3/auth",
    369                     "{'mode':'anonymous','requestedScope':'unknown'}"));
    370 }
    371 
    372 TEST_F(PrivetHandlerTest, AuthErrorAccessDenied) {
    373   EXPECT_PRED2(IsEqualError, CodeWithReason(403, "accessDenied"),
    374                HandleRequest("/privet/v3/auth",
    375                              "{'mode':'anonymous','requestedScope':'owner'}"));
    376 }
    377 
    378 TEST_F(PrivetHandlerTest, AuthErrorInvalidAuthCode) {
    379   auto set_error = [](ErrorPtr* error) {
    380     return Error::AddTo(error, FROM_HERE, "invalidAuthCode", "");
    381   };
    382   EXPECT_CALL(security_, CreateAccessToken(_, "testToken", _, _, _, _, _))
    383       .WillRepeatedly(WithArgs<6>(Invoke(set_error)));
    384   const char kInput[] = R"({
    385     'mode': 'pairing',
    386     'requestedScope': 'user',
    387     'authCode': 'testToken'
    388   })";
    389   EXPECT_PRED2(IsEqualError, CodeWithReason(403, "invalidAuthCode"),
    390                HandleRequest("/privet/v3/auth", kInput));
    391 }
    392 
    393 TEST_F(PrivetHandlerTest, AuthAnonymous) {
    394   const char kExpected[] = R"({
    395     'accessToken': 'GuestAccessToken',
    396     'expiresIn': 15,
    397     'scope': 'viewer',
    398     'tokenType': 'Privet'
    399   })";
    400   EXPECT_JSON_EQ(kExpected,
    401                  HandleRequest("/privet/v3/auth",
    402                                "{'mode':'anonymous','requestedScope':'auto'}"));
    403 }
    404 
    405 TEST_F(PrivetHandlerTest, AuthPairing) {
    406   EXPECT_CALL(security_, CreateAccessToken(_, _, _, _, _, _, _))
    407       .WillRepeatedly(DoAll(SetArgPointee<3>("OwnerAccessToken"),
    408                             SetArgPointee<4>(AuthScope::kOwner),
    409                             SetArgPointee<5>(base::TimeDelta::FromSeconds(15)),
    410                             Return(true)));
    411   const char kInput[] = R"({
    412     'mode': 'pairing',
    413     'requestedScope': 'owner',
    414     'authCode': 'testToken'
    415   })";
    416   const char kExpected[] = R"({
    417     'accessToken': 'OwnerAccessToken',
    418     'expiresIn': 15,
    419     'scope': 'owner',
    420     'tokenType': 'Privet'
    421   })";
    422   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/auth", kInput));
    423 }
    424 
    425 TEST_F(PrivetHandlerTest, AuthLocalAuto) {
    426   EXPECT_CALL(security_, CreateAccessToken(_, _, _, _, _, _, _))
    427       .WillRepeatedly(DoAll(SetArgPointee<3>("UserAccessToken"),
    428                             SetArgPointee<4>(AuthScope::kUser),
    429                             SetArgPointee<5>(base::TimeDelta::FromSeconds(15)),
    430                             Return(true)));
    431   const char kInput[] = R"({
    432     'mode': 'local',
    433     'requestedScope': 'auto',
    434     'authCode': 'localAuthToken'
    435   })";
    436   const char kExpected[] = R"({
    437     'accessToken': 'UserAccessToken',
    438     'expiresIn': 15,
    439     'scope': 'user',
    440     'tokenType': 'Privet'
    441   })";
    442   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/auth", kInput));
    443 }
    444 
    445 TEST_F(PrivetHandlerTest, AuthLocal) {
    446   EXPECT_CALL(security_, CreateAccessToken(_, _, _, _, _, _, _))
    447       .WillRepeatedly(DoAll(SetArgPointee<3>("ManagerAccessToken"),
    448                             SetArgPointee<4>(AuthScope::kManager),
    449                             SetArgPointee<5>(base::TimeDelta::FromSeconds(15)),
    450                             Return(true)));
    451   const char kInput[] = R"({
    452     'mode': 'local',
    453     'requestedScope': 'manager',
    454     'authCode': 'localAuthToken'
    455   })";
    456   const char kExpected[] = R"({
    457     'accessToken': 'ManagerAccessToken',
    458     'expiresIn': 15,
    459     'scope': 'manager',
    460     'tokenType': 'Privet'
    461   })";
    462   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/auth", kInput));
    463 }
    464 
    465 TEST_F(PrivetHandlerTest, AuthLocalHighScope) {
    466   EXPECT_CALL(security_, CreateAccessToken(_, _, _, _, _, _, _))
    467       .WillRepeatedly(DoAll(SetArgPointee<3>("UserAccessToken"),
    468                             SetArgPointee<4>(AuthScope::kUser),
    469                             SetArgPointee<5>(base::TimeDelta::FromSeconds(1)),
    470                             Return(true)));
    471   const char kInput[] = R"({
    472     'mode': 'local',
    473     'requestedScope': 'manager',
    474     'authCode': 'localAuthToken'
    475   })";
    476   EXPECT_PRED2(IsEqualError, CodeWithReason(403, "accessDenied"),
    477                HandleRequest("/privet/v3/auth", kInput));
    478 }
    479 
    480 class PrivetHandlerTestWithAuth : public PrivetHandlerTest {
    481  public:
    482   void SetUp() override {
    483     PrivetHandlerTest::SetUp();
    484     auth_header_ = "Privet 123";
    485     EXPECT_CALL(security_, ParseAccessToken(_, _, _))
    486         .WillRepeatedly(DoAll(
    487             SetArgPointee<1>(UserInfo{AuthScope::kOwner, TestUserId{"1"}}),
    488             Return(true)));
    489   }
    490 };
    491 
    492 class PrivetHandlerSetupTest : public PrivetHandlerTestWithAuth {};
    493 
    494 TEST_F(PrivetHandlerSetupTest, StatusEmpty) {
    495   SetNoWifiAndGcd();
    496   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/setup/status", "{}"));
    497 }
    498 
    499 TEST_F(PrivetHandlerSetupTest, StatusWifi) {
    500   wifi_.setup_state_ = SetupState{SetupState::kSuccess};
    501 
    502   const char kExpected[] = R"({
    503     'wifi': {
    504         'ssid': 'TestSsid',
    505         'status': 'success'
    506      }
    507   })";
    508   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/setup/status", "{}"));
    509 }
    510 
    511 TEST_F(PrivetHandlerSetupTest, StatusWifiError) {
    512   ErrorPtr error;
    513   Error::AddTo(&error, FROM_HERE, "invalidPassphrase", "");
    514   wifi_.setup_state_ = SetupState{std::move(error)};
    515 
    516   const char kExpected[] = R"({
    517     'wifi': {
    518         'status': 'error',
    519         'error': {
    520           'code': 'invalidPassphrase'
    521         }
    522      }
    523   })";
    524   EXPECT_JSON_EQ(kExpected,
    525                  *StripDebugErrorDetails(
    526                      "wifi", HandleRequest("/privet/v3/setup/status", "{}")));
    527 }
    528 
    529 TEST_F(PrivetHandlerSetupTest, StatusGcd) {
    530   cloud_.setup_state_ = SetupState{SetupState::kSuccess};
    531 
    532   const char kExpected[] = R"({
    533     'gcd': {
    534         'id': 'TestCloudId',
    535         'status': 'success'
    536      }
    537   })";
    538   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/setup/status", "{}"));
    539 }
    540 
    541 TEST_F(PrivetHandlerSetupTest, StatusGcdError) {
    542   ErrorPtr error;
    543   Error::AddTo(&error, FROM_HERE, "invalidTicket", "");
    544   cloud_.setup_state_ = SetupState{std::move(error)};
    545 
    546   const char kExpected[] = R"({
    547     'gcd': {
    548         'status': 'error',
    549         'error': {
    550           'code': 'invalidTicket'
    551         }
    552      }
    553   })";
    554   EXPECT_JSON_EQ(kExpected,
    555                  *StripDebugErrorDetails(
    556                      "gcd", HandleRequest("/privet/v3/setup/status", "{}")));
    557 }
    558 
    559 TEST_F(PrivetHandlerSetupTest, SetupNameDescriptionLocation) {
    560   EXPECT_CALL(cloud_,
    561               UpdateDeviceInfo("testName", "testDescription", "testLocation"))
    562       .Times(1);
    563   const char kInput[] = R"({
    564     'name': 'testName',
    565     'description': 'testDescription',
    566     'location': 'testLocation'
    567   })";
    568   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/setup/start", kInput));
    569 }
    570 
    571 TEST_F(PrivetHandlerSetupTest, InvalidParams) {
    572   const char kInputWifi[] = R"({
    573     'wifi': {
    574       'ssid': ''
    575     }
    576   })";
    577   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidParams"),
    578                HandleRequest("/privet/v3/setup/start", kInputWifi));
    579 
    580   const char kInputRegistration[] = R"({
    581     'gcd': {
    582       'ticketId': ''
    583     }
    584   })";
    585   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidParams"),
    586                HandleRequest("/privet/v3/setup/start", kInputRegistration));
    587 }
    588 
    589 TEST_F(PrivetHandlerSetupTest, WifiSetupUnavailable) {
    590   SetNoWifiAndGcd();
    591   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "setupUnavailable"),
    592                HandleRequest("/privet/v3/setup/start", "{'wifi': {}}"));
    593 }
    594 
    595 TEST_F(PrivetHandlerSetupTest, WifiSetup) {
    596   const char kInput[] = R"({
    597     'wifi': {
    598       'ssid': 'testSsid',
    599       'passphrase': 'testPass'
    600     }
    601   })";
    602   auto set_error = [](const std::string&, const std::string&, ErrorPtr* error) {
    603     return Error::AddTo(error, FROM_HERE, "deviceBusy", "");
    604   };
    605   EXPECT_CALL(wifi_, ConfigureCredentials(_, _, _)).WillOnce(Invoke(set_error));
    606   EXPECT_PRED2(IsEqualError, CodeWithReason(503, "deviceBusy"),
    607                HandleRequest("/privet/v3/setup/start", kInput));
    608 
    609   const char kExpected[] = R"({
    610     'wifi': {
    611       'status': 'inProgress'
    612     }
    613   })";
    614   wifi_.setup_state_ = SetupState{SetupState::kInProgress};
    615   EXPECT_CALL(wifi_, ConfigureCredentials("testSsid", "testPass", _))
    616       .WillOnce(Return(true));
    617   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/setup/start", kInput));
    618 }
    619 
    620 TEST_F(PrivetHandlerSetupTest, GcdSetupUnavailable) {
    621   SetNoWifiAndGcd();
    622   const char kInput[] = R"({
    623     'gcd': {
    624       'ticketId': 'testTicket',
    625       'user': 'testUser'
    626     }
    627   })";
    628 
    629   EXPECT_PRED2(IsEqualError, CodeWithReason(400, "setupUnavailable"),
    630                HandleRequest("/privet/v3/setup/start", kInput));
    631 }
    632 
    633 TEST_F(PrivetHandlerSetupTest, GcdSetup) {
    634   const char kInput[] = R"({
    635     'gcd': {
    636       'ticketId': 'testTicket',
    637       'user': 'testUser'
    638     }
    639   })";
    640 
    641   auto set_error = [](const std::string&, const std::string&, ErrorPtr* error) {
    642     return Error::AddTo(error, FROM_HERE, "deviceBusy", "");
    643   };
    644   EXPECT_CALL(cloud_, Setup(_, _, _)).WillOnce(Invoke(set_error));
    645   EXPECT_PRED2(IsEqualError, CodeWithReason(503, "deviceBusy"),
    646                HandleRequest("/privet/v3/setup/start", kInput));
    647 
    648   const char kExpected[] = R"({
    649     'gcd': {
    650       'status': 'inProgress'
    651     }
    652   })";
    653   cloud_.setup_state_ = SetupState{SetupState::kInProgress};
    654   EXPECT_CALL(cloud_, Setup("testTicket", "testUser", _))
    655       .WillOnce(Return(true));
    656   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/setup/start", kInput));
    657 }
    658 
    659 TEST_F(PrivetHandlerSetupTest, GcdSetupAsMaster) {
    660   EXPECT_CALL(security_, ParseAccessToken(_, _, _))
    661       .WillRepeatedly(DoAll(
    662           SetArgPointee<1>(UserInfo{AuthScope::kManager, TestUserId{"1"}}),
    663           Return(true)));
    664   const char kInput[] = R"({
    665     'gcd': {
    666       'ticketId': 'testTicket',
    667       'user': 'testUser'
    668     }
    669   })";
    670 
    671   EXPECT_PRED2(IsEqualError, CodeWithReason(403, "invalidAuthorizationScope"),
    672                HandleRequest("/privet/v3/setup/start", kInput));
    673 }
    674 
    675 TEST_F(PrivetHandlerTestWithAuth, ClaimAccessControl) {
    676   EXPECT_JSON_EQ("{'clientToken': 'RootClientAuthToken'}",
    677                  HandleRequest("/privet/v3/accessControl/claim", "{}"));
    678 }
    679 
    680 TEST_F(PrivetHandlerTestWithAuth, ConfirmAccessControl) {
    681   EXPECT_JSON_EQ("{}",
    682                  HandleRequest("/privet/v3/accessControl/confirm",
    683                                "{'clientToken': 'DerivedClientAuthToken'}"));
    684 }
    685 
    686 TEST_F(PrivetHandlerTestWithAuth, State) {
    687   EXPECT_JSON_EQ("{'state': {'test': {}}, 'fingerprint': '1'}",
    688                  HandleRequest("/privet/v3/state", "{}"));
    689 
    690   cloud_.NotifyOnStateChanged();
    691 
    692   EXPECT_JSON_EQ("{'state': {'test': {}}, 'fingerprint': '2'}",
    693                  HandleRequest("/privet/v3/state", "{}"));
    694 }
    695 
    696 TEST_F(PrivetHandlerTestWithAuth, CommandsDefs) {
    697   EXPECT_JSON_EQ("{'commands': {'test':{}}, 'fingerprint': '1'}",
    698                  HandleRequest("/privet/v3/commandDefs", "{}"));
    699 
    700   cloud_.NotifyOnTraitDefsChanged();
    701 
    702   EXPECT_JSON_EQ("{'commands': {'test':{}}, 'fingerprint': '2'}",
    703                  HandleRequest("/privet/v3/commandDefs", "{}"));
    704 }
    705 
    706 TEST_F(PrivetHandlerTestWithAuth, Traits) {
    707   EXPECT_JSON_EQ("{'traits': {'test': {}}, 'fingerprint': '1'}",
    708                  HandleRequest("/privet/v3/traits", "{}"));
    709 
    710   cloud_.NotifyOnTraitDefsChanged();
    711 
    712   EXPECT_JSON_EQ("{'traits': {'test': {}}, 'fingerprint': '2'}",
    713                  HandleRequest("/privet/v3/traits", "{}"));
    714 }
    715 
    716 TEST_F(PrivetHandlerTestWithAuth, Components) {
    717   EXPECT_JSON_EQ("{'components': {'test': {}}, 'fingerprint': '1'}",
    718                  HandleRequest("/privet/v3/components", "{}"));
    719 
    720   cloud_.NotifyOnComponentTreeChanged();
    721 
    722   EXPECT_JSON_EQ("{'components': {'test': {}}, 'fingerprint': '2'}",
    723                  HandleRequest("/privet/v3/components", "{}"));
    724 
    725   // State change will also change the components fingerprint.
    726   cloud_.NotifyOnStateChanged();
    727 
    728   EXPECT_JSON_EQ("{'components': {'test': {}}, 'fingerprint': '3'}",
    729                  HandleRequest("/privet/v3/components", "{}"));
    730 }
    731 
    732 TEST_F(PrivetHandlerTestWithAuth, ComponentsWithFiltersAndPaths) {
    733   const char kComponents[] = R"({
    734     "comp1": {
    735       "traits": ["a", "b"],
    736       "state": {
    737         "a" : {
    738           "prop": 1
    739         }
    740       },
    741       "components": {
    742         "comp2": {
    743           "traits": ["c"],
    744           "components": {
    745             "comp4": {
    746               "traits": ["d"]
    747             }
    748           }
    749         },
    750         "comp3": {
    751           "traits": ["e"]
    752         }
    753       }
    754     }
    755   })";
    756   base::DictionaryValue components;
    757   LoadTestJson(kComponents, &components);
    758   EXPECT_CALL(cloud_, FindComponent(_, _)).WillRepeatedly(Return(nullptr));
    759   EXPECT_CALL(cloud_, GetComponents()).WillRepeatedly(ReturnRef(components));
    760   const char kExpected1[] = R"({
    761     "components": {
    762       "comp1": {
    763         "state": {
    764           "a" : {
    765             "prop": 1
    766           }
    767         }
    768       }
    769     },
    770     "fingerprint": "1"
    771   })";
    772   EXPECT_JSON_EQ(kExpected1, HandleRequest("/privet/v3/components",
    773                                            "{'filter':['state']}"));
    774 
    775   const char kExpected2[] = R"({
    776     "components": {
    777       "comp1": {
    778         "traits": ["a", "b"]
    779       }
    780     },
    781     "fingerprint": "1"
    782   })";
    783   EXPECT_JSON_EQ(kExpected2, HandleRequest("/privet/v3/components",
    784                                            "{'filter':['traits']}"));
    785 
    786   const char kExpected3[] = R"({
    787     "components": {
    788       "comp1": {
    789         "components": {
    790           "comp2": {
    791             "components": {
    792               "comp4": {}
    793             }
    794           },
    795           "comp3": {}
    796         }
    797       }
    798     },
    799     "fingerprint": "1"
    800   })";
    801   EXPECT_JSON_EQ(kExpected3, HandleRequest("/privet/v3/components",
    802                                            "{'filter':['components']}"));
    803 
    804   const char kExpected4[] = R"({
    805     "components": {
    806       "comp1": {
    807         "traits": ["a", "b"],
    808         "state": {
    809           "a" : {
    810             "prop": 1
    811           }
    812         },
    813         "components": {
    814           "comp2": {
    815             "traits": ["c"],
    816             "components": {
    817               "comp4": {
    818                 "traits": ["d"]
    819               }
    820             }
    821           },
    822           "comp3": {
    823             "traits": ["e"]
    824           }
    825         }
    826       }
    827     },
    828     "fingerprint": "1"
    829   })";
    830   EXPECT_JSON_EQ(kExpected4,
    831                  HandleRequest("/privet/v3/components",
    832                                "{'filter':['traits', 'components', 'state']}"));
    833 
    834   const base::DictionaryValue* comp2 = nullptr;
    835   ASSERT_TRUE(components.GetDictionary("comp1.components.comp2", &comp2));
    836   EXPECT_CALL(cloud_, FindComponent("comp1.comp2", _)).WillOnce(Return(comp2));
    837 
    838   const char kExpected5[] = R"({
    839     "components": {
    840       "comp2": {
    841         "traits": ["c"],
    842         "components": {
    843           "comp4": {
    844             "traits": ["d"]
    845           }
    846         }
    847       }
    848     },
    849     "fingerprint": "1"
    850   })";
    851   EXPECT_JSON_EQ(
    852       kExpected5,
    853       HandleRequest(
    854           "/privet/v3/components",
    855           "{'path':'comp1.comp2', 'filter':['traits', 'components']}"));
    856 
    857   auto error_handler = [](ErrorPtr* error) -> const base::DictionaryValue* {
    858     return Error::AddTo(error, FROM_HERE, "componentNotFound", "");
    859   };
    860   EXPECT_CALL(cloud_, FindComponent("comp7", _))
    861       .WillOnce(WithArgs<1>(Invoke(error_handler)));
    862 
    863   EXPECT_PRED2(
    864       IsEqualError, CodeWithReason(500, "componentNotFound"),
    865       HandleRequest("/privet/v3/components",
    866                     "{'path':'comp7', 'filter':['traits', 'components']}"));
    867 }
    868 
    869 TEST_F(PrivetHandlerTestWithAuth, CommandsExecute) {
    870   const char kInput[] = "{'name': 'test'}";
    871   base::DictionaryValue command;
    872   LoadTestJson(kInput, &command);
    873   LoadTestJson("{'id':'5'}", &command);
    874   EXPECT_CALL(cloud_, AddCommand(_, _, _))
    875       .WillOnce(WithArgs<2>(Invoke(
    876           [&command](const CloudDelegate::CommandDoneCallback& callback) {
    877             callback.Run(command, nullptr);
    878           })));
    879 
    880   EXPECT_JSON_EQ("{'name':'test', 'id':'5'}",
    881                  HandleRequest("/privet/v3/commands/execute", kInput));
    882 }
    883 
    884 TEST_F(PrivetHandlerTestWithAuth, CommandsStatus) {
    885   const char kInput[] = "{'id': '5'}";
    886   base::DictionaryValue command;
    887   LoadTestJson(kInput, &command);
    888   LoadTestJson("{'name':'test'}", &command);
    889   EXPECT_CALL(cloud_, GetCommand(_, _, _))
    890       .WillOnce(WithArgs<2>(Invoke(
    891           [&command](const CloudDelegate::CommandDoneCallback& callback) {
    892             callback.Run(command, nullptr);
    893           })));
    894 
    895   EXPECT_JSON_EQ("{'name':'test', 'id':'5'}",
    896                  HandleRequest("/privet/v3/commands/status", kInput));
    897 
    898   ErrorPtr error;
    899   Error::AddTo(&error, FROM_HERE, "notFound", "");
    900   EXPECT_CALL(cloud_, GetCommand(_, _, _))
    901       .WillOnce(WithArgs<2>(
    902           Invoke([&error](const CloudDelegate::CommandDoneCallback& callback) {
    903             callback.Run({}, std::move(error));
    904           })));
    905 
    906   EXPECT_PRED2(IsEqualError, CodeWithReason(404, "notFound"),
    907                HandleRequest("/privet/v3/commands/status", "{'id': '15'}"));
    908 }
    909 
    910 TEST_F(PrivetHandlerTestWithAuth, CommandsCancel) {
    911   const char kExpected[] = "{'id': '5', 'name':'test', 'state':'cancelled'}";
    912   base::DictionaryValue command;
    913   LoadTestJson(kExpected, &command);
    914   EXPECT_CALL(cloud_, CancelCommand(_, _, _))
    915       .WillOnce(WithArgs<2>(Invoke(
    916           [&command](const CloudDelegate::CommandDoneCallback& callback) {
    917             callback.Run(command, nullptr);
    918           })));
    919 
    920   EXPECT_JSON_EQ(kExpected,
    921                  HandleRequest("/privet/v3/commands/cancel", "{'id': '8'}"));
    922 
    923   ErrorPtr error;
    924   Error::AddTo(&error, FROM_HERE, "notFound", "");
    925   EXPECT_CALL(cloud_, CancelCommand(_, _, _))
    926       .WillOnce(WithArgs<2>(
    927           Invoke([&error](const CloudDelegate::CommandDoneCallback& callback) {
    928             callback.Run({}, std::move(error));
    929           })));
    930 
    931   EXPECT_PRED2(IsEqualError, CodeWithReason(404, "notFound"),
    932                HandleRequest("/privet/v3/commands/cancel", "{'id': '11'}"));
    933 }
    934 
    935 TEST_F(PrivetHandlerTestWithAuth, CommandsList) {
    936   const char kExpected[] = R"({
    937     'commands' : [
    938         {'id':'5', 'state':'cancelled'},
    939         {'id':'15', 'state':'inProgress'}
    940      ]})";
    941 
    942   base::DictionaryValue commands;
    943   LoadTestJson(kExpected, &commands);
    944 
    945   EXPECT_CALL(cloud_, ListCommands(_, _))
    946       .WillOnce(WithArgs<1>(Invoke(
    947           [&commands](const CloudDelegate::CommandDoneCallback& callback) {
    948             callback.Run(commands, nullptr);
    949           })));
    950 
    951   EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/commands/list", "{}"));
    952 }
    953 
    954 class PrivetHandlerCheckForUpdatesTest : public PrivetHandlerTestWithAuth {};
    955 
    956 TEST_F(PrivetHandlerCheckForUpdatesTest, NoInput) {
    957   EXPECT_CALL(device_, GetHttpRequestTimeout())
    958       .WillOnce(Return(base::TimeDelta::Max()));
    959   cloud_.NotifyOnTraitDefsChanged();
    960   cloud_.NotifyOnComponentTreeChanged();
    961   cloud_.NotifyOnStateChanged();
    962   const char kInput[] = "{}";
    963   const char kExpected[] = R"({
    964    'commandsFingerprint': '2',
    965    'stateFingerprint': '2',
    966    'traitsFingerprint': '2',
    967    'componentsFingerprint': '3'
    968   })";
    969   EXPECT_JSON_EQ(kExpected,
    970                  HandleRequest("/privet/v3/checkForUpdates", kInput));
    971   EXPECT_EQ(1, GetResponseCount());
    972 }
    973 
    974 TEST_F(PrivetHandlerCheckForUpdatesTest, AlreadyChanged) {
    975   EXPECT_CALL(device_, GetHttpRequestTimeout())
    976       .WillOnce(Return(base::TimeDelta::Max()));
    977   cloud_.NotifyOnTraitDefsChanged();
    978   cloud_.NotifyOnComponentTreeChanged();
    979   cloud_.NotifyOnStateChanged();
    980   const char kInput[] = R"({
    981    'commandsFingerprint': '1',
    982    'stateFingerprint': '1',
    983    'traitsFingerprint': '1',
    984    'componentsFingerprint': '1'
    985   })";
    986   const char kExpected[] = R"({
    987    'commandsFingerprint': '2',
    988    'stateFingerprint': '2',
    989    'traitsFingerprint': '2',
    990    'componentsFingerprint': '3'
    991   })";
    992   EXPECT_JSON_EQ(kExpected,
    993                  HandleRequest("/privet/v3/checkForUpdates", kInput));
    994   EXPECT_EQ(1, GetResponseCount());
    995 }
    996 
    997 TEST_F(PrivetHandlerCheckForUpdatesTest, LongPollCommands) {
    998   EXPECT_CALL(device_, GetHttpRequestTimeout())
    999       .WillOnce(Return(base::TimeDelta::Max()));
   1000   const char kInput[] = R"({
   1001    'commandsFingerprint': '1',
   1002    'stateFingerprint': '1',
   1003    'traitsFingerprint': '1',
   1004    'componentsFingerprint': '1'
   1005   })";
   1006   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   1007   EXPECT_EQ(0, GetResponseCount());
   1008   cloud_.NotifyOnTraitDefsChanged();
   1009   EXPECT_EQ(1, GetResponseCount());
   1010   const char kExpected[] = R"({
   1011    'commandsFingerprint': '2',
   1012    'stateFingerprint': '1',
   1013    'traitsFingerprint': '2',
   1014    'componentsFingerprint': '1'
   1015   })";
   1016   EXPECT_JSON_EQ(kExpected, GetResponse());
   1017 }
   1018 
   1019 TEST_F(PrivetHandlerCheckForUpdatesTest, LongPollTraits) {
   1020   EXPECT_CALL(device_, GetHttpRequestTimeout())
   1021       .WillOnce(Return(base::TimeDelta::Max()));
   1022   const char kInput[] = R"({
   1023    'commandsFingerprint': '1',
   1024    'stateFingerprint': '1',
   1025    'traitsFingerprint': '1',
   1026    'componentsFingerprint': '1'
   1027   })";
   1028   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   1029   EXPECT_EQ(0, GetResponseCount());
   1030   cloud_.NotifyOnTraitDefsChanged();
   1031   EXPECT_EQ(1, GetResponseCount());
   1032   const char kExpected[] = R"({
   1033    'commandsFingerprint': '2',
   1034    'stateFingerprint': '1',
   1035    'traitsFingerprint': '2',
   1036    'componentsFingerprint': '1'
   1037   })";
   1038   EXPECT_JSON_EQ(kExpected, GetResponse());
   1039 }
   1040 
   1041 TEST_F(PrivetHandlerCheckForUpdatesTest, LongPollState) {
   1042   EXPECT_CALL(device_, GetHttpRequestTimeout())
   1043       .WillOnce(Return(base::TimeDelta::Max()));
   1044   const char kInput[] = R"({
   1045    'commandsFingerprint': '1',
   1046    'stateFingerprint': '1',
   1047    'traitsFingerprint': '1',
   1048    'componentsFingerprint': '1'
   1049   })";
   1050   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   1051   EXPECT_EQ(0, GetResponseCount());
   1052   cloud_.NotifyOnStateChanged();
   1053   EXPECT_EQ(1, GetResponseCount());
   1054   const char kExpected[] = R"({
   1055    'commandsFingerprint': '1',
   1056    'stateFingerprint': '2',
   1057    'traitsFingerprint': '1',
   1058    'componentsFingerprint': '2'
   1059   })";
   1060   EXPECT_JSON_EQ(kExpected, GetResponse());
   1061 }
   1062 
   1063 TEST_F(PrivetHandlerCheckForUpdatesTest, LongPollComponents) {
   1064   EXPECT_CALL(device_, GetHttpRequestTimeout())
   1065       .WillOnce(Return(base::TimeDelta::Max()));
   1066   const char kInput[] = R"({
   1067    'commandsFingerprint': '1',
   1068    'stateFingerprint': '1',
   1069    'traitsFingerprint': '1',
   1070    'componentsFingerprint': '1'
   1071   })";
   1072   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   1073   EXPECT_EQ(0, GetResponseCount());
   1074   cloud_.NotifyOnComponentTreeChanged();
   1075   EXPECT_EQ(1, GetResponseCount());
   1076   const char kExpected[] = R"({
   1077    'commandsFingerprint': '1',
   1078    'stateFingerprint': '1',
   1079    'traitsFingerprint': '1',
   1080    'componentsFingerprint': '2'
   1081   })";
   1082   EXPECT_JSON_EQ(kExpected, GetResponse());
   1083 }
   1084 
   1085 TEST_F(PrivetHandlerCheckForUpdatesTest, LongPollIgnoreTraits) {
   1086   EXPECT_CALL(device_, GetHttpRequestTimeout())
   1087       .WillOnce(Return(base::TimeDelta::Max()));
   1088   const char kInput[] = R"({
   1089    'stateFingerprint': '1',
   1090    'componentsFingerprint': '1'
   1091   })";
   1092   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   1093   EXPECT_EQ(0, GetResponseCount());
   1094   cloud_.NotifyOnTraitDefsChanged();
   1095   EXPECT_EQ(0, GetResponseCount());
   1096   cloud_.NotifyOnComponentTreeChanged();
   1097   EXPECT_EQ(1, GetResponseCount());
   1098   const char kExpected[] = R"({
   1099    'commandsFingerprint': '2',
   1100    'stateFingerprint': '1',
   1101    'traitsFingerprint': '2',
   1102    'componentsFingerprint': '2'
   1103   })";
   1104   EXPECT_JSON_EQ(kExpected, GetResponse());
   1105 }
   1106 
   1107 TEST_F(PrivetHandlerCheckForUpdatesTest, LongPollIgnoreState) {
   1108   EXPECT_CALL(device_, GetHttpRequestTimeout())
   1109       .WillOnce(Return(base::TimeDelta::Max()));
   1110   const char kInput[] = R"({
   1111    'commandsFingerprint': '1',
   1112    'traitsFingerprint': '1'
   1113   })";
   1114   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   1115   EXPECT_EQ(0, GetResponseCount());
   1116   cloud_.NotifyOnStateChanged();
   1117   EXPECT_EQ(0, GetResponseCount());
   1118   cloud_.NotifyOnComponentTreeChanged();
   1119   EXPECT_EQ(0, GetResponseCount());
   1120   cloud_.NotifyOnTraitDefsChanged();
   1121   EXPECT_EQ(1, GetResponseCount());
   1122   const char kExpected[] = R"({
   1123    'commandsFingerprint': '2',
   1124    'stateFingerprint': '2',
   1125    'traitsFingerprint': '2',
   1126    'componentsFingerprint': '3'
   1127   })";
   1128   EXPECT_JSON_EQ(kExpected, GetResponse());
   1129 }
   1130 
   1131 TEST_F(PrivetHandlerCheckForUpdatesTest, InstantTimeout) {
   1132   EXPECT_CALL(device_, GetHttpRequestTimeout())
   1133       .WillOnce(Return(base::TimeDelta::Max()));
   1134   const char kInput[] = R"({
   1135    'commandsFingerprint': '1',
   1136    'stateFingerprint': '1',
   1137    'traitsFingerprint': '1',
   1138    'componentsFingerprint': '1',
   1139    'waitTimeout': 0
   1140   })";
   1141   const char kExpected[] = R"({
   1142    'commandsFingerprint': '1',
   1143    'stateFingerprint': '1',
   1144    'traitsFingerprint': '1',
   1145    'componentsFingerprint': '1'
   1146   })";
   1147   EXPECT_JSON_EQ(kExpected,
   1148                  HandleRequest("/privet/v3/checkForUpdates", kInput));
   1149 }
   1150 
   1151 TEST_F(PrivetHandlerCheckForUpdatesTest, UserTimeout) {
   1152   EXPECT_CALL(device_, GetHttpRequestTimeout())
   1153       .WillOnce(Return(base::TimeDelta::Max()));
   1154   const char kInput[] = R"({
   1155    'commandsFingerprint': '1',
   1156    'stateFingerprint': '1',
   1157    'traitsFingerprint': '1',
   1158    'componentsFingerprint': '1',
   1159    'waitTimeout': 3
   1160   })";
   1161   base::Closure callback;
   1162   EXPECT_CALL(device_, PostDelayedTask(_, _, base::TimeDelta::FromSeconds(3)))
   1163       .WillOnce(SaveArg<1>(&callback));
   1164   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   1165   EXPECT_EQ(0, GetResponseCount());
   1166   callback.Run();
   1167   EXPECT_EQ(1, GetResponseCount());
   1168   const char kExpected[] = R"({
   1169    'commandsFingerprint': '1',
   1170    'stateFingerprint': '1',
   1171    'traitsFingerprint': '1',
   1172    'componentsFingerprint': '1'
   1173   })";
   1174   EXPECT_JSON_EQ(kExpected, GetResponse());
   1175 }
   1176 
   1177 TEST_F(PrivetHandlerCheckForUpdatesTest, ServerTimeout) {
   1178   EXPECT_CALL(device_, GetHttpRequestTimeout())
   1179       .WillOnce(Return(base::TimeDelta::FromMinutes(1)));
   1180   const char kInput[] = R"({
   1181    'commandsFingerprint': '1',
   1182    'stateFingerprint': '1',
   1183    'traitsFingerprint': '1',
   1184    'componentsFingerprint': '1'
   1185   })";
   1186   base::Closure callback;
   1187   EXPECT_CALL(device_, PostDelayedTask(_, _, base::TimeDelta::FromSeconds(50)))
   1188       .WillOnce(SaveArg<1>(&callback));
   1189   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   1190   EXPECT_EQ(0, GetResponseCount());
   1191   callback.Run();
   1192   EXPECT_EQ(1, GetResponseCount());
   1193   const char kExpected[] = R"({
   1194    'commandsFingerprint': '1',
   1195    'stateFingerprint': '1',
   1196    'traitsFingerprint': '1',
   1197    'componentsFingerprint': '1'
   1198   })";
   1199   EXPECT_JSON_EQ(kExpected, GetResponse());
   1200 }
   1201 
   1202 TEST_F(PrivetHandlerCheckForUpdatesTest, VeryShortServerTimeout) {
   1203   EXPECT_CALL(device_, GetHttpRequestTimeout())
   1204       .WillOnce(Return(base::TimeDelta::FromSeconds(5)));
   1205   const char kInput[] = R"({
   1206    'commandsFingerprint': '1',
   1207    'stateFingerprint': '1',
   1208    'traitsFingerprint': '1',
   1209    'componentsFingerprint': '1'
   1210   })";
   1211   EXPECT_JSON_EQ(kInput, HandleRequest("/privet/v3/checkForUpdates", kInput));
   1212   EXPECT_EQ(1, GetResponseCount());
   1213 }
   1214 
   1215 TEST_F(PrivetHandlerCheckForUpdatesTest, ServerAndUserTimeout) {
   1216   EXPECT_CALL(device_, GetHttpRequestTimeout())
   1217       .WillOnce(Return(base::TimeDelta::FromMinutes(1)));
   1218   const char kInput[] = R"({
   1219    'commandsFingerprint': '1',
   1220    'stateFingerprint': '1',
   1221    'traitsFingerprint': '1',
   1222    'componentsFingerprint': '1',
   1223    'waitTimeout': 10
   1224   })";
   1225   base::Closure callback;
   1226   EXPECT_CALL(device_, PostDelayedTask(_, _, base::TimeDelta::FromSeconds(10)))
   1227       .WillOnce(SaveArg<1>(&callback));
   1228   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   1229   EXPECT_EQ(0, GetResponseCount());
   1230   callback.Run();
   1231   EXPECT_EQ(1, GetResponseCount());
   1232   const char kExpected[] = R"({
   1233    'commandsFingerprint': '1',
   1234    'stateFingerprint': '1',
   1235    'traitsFingerprint': '1',
   1236    'componentsFingerprint': '1'
   1237   })";
   1238   EXPECT_JSON_EQ(kExpected, GetResponse());
   1239 }
   1240 
   1241 TEST_F(PrivetHandlerCheckForUpdatesTest, ChangeBeforeTimeout) {
   1242   EXPECT_CALL(device_, GetHttpRequestTimeout())
   1243       .WillOnce(Return(base::TimeDelta::Max()));
   1244   const char kInput[] = R"({
   1245    'commandsFingerprint': '1',
   1246    'stateFingerprint': '1',
   1247    'traitsFingerprint': '1',
   1248    'componentsFingerprint': '1',
   1249    'waitTimeout': 10
   1250   })";
   1251   base::Closure callback;
   1252   EXPECT_CALL(device_, PostDelayedTask(_, _, base::TimeDelta::FromSeconds(10)))
   1253       .WillOnce(SaveArg<1>(&callback));
   1254   EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   1255   EXPECT_EQ(0, GetResponseCount());
   1256   cloud_.NotifyOnTraitDefsChanged();
   1257   EXPECT_EQ(1, GetResponseCount());
   1258   const char kExpected[] = R"({
   1259    'commandsFingerprint': '2',
   1260    'stateFingerprint': '1',
   1261    'traitsFingerprint': '2',
   1262    'componentsFingerprint': '1'
   1263   })";
   1264   EXPECT_JSON_EQ(kExpected, GetResponse());
   1265   callback.Run();
   1266   EXPECT_EQ(1, GetResponseCount());
   1267 }
   1268 
   1269 }  // namespace privet
   1270 }  // namespace weave
   1271