• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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