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