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
LoadTestJson(const std::string & test_json,base::DictionaryValue * dictionary)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 {
CodeWithReasonweave::privet::__anon4a4f52fe0111::CodeWithReason54 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
operator <<(std::ostream & stream,const CodeWithReason & error)60 std::ostream& operator<<(std::ostream& stream, const CodeWithReason& error) {
61 return stream << "{" << error.code << ", " << error.reason << "}";
62 }
63
IsEqualError(const CodeWithReason & expected,const base::DictionaryValue & dictionary)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.
StripDebugErrorDetails(const std::string & path_to_error_object,const base::DictionaryValue & value)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:
PrivetHandlerTest()92 PrivetHandlerTest() {}
93
94 protected:
SetUp()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
HandleRequest(const std::string & api,const base::DictionaryValue * input)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
HandleRequest(const std::string & api,const std::string & json_input)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
HandleUnknownRequest(const std::string & api)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
GetResponse() const128 const base::DictionaryValue& GetResponse() const { return output_; }
GetResponseCount() const129 int GetResponseCount() const { return response_count_; }
130
SetNoWifiAndGcd()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:
HandlerCallback(int status,const base::DictionaryValue & output)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
HandlerNoFound(int status,const base::DictionaryValue &)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
TEST_F(PrivetHandlerTest,UnknownApi)175 TEST_F(PrivetHandlerTest, UnknownApi) {
176 HandleUnknownRequest("/privet/foo");
177 }
178
TEST_F(PrivetHandlerTest,InvalidFormat)179 TEST_F(PrivetHandlerTest, InvalidFormat) {
180 auth_header_ = "";
181 EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidFormat"),
182 HandleRequest("/privet/info", nullptr));
183 }
184
TEST_F(PrivetHandlerTest,MissingAuth)185 TEST_F(PrivetHandlerTest, MissingAuth) {
186 auth_header_ = "";
187 EXPECT_PRED2(IsEqualError, CodeWithReason(401, "missingAuthorization"),
188 HandleRequest("/privet/info", "{}"));
189 }
190
TEST_F(PrivetHandlerTest,InvalidAuth)191 TEST_F(PrivetHandlerTest, InvalidAuth) {
192 auth_header_ = "foo";
193 EXPECT_PRED2(IsEqualError, CodeWithReason(401, "invalidAuthorization"),
194 HandleRequest("/privet/info", "{}"));
195 }
196
TEST_F(PrivetHandlerTest,ExpiredAuth)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
TEST_F(PrivetHandlerTest,InvalidAuthScope)207 TEST_F(PrivetHandlerTest, InvalidAuthScope) {
208 EXPECT_PRED2(IsEqualError, CodeWithReason(403, "invalidAuthorizationScope"),
209 HandleRequest("/privet/v3/setup/start", "{}"));
210 }
211
TEST_F(PrivetHandlerTest,InfoMinimal)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
TEST_F(PrivetHandlerTest,Info)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
TEST_F(PrivetHandlerTest,PairingStartInvalidParams)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
TEST_F(PrivetHandlerTest,PairingStart)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
TEST_F(PrivetHandlerTest,PairingConfirm)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
TEST_F(PrivetHandlerTest,PairingCancel)345 TEST_F(PrivetHandlerTest, PairingCancel) {
346 EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/pairing/cancel",
347 "{'sessionId': 'testSession'}"));
348 }
349
TEST_F(PrivetHandlerTest,AuthErrorNoType)350 TEST_F(PrivetHandlerTest, AuthErrorNoType) {
351 EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidAuthMode"),
352 HandleRequest("/privet/v3/auth", "{}"));
353 }
354
TEST_F(PrivetHandlerTest,AuthErrorInvalidType)355 TEST_F(PrivetHandlerTest, AuthErrorInvalidType) {
356 EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidAuthMode"),
357 HandleRequest("/privet/v3/auth", "{'mode':'unknown'}"));
358 }
359
TEST_F(PrivetHandlerTest,AuthErrorNoScope)360 TEST_F(PrivetHandlerTest, AuthErrorNoScope) {
361 EXPECT_PRED2(IsEqualError, CodeWithReason(400, "invalidRequestedScope"),
362 HandleRequest("/privet/v3/auth", "{'mode':'anonymous'}"));
363 }
364
TEST_F(PrivetHandlerTest,AuthErrorInvalidScope)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
TEST_F(PrivetHandlerTest,AuthErrorAccessDenied)372 TEST_F(PrivetHandlerTest, AuthErrorAccessDenied) {
373 EXPECT_PRED2(IsEqualError, CodeWithReason(403, "accessDenied"),
374 HandleRequest("/privet/v3/auth",
375 "{'mode':'anonymous','requestedScope':'owner'}"));
376 }
377
TEST_F(PrivetHandlerTest,AuthErrorInvalidAuthCode)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
TEST_F(PrivetHandlerTest,AuthAnonymous)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
TEST_F(PrivetHandlerTest,AuthPairing)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
TEST_F(PrivetHandlerTest,AuthLocalAuto)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
TEST_F(PrivetHandlerTest,AuthLocal)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
TEST_F(PrivetHandlerTest,AuthLocalHighScope)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:
SetUp()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
TEST_F(PrivetHandlerSetupTest,StatusEmpty)494 TEST_F(PrivetHandlerSetupTest, StatusEmpty) {
495 SetNoWifiAndGcd();
496 EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/setup/status", "{}"));
497 }
498
TEST_F(PrivetHandlerSetupTest,StatusWifi)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
TEST_F(PrivetHandlerSetupTest,StatusWifiError)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
TEST_F(PrivetHandlerSetupTest,StatusGcd)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
TEST_F(PrivetHandlerSetupTest,StatusGcdError)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
TEST_F(PrivetHandlerSetupTest,SetupNameDescriptionLocation)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
TEST_F(PrivetHandlerSetupTest,InvalidParams)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
TEST_F(PrivetHandlerSetupTest,WifiSetupUnavailable)589 TEST_F(PrivetHandlerSetupTest, WifiSetupUnavailable) {
590 SetNoWifiAndGcd();
591 EXPECT_PRED2(IsEqualError, CodeWithReason(400, "setupUnavailable"),
592 HandleRequest("/privet/v3/setup/start", "{'wifi': {}}"));
593 }
594
TEST_F(PrivetHandlerSetupTest,WifiSetup)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
TEST_F(PrivetHandlerSetupTest,GcdSetupUnavailable)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
TEST_F(PrivetHandlerSetupTest,GcdSetup)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
TEST_F(PrivetHandlerSetupTest,GcdSetupAsMaster)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
TEST_F(PrivetHandlerTestWithAuth,ClaimAccessControl)675 TEST_F(PrivetHandlerTestWithAuth, ClaimAccessControl) {
676 EXPECT_JSON_EQ("{'clientToken': 'RootClientAuthToken'}",
677 HandleRequest("/privet/v3/accessControl/claim", "{}"));
678 }
679
TEST_F(PrivetHandlerTestWithAuth,ConfirmAccessControl)680 TEST_F(PrivetHandlerTestWithAuth, ConfirmAccessControl) {
681 EXPECT_JSON_EQ("{}",
682 HandleRequest("/privet/v3/accessControl/confirm",
683 "{'clientToken': 'DerivedClientAuthToken'}"));
684 }
685
TEST_F(PrivetHandlerTestWithAuth,State)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
TEST_F(PrivetHandlerTestWithAuth,CommandsDefs)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
TEST_F(PrivetHandlerTestWithAuth,Traits)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
TEST_F(PrivetHandlerTestWithAuth,Components)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
TEST_F(PrivetHandlerTestWithAuth,ComponentsWithFiltersAndPaths)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
TEST_F(PrivetHandlerTestWithAuth,CommandsExecute)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
TEST_F(PrivetHandlerTestWithAuth,CommandsStatus)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
TEST_F(PrivetHandlerTestWithAuth,CommandsCancel)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
TEST_F(PrivetHandlerTestWithAuth,CommandsList)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
TEST_F(PrivetHandlerCheckForUpdatesTest,NoInput)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
TEST_F(PrivetHandlerCheckForUpdatesTest,AlreadyChanged)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
TEST_F(PrivetHandlerCheckForUpdatesTest,LongPollCommands)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
TEST_F(PrivetHandlerCheckForUpdatesTest,LongPollTraits)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
TEST_F(PrivetHandlerCheckForUpdatesTest,LongPollState)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
TEST_F(PrivetHandlerCheckForUpdatesTest,LongPollComponents)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
TEST_F(PrivetHandlerCheckForUpdatesTest,LongPollIgnoreTraits)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
TEST_F(PrivetHandlerCheckForUpdatesTest,LongPollIgnoreState)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
TEST_F(PrivetHandlerCheckForUpdatesTest,InstantTimeout)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
TEST_F(PrivetHandlerCheckForUpdatesTest,UserTimeout)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
TEST_F(PrivetHandlerCheckForUpdatesTest,ServerTimeout)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
TEST_F(PrivetHandlerCheckForUpdatesTest,VeryShortServerTimeout)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
TEST_F(PrivetHandlerCheckForUpdatesTest,ServerAndUserTimeout)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
TEST_F(PrivetHandlerCheckForUpdatesTest,ChangeBeforeTimeout)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