blob: d03ae158fdfce90ca7f0ccaa37dc7a7cf656f341 [file] [log] [blame]
Vitaly Buka4615e0d2015-10-14 15:35:12 -07001// Copyright 2015 The Weave Authors. All rights reserved.
Alex Vakulenko8e34d392014-04-29 11:02:56 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Stefan Sauer2d16dfa2015-09-25 17:08:35 +02005#include "src/device_registration_info.h"
Alex Deymof6cbe322014-11-10 19:55:35 -08006
Alex Vakulenko8e34d392014-04-29 11:02:56 -07007#include <base/json/json_reader.h>
Alex Vakulenko9e2f8cd2015-04-07 16:28:09 -07008#include <base/json/json_writer.h>
Alex Vakulenko8e34d392014-04-29 11:02:56 -07009#include <base/values.h>
10#include <gtest/gtest.h>
Vitaly Bukaff1d1862015-10-07 20:40:36 -070011#include <weave/provider/test/fake_task_runner.h>
Vitaly Buka1e363672015-09-25 14:01:16 -070012#include <weave/provider/test/mock_config_store.h>
13#include <weave/provider/test/mock_http_client.h>
Alex Vakulenko8a05beb2015-11-24 17:13:20 -080014#include <weave/test/unittest_utils.h>
Alex Vakulenko8e34d392014-04-29 11:02:56 -070015
Stefan Sauer2d16dfa2015-09-25 17:08:35 +020016#include "src/bind_lambda.h"
Alex Vakulenkod91d6252015-12-05 17:14:39 -080017#include "src/component_manager_impl.h"
Stefan Sauer2d16dfa2015-09-25 17:08:35 +020018#include "src/http_constants.h"
Alex Vakulenko8e34d392014-04-29 11:02:56 -070019
Vitaly Buka6b61e572015-08-05 23:16:13 -070020using testing::_;
Vitaly Buka10206182015-08-05 11:17:43 -070021using testing::AtLeast;
22using testing::Invoke;
23using testing::InvokeWithoutArgs;
24using testing::Mock;
Vitaly Buka6b61e572015-08-05 23:16:13 -070025using testing::Return;
Vitaly Buka10206182015-08-05 11:17:43 -070026using testing::ReturnRef;
27using testing::ReturnRefOfCopy;
Vitaly Bukac11a17d2015-08-15 10:36:10 -070028using testing::SaveArg;
Vitaly Buka6b61e572015-08-05 23:16:13 -070029using testing::StrictMock;
Vitaly Buka10206182015-08-05 11:17:43 -070030using testing::WithArgs;
Vitaly Buka6b61e572015-08-05 23:16:13 -070031
Vitaly Bukab6f015a2015-07-09 14:59:23 -070032namespace weave {
Vitaly Buka32005de2015-05-01 12:33:31 -070033
Vitaly Buka0f6b2ec2015-08-20 15:35:19 -070034using test::CreateDictionaryValue;
35using test::CreateValue;
Vitaly Buka1e363672015-09-25 14:01:16 -070036using provider::test::MockHttpClient;
37using provider::test::MockHttpClientResponse;
38using provider::HttpClient;
Alex Vakulenkocca20932014-08-20 17:35:12 -070039
Alex Vakulenko8e34d392014-04-29 11:02:56 -070040namespace {
Alex Vakulenko8e34d392014-04-29 11:02:56 -070041
42namespace test_data {
43
Vitaly Bukaa647c852015-07-06 14:51:01 -070044const char kServiceURL[] = "http://gcd.server.com/";
45const char kOAuthURL[] = "http://oauth.server.com/";
46const char kApiKey[] = "GOadRdTf9FERf0k4w6EFOof56fUJ3kFDdFL3d7f";
47const char kClientId[] =
48 "123543821385-sfjkjshdkjhfk234sdfsdfkskd"
49 "fkjh7f.apps.googleusercontent.com";
50const char kClientSecret[] = "5sdGdGlfolGlrFKfdFlgP6FG";
Vitaly Buka6a9d0e22015-12-06 17:49:11 -080051const char kCloudId[] = "4a7ea2d1-b331-1e1f-b206-e863c7635196";
Vitaly Bukaa647c852015-07-06 14:51:01 -070052const char kClaimTicketId[] = "RTcUE";
53const char kAccessToken[] =
54 "ya29.1.AADtN_V-dLUM-sVZ0qVjG9Dxm5NgdS9J"
55 "Mx_JLUqhC9bED_YFjzHZtYt65ZzXCS35NMAeaVZ"
56 "Dei530-w0yE2urpQ";
57const char kRefreshToken[] =
58 "1/zQmxR6PKNvhcxf9SjXUrCjcmCrcqRKXctc6cp"
59 "1nI-GQ";
60const char kRobotAccountAuthCode[] =
61 "4/Mf_ujEhPejVhOq-OxW9F5cSOnWzx."
62 "YgciVjTYGscRshQV0ieZDAqiTIjMigI";
63const char kRobotAccountEmail[] =
64 "6ed0b3f54f9bd619b942f4ad2441c252@"
65 "clouddevices.gserviceaccount.com";
Vitaly Buka10206182015-08-05 11:17:43 -070066
Alex Vakulenko8e34d392014-04-29 11:02:56 -070067} // namespace test_data
68
Vitaly Buka10206182015-08-05 11:17:43 -070069std::string GetFormField(const std::string& data, const std::string& name) {
70 EXPECT_FALSE(data.empty());
Vitaly Buka7d556392015-08-13 20:06:48 -070071 for (const auto& i : WebParamsDecode(data)) {
Vitaly Buka10206182015-08-05 11:17:43 -070072 if (i.first == name)
73 return i.second;
Alex Vakulenko8e34d392014-04-29 11:02:56 -070074 }
Vitaly Buka10206182015-08-05 11:17:43 -070075 return {};
Alex Vakulenko8e34d392014-04-29 11:02:56 -070076}
77
Vitaly Buka17280372015-10-10 17:11:47 -070078std::unique_ptr<HttpClient::Response> ReplyWithJson(int status_code,
79 const base::Value& json) {
Vitaly Buka10206182015-08-05 11:17:43 -070080 std::string text;
81 base::JSONWriter::WriteWithOptions(
82 json, base::JSONWriter::OPTIONS_PRETTY_PRINT, &text);
83
Vitaly Buka17280372015-10-10 17:11:47 -070084 std::unique_ptr<MockHttpClientResponse> response{
85 new StrictMock<MockHttpClientResponse>};
Vitaly Buka10206182015-08-05 11:17:43 -070086 EXPECT_CALL(*response, GetStatusCode())
87 .Times(AtLeast(1))
88 .WillRepeatedly(Return(status_code));
89 EXPECT_CALL(*response, GetContentType())
90 .Times(AtLeast(1))
Vitaly Buka815b6032015-08-06 11:06:25 -070091 .WillRepeatedly(Return(http::kJsonUtf8));
Vitaly Buka10206182015-08-05 11:17:43 -070092 EXPECT_CALL(*response, GetData())
93 .Times(AtLeast(1))
Vitaly Buka4774df22015-10-09 12:36:22 -070094 .WillRepeatedly(Return(text));
Vitaly Buka17280372015-10-10 17:11:47 -070095 return std::move(response);
Nathan Bullock24d189f2015-02-26 13:09:18 -050096}
97
Vitaly Buka10206182015-08-05 11:17:43 -070098std::pair<std::string, std::string> GetAuthHeader() {
Vitaly Buka815b6032015-08-06 11:06:25 -070099 return {http::kAuthorization,
100 std::string("Bearer ") + test_data::kAccessToken};
101}
102
103std::pair<std::string, std::string> GetJsonHeader() {
104 return {http::kContentType, http::kJsonUtf8};
105}
106
107std::pair<std::string, std::string> GetFormHeader() {
108 return {http::kContentType, http::kWwwFormUrlEncoded};
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700109}
110
111} // anonymous namespace
112
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700113class DeviceRegistrationInfoTest : public ::testing::Test {
114 protected:
Alex Vakulenko5a9e7182014-08-11 15:59:58 -0700115 void SetUp() override {
Vitaly Buka666b43e2015-12-08 16:35:47 -0800116 dev_reg_.reset(new DeviceRegistrationInfo{&config_, &component_manager_,
117 &task_runner_, &http_client_,
118 nullptr, nullptr});
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700119
Vitaly Bukac11a17d2015-08-15 10:36:10 -0700120 ReloadDefaults();
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700121 }
122
Vitaly Bukac11a17d2015-08-15 10:36:10 -0700123 void ReloadDefaults() {
124 EXPECT_CALL(config_store_, LoadDefaults(_))
125 .WillOnce(Invoke([](Settings* settings) {
126 settings->client_id = test_data::kClientId;
127 settings->client_secret = test_data::kClientSecret;
128 settings->api_key = test_data::kApiKey;
Vitaly Bukaa05eadb2015-09-29 16:38:24 -0700129 settings->oem_name = "Coffee Pot Maker";
130 settings->model_name = "Pot v1";
Vitaly Bukac11a17d2015-08-15 10:36:10 -0700131 settings->name = "Coffee Pot";
132 settings->description = "Easy to clean";
133 settings->location = "Kitchen";
Vitaly Bukab624bc42015-09-29 19:13:55 -0700134 settings->local_anonymous_access_role = AuthScope::kViewer;
Vitaly Bukac11a17d2015-08-15 10:36:10 -0700135 settings->model_id = "AAAAA";
136 settings->oauth_url = test_data::kOAuthURL;
137 settings->service_url = test_data::kServiceURL;
138 return true;
139 }));
Vitaly Buka666b43e2015-12-08 16:35:47 -0800140 config_.Load();
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700141 dev_reg_->Start();
142 }
143
Vitaly Bukac11a17d2015-08-15 10:36:10 -0700144 void ReloadSettings() {
145 base::DictionaryValue dict;
146 dict.SetString("refresh_token", test_data::kRefreshToken);
Vitaly Buka6a9d0e22015-12-06 17:49:11 -0800147 dict.SetString("cloud_id", test_data::kCloudId);
Vitaly Bukac11a17d2015-08-15 10:36:10 -0700148 dict.SetString("robot_account", test_data::kRobotAccountEmail);
149 std::string json_string;
150 base::JSONWriter::WriteWithOptions(
151 dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_string);
152 EXPECT_CALL(config_store_, LoadSettings()).WillOnce(Return(json_string));
153 ReloadDefaults();
154 }
155
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700156 void PublishCommands(const base::ListValue& commands) {
Vitaly Buka74763422015-10-11 00:39:52 -0700157 dev_reg_->PublishCommands(commands, nullptr);
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700158 }
159
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700160 bool RefreshAccessToken(ErrorPtr* error) const {
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700161 bool succeeded = false;
Vitaly Buka74763422015-10-11 00:39:52 -0700162 auto callback = [&succeeded, &error](ErrorPtr in_error) {
163 if (error) {
Vitaly Bukaf7f52d42015-10-10 22:43:55 -0700164 *error = std::move(in_error);
Vitaly Buka74763422015-10-11 00:39:52 -0700165 return;
166 }
167 succeeded = true;
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700168 };
Vitaly Buka74763422015-10-11 00:39:52 -0700169 dev_reg_->RefreshAccessToken(base::Bind(callback));
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700170 return succeeded;
171 }
172
Vitaly Bukaa647c852015-07-06 14:51:01 -0700173 void SetAccessToken() { dev_reg_->access_token_ = test_data::kAccessToken; }
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700174
Vitaly Bukac3c6dab2015-10-01 19:41:02 -0700175 GcdState GetGcdState() const { return dev_reg_->GetGcdState(); }
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700176
Vitaly Buka672634b2015-11-20 09:49:30 -0800177 bool HaveRegistrationCredentials() const {
178 return dev_reg_->HaveRegistrationCredentials();
179 }
180
Vitaly Bukaff1d1862015-10-07 20:40:36 -0700181 provider::test::FakeTaskRunner task_runner_;
Vitaly Buka1e363672015-09-25 14:01:16 -0700182 provider::test::MockConfigStore config_store_;
Vitaly Buka10206182015-08-05 11:17:43 -0700183 StrictMock<MockHttpClient> http_client_;
Alex Vakulenko45109442014-07-29 11:07:10 -0700184 base::DictionaryValue data_;
Vitaly Buka666b43e2015-12-08 16:35:47 -0800185 Config config_{&config_store_};
Alex Vakulenko45109442014-07-29 11:07:10 -0700186 std::unique_ptr<DeviceRegistrationInfo> dev_reg_;
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800187 ComponentManagerImpl component_manager_;
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700188};
189
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700190TEST_F(DeviceRegistrationInfoTest, GetServiceURL) {
Alex Vakulenko45109442014-07-29 11:07:10 -0700191 EXPECT_EQ(test_data::kServiceURL, dev_reg_->GetServiceURL());
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700192 std::string url = test_data::kServiceURL;
193 url += "registrationTickets";
Alex Vakulenko45109442014-07-29 11:07:10 -0700194 EXPECT_EQ(url, dev_reg_->GetServiceURL("registrationTickets"));
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700195 url += "?key=";
196 url += test_data::kApiKey;
Vitaly Bukaa647c852015-07-06 14:51:01 -0700197 EXPECT_EQ(url, dev_reg_->GetServiceURL("registrationTickets",
198 {{"key", test_data::kApiKey}}));
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700199 url += "&restart=true";
Vitaly Bukaa647c852015-07-06 14:51:01 -0700200 EXPECT_EQ(url, dev_reg_->GetServiceURL(
201 "registrationTickets",
202 {
203 {"key", test_data::kApiKey}, {"restart", "true"},
204 }));
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700205}
206
207TEST_F(DeviceRegistrationInfoTest, GetOAuthURL) {
Alex Vakulenko45109442014-07-29 11:07:10 -0700208 EXPECT_EQ(test_data::kOAuthURL, dev_reg_->GetOAuthURL());
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700209 std::string url = test_data::kOAuthURL;
Vitaly Buka88272d52015-11-17 17:04:01 -0800210 url += "auth?redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&";
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700211 url += "response_type=code&";
212 url += "client_id=";
213 url += test_data::kClientId;
Vitaly Bukaa647c852015-07-06 14:51:01 -0700214 EXPECT_EQ(url, dev_reg_->GetOAuthURL(
Vitaly Bukac27390d2015-11-19 14:42:35 -0800215 "auth", {{"redirect_uri", "urn:ietf:wg:oauth:2.0:oob"},
216 {"response_type", "code"},
217 {"client_id", test_data::kClientId}}));
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700218}
219
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700220TEST_F(DeviceRegistrationInfoTest, HaveRegistrationCredentials) {
Vitaly Buka672634b2015-11-20 09:49:30 -0800221 EXPECT_FALSE(HaveRegistrationCredentials());
Vitaly Bukac11a17d2015-08-15 10:36:10 -0700222 ReloadSettings();
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700223
Vitaly Buka1a42e142015-10-10 18:15:15 -0700224 EXPECT_CALL(
225 http_client_,
226 SendRequest(HttpClient::Method::kPost, dev_reg_->GetOAuthURL("token"),
Vitaly Buka74763422015-10-11 00:39:52 -0700227 HttpClient::Headers{GetFormHeader()}, _, _))
Vitaly Buka17280372015-10-10 17:11:47 -0700228 .WillOnce(WithArgs<3, 4>(Invoke([](
229 const std::string& data,
Vitaly Buka74763422015-10-11 00:39:52 -0700230 const HttpClient::SendRequestCallback& callback) {
Vitaly Buka10206182015-08-05 11:17:43 -0700231 EXPECT_EQ("refresh_token", GetFormField(data, "grant_type"));
232 EXPECT_EQ(test_data::kRefreshToken,
233 GetFormField(data, "refresh_token"));
234 EXPECT_EQ(test_data::kClientId, GetFormField(data, "client_id"));
235 EXPECT_EQ(test_data::kClientSecret,
236 GetFormField(data, "client_secret"));
237
238 base::DictionaryValue json;
239 json.SetString("access_token", test_data::kAccessToken);
240 json.SetInteger("expires_in", 3600);
241
Vitaly Buka74763422015-10-11 00:39:52 -0700242 callback.Run(ReplyWithJson(200, json), nullptr);
Vitaly Buka10206182015-08-05 11:17:43 -0700243 })));
244
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700245 EXPECT_TRUE(RefreshAccessToken(nullptr));
Vitaly Buka672634b2015-11-20 09:49:30 -0800246 EXPECT_TRUE(HaveRegistrationCredentials());
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700247}
248
Nathan Bullock24d189f2015-02-26 13:09:18 -0500249TEST_F(DeviceRegistrationInfoTest, CheckAuthenticationFailure) {
Vitaly Bukac11a17d2015-08-15 10:36:10 -0700250 ReloadSettings();
Vitaly Bukac3c6dab2015-10-01 19:41:02 -0700251 EXPECT_EQ(GcdState::kConnecting, GetGcdState());
Nathan Bullock24d189f2015-02-26 13:09:18 -0500252
Vitaly Buka1a42e142015-10-10 18:15:15 -0700253 EXPECT_CALL(
254 http_client_,
255 SendRequest(HttpClient::Method::kPost, dev_reg_->GetOAuthURL("token"),
Vitaly Buka74763422015-10-11 00:39:52 -0700256 HttpClient::Headers{GetFormHeader()}, _, _))
Vitaly Buka17280372015-10-10 17:11:47 -0700257 .WillOnce(WithArgs<3, 4>(Invoke([](
258 const std::string& data,
Vitaly Buka74763422015-10-11 00:39:52 -0700259 const HttpClient::SendRequestCallback& callback) {
Vitaly Buka10206182015-08-05 11:17:43 -0700260 EXPECT_EQ("refresh_token", GetFormField(data, "grant_type"));
261 EXPECT_EQ(test_data::kRefreshToken,
262 GetFormField(data, "refresh_token"));
263 EXPECT_EQ(test_data::kClientId, GetFormField(data, "client_id"));
264 EXPECT_EQ(test_data::kClientSecret,
265 GetFormField(data, "client_secret"));
266
267 base::DictionaryValue json;
268 json.SetString("error", "unable_to_authenticate");
Vitaly Buka74763422015-10-11 00:39:52 -0700269 callback.Run(ReplyWithJson(400, json), nullptr);
Vitaly Buka10206182015-08-05 11:17:43 -0700270 })));
271
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700272 ErrorPtr error;
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700273 EXPECT_FALSE(RefreshAccessToken(&error));
Vitaly Buka32005de2015-05-01 12:33:31 -0700274 EXPECT_TRUE(error->HasError(kErrorDomainOAuth2, "unable_to_authenticate"));
Vitaly Bukac3c6dab2015-10-01 19:41:02 -0700275 EXPECT_EQ(GcdState::kConnecting, GetGcdState());
Nathan Bullock24d189f2015-02-26 13:09:18 -0500276}
277
278TEST_F(DeviceRegistrationInfoTest, CheckDeregistration) {
Vitaly Bukac11a17d2015-08-15 10:36:10 -0700279 ReloadSettings();
Vitaly Bukac3c6dab2015-10-01 19:41:02 -0700280 EXPECT_EQ(GcdState::kConnecting, GetGcdState());
Nathan Bullock24d189f2015-02-26 13:09:18 -0500281
Vitaly Buka1a42e142015-10-10 18:15:15 -0700282 EXPECT_CALL(
283 http_client_,
284 SendRequest(HttpClient::Method::kPost, dev_reg_->GetOAuthURL("token"),
Vitaly Buka74763422015-10-11 00:39:52 -0700285 HttpClient::Headers{GetFormHeader()}, _, _))
Vitaly Buka17280372015-10-10 17:11:47 -0700286 .WillOnce(WithArgs<3, 4>(Invoke([](
287 const std::string& data,
Vitaly Buka74763422015-10-11 00:39:52 -0700288 const HttpClient::SendRequestCallback& callback) {
Vitaly Buka10206182015-08-05 11:17:43 -0700289 EXPECT_EQ("refresh_token", GetFormField(data, "grant_type"));
290 EXPECT_EQ(test_data::kRefreshToken,
291 GetFormField(data, "refresh_token"));
292 EXPECT_EQ(test_data::kClientId, GetFormField(data, "client_id"));
293 EXPECT_EQ(test_data::kClientSecret,
294 GetFormField(data, "client_secret"));
295
296 base::DictionaryValue json;
297 json.SetString("error", "invalid_grant");
Vitaly Buka74763422015-10-11 00:39:52 -0700298 callback.Run(ReplyWithJson(400, json), nullptr);
Vitaly Buka10206182015-08-05 11:17:43 -0700299 })));
300
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700301 ErrorPtr error;
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700302 EXPECT_FALSE(RefreshAccessToken(&error));
Vitaly Buka32005de2015-05-01 12:33:31 -0700303 EXPECT_TRUE(error->HasError(kErrorDomainOAuth2, "invalid_grant"));
Vitaly Bukac3c6dab2015-10-01 19:41:02 -0700304 EXPECT_EQ(GcdState::kInvalidCredentials, GetGcdState());
Vitaly Buka6a9d0e22015-12-06 17:49:11 -0800305 EXPECT_EQ(test_data::kCloudId, dev_reg_->GetSettings().cloud_id);
Nathan Bullock24d189f2015-02-26 13:09:18 -0500306}
307
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700308TEST_F(DeviceRegistrationInfoTest, GetDeviceInfo) {
Vitaly Bukac11a17d2015-08-15 10:36:10 -0700309 ReloadSettings();
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700310 SetAccessToken();
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700311
Vitaly Buka74763422015-10-11 00:39:52 -0700312 EXPECT_CALL(
313 http_client_,
314 SendRequest(HttpClient::Method::kGet, dev_reg_->GetDeviceURL(),
315 HttpClient::Headers{GetAuthHeader(), GetJsonHeader()}, _, _))
Vitaly Buka17280372015-10-10 17:11:47 -0700316 .WillOnce(WithArgs<3, 4>(
317 Invoke([](const std::string& data,
Vitaly Buka74763422015-10-11 00:39:52 -0700318 const HttpClient::SendRequestCallback& callback) {
Vitaly Buka17280372015-10-10 17:11:47 -0700319 base::DictionaryValue json;
320 json.SetString("channel.supportedType", "xmpp");
321 json.SetString("deviceKind", "vendor");
Vitaly Buka6a9d0e22015-12-06 17:49:11 -0800322 json.SetString("id", test_data::kCloudId);
Vitaly Buka88272d52015-11-17 17:04:01 -0800323 json.SetString("kind", "weave#device");
Vitaly Buka74763422015-10-11 00:39:52 -0700324 callback.Run(ReplyWithJson(200, json), nullptr);
Vitaly Buka17280372015-10-10 17:11:47 -0700325 })));
Vitaly Buka10206182015-08-05 11:17:43 -0700326
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700327 bool succeeded = false;
Vitaly Buka74763422015-10-11 00:39:52 -0700328 auto callback = [&succeeded, this](const base::DictionaryValue& info,
329 ErrorPtr error) {
330 EXPECT_FALSE(error);
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700331 std::string id;
332 EXPECT_TRUE(info.GetString("id", &id));
Vitaly Buka6a9d0e22015-12-06 17:49:11 -0800333 EXPECT_EQ(test_data::kCloudId, id);
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700334 succeeded = true;
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700335 };
Vitaly Buka74763422015-10-11 00:39:52 -0700336 dev_reg_->GetDeviceInfo(base::Bind(callback));
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700337 EXPECT_TRUE(succeeded);
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700338}
339
Anton Muhinbeb1c5b2014-10-16 18:59:57 +0400340TEST_F(DeviceRegistrationInfoTest, RegisterDevice) {
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800341 auto json_traits = CreateDictionaryValue(R"({
Alex Vakulenko45109442014-07-29 11:07:10 -0700342 'base': {
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800343 'commands': {
344 'reboot': {
345 'parameters': {'delay': {'minimum': 10, 'type': 'integer'}},
346 'minimalRole': 'user'
347 }
348 },
349 'state': {
350 'firmwareVersion': {'type': 'string'}
Alex Vakulenko45109442014-07-29 11:07:10 -0700351 }
352 },
353 'robot': {
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800354 'commands': {
355 '_jump': {
356 'parameters': {'_height': {'type': 'integer'}},
357 'minimalRole': 'user'
358 }
Alex Vakulenko45109442014-07-29 11:07:10 -0700359 }
360 }
361 })");
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800362 EXPECT_TRUE(component_manager_.LoadTraits(*json_traits, nullptr));
363 EXPECT_TRUE(component_manager_.AddComponent("", "comp", {"base", "robot"},
364 nullptr));
365 base::StringValue ver{"1.0"};
366 EXPECT_TRUE(component_manager_.SetStateProperty(
367 "comp", "base.firmwareVersion", ver, nullptr));
Alex Vakulenko45109442014-07-29 11:07:10 -0700368
Vitaly Buka10206182015-08-05 11:17:43 -0700369 std::string ticket_url = dev_reg_->GetServiceURL("registrationTickets/") +
370 test_data::kClaimTicketId;
Vitaly Buka1a42e142015-10-10 18:15:15 -0700371 EXPECT_CALL(http_client_,
372 SendRequest(HttpClient::Method::kPatch,
373 ticket_url + "?key=" + test_data::kApiKey,
Vitaly Buka74763422015-10-11 00:39:52 -0700374 HttpClient::Headers{GetJsonHeader()}, _, _))
Vitaly Buka17280372015-10-10 17:11:47 -0700375 .WillOnce(WithArgs<3, 4>(Invoke([](
376 const std::string& data,
Vitaly Buka74763422015-10-11 00:39:52 -0700377 const HttpClient::SendRequestCallback& callback) {
Vitaly Buka0f6b2ec2015-08-20 15:35:19 -0700378 auto json = test::CreateDictionaryValue(data);
Vitaly Buka10206182015-08-05 11:17:43 -0700379 EXPECT_NE(nullptr, json.get());
380 std::string value;
381 EXPECT_TRUE(json->GetString("id", &value));
382 EXPECT_EQ(test_data::kClaimTicketId, value);
383 EXPECT_TRUE(
384 json->GetString("deviceDraft.channel.supportedType", &value));
385 EXPECT_EQ("pull", value);
386 EXPECT_TRUE(json->GetString("oauthClientId", &value));
387 EXPECT_EQ(test_data::kClientId, value);
Vitaly Buka10206182015-08-05 11:17:43 -0700388 EXPECT_TRUE(json->GetString("deviceDraft.description", &value));
389 EXPECT_EQ("Easy to clean", value);
390 EXPECT_TRUE(json->GetString("deviceDraft.location", &value));
391 EXPECT_EQ("Kitchen", value);
392 EXPECT_TRUE(json->GetString("deviceDraft.modelManifestId", &value));
393 EXPECT_EQ("AAAAA", value);
394 EXPECT_TRUE(json->GetString("deviceDraft.name", &value));
395 EXPECT_EQ("Coffee Pot", value);
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800396 base::DictionaryValue* dict = nullptr;
397 EXPECT_TRUE(json->GetDictionary("deviceDraft.commandDefs", &dict));
398 auto expectedCommandDefs = R"({
Vitaly Buka10206182015-08-05 11:17:43 -0700399 'base': {
400 'reboot': {
401 'parameters': {
402 'delay': {
403 'minimum': 10,
404 'type': 'integer'
405 }
406 },
407 'minimalRole': 'user'
408 }
409 },
410 'robot': {
411 '_jump': {
412 'parameters': {
413 '_height': {
414 'type': 'integer'
415 }
416 },
417 'minimalRole': 'user'
418 }
419 }
420 })";
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800421 EXPECT_JSON_EQ(expectedCommandDefs, *dict);
422
423 EXPECT_TRUE(json->GetDictionary("deviceDraft.state", &dict));
424 auto expectedState = R"({
425 'base': {
426 'firmwareVersion': '1.0'
427 }
428 })";
429 EXPECT_JSON_EQ(expectedState, *dict);
430
431 EXPECT_TRUE(json->GetDictionary("deviceDraft.traits", &dict));
432 auto expectedTraits = R"({
433 'base': {
434 'commands': {
435 'reboot': {
436 'parameters': {'delay': {'minimum': 10, 'type': 'integer'}},
437 'minimalRole': 'user'
438 }
439 },
440 'state': {
441 'firmwareVersion': {'type': 'string'}
442 }
443 },
444 'robot': {
445 'commands': {
446 '_jump': {
447 'parameters': {'_height': {'type': 'integer'}},
448 'minimalRole': 'user'
449 }
450 }
451 }
452 })";
453 EXPECT_JSON_EQ(expectedTraits, *dict);
454
455 EXPECT_TRUE(json->GetDictionary("deviceDraft.components", &dict));
456 auto expectedComponents = R"({
457 'comp': {
458 'traits': ['base', 'robot'],
459 'state': {
460 'base': { 'firmwareVersion': '1.0' }
461 }
462 }
463 })";
464 EXPECT_JSON_EQ(expectedComponents, *dict);
Vitaly Buka10206182015-08-05 11:17:43 -0700465
466 base::DictionaryValue json_resp;
467 json_resp.SetString("id", test_data::kClaimTicketId);
Vitaly Buka88272d52015-11-17 17:04:01 -0800468 json_resp.SetString("kind", "weave#registrationTicket");
Vitaly Buka10206182015-08-05 11:17:43 -0700469 json_resp.SetString("oauthClientId", test_data::kClientId);
470 base::DictionaryValue* device_draft = nullptr;
471 EXPECT_TRUE(json->GetDictionary("deviceDraft", &device_draft));
472 device_draft = device_draft->DeepCopy();
Vitaly Buka6a9d0e22015-12-06 17:49:11 -0800473 device_draft->SetString("id", test_data::kCloudId);
Vitaly Buka88272d52015-11-17 17:04:01 -0800474 device_draft->SetString("kind", "weave#device");
Vitaly Buka10206182015-08-05 11:17:43 -0700475 json_resp.Set("deviceDraft", device_draft);
476
Vitaly Buka74763422015-10-11 00:39:52 -0700477 callback.Run(ReplyWithJson(200, json_resp), nullptr);
Vitaly Buka10206182015-08-05 11:17:43 -0700478 })));
479
480 EXPECT_CALL(http_client_,
Vitaly Buka1a42e142015-10-10 18:15:15 -0700481 SendRequest(HttpClient::Method::kPost,
Vitaly Buka17280372015-10-10 17:11:47 -0700482 ticket_url + "/finalize?key=" + test_data::kApiKey,
Vitaly Buka74763422015-10-11 00:39:52 -0700483 HttpClient::Headers{}, _, _))
484 .WillOnce(WithArgs<4>(
485 Invoke([](const HttpClient::SendRequestCallback& callback) {
Vitaly Buka17280372015-10-10 17:11:47 -0700486 base::DictionaryValue json;
487 json.SetString("id", test_data::kClaimTicketId);
Vitaly Buka88272d52015-11-17 17:04:01 -0800488 json.SetString("kind", "weave#registrationTicket");
Vitaly Buka17280372015-10-10 17:11:47 -0700489 json.SetString("oauthClientId", test_data::kClientId);
490 json.SetString("userEmail", "user@email.com");
Vitaly Buka6a9d0e22015-12-06 17:49:11 -0800491 json.SetString("deviceDraft.id", test_data::kCloudId);
Vitaly Buka88272d52015-11-17 17:04:01 -0800492 json.SetString("deviceDraft.kind", "weave#device");
Vitaly Buka17280372015-10-10 17:11:47 -0700493 json.SetString("deviceDraft.channel.supportedType", "xmpp");
494 json.SetString("robotAccountEmail", test_data::kRobotAccountEmail);
495 json.SetString("robotAccountAuthorizationCode",
496 test_data::kRobotAccountAuthCode);
Vitaly Buka74763422015-10-11 00:39:52 -0700497 callback.Run(ReplyWithJson(200, json), nullptr);
Vitaly Buka17280372015-10-10 17:11:47 -0700498 })));
Vitaly Buka10206182015-08-05 11:17:43 -0700499
Vitaly Buka1a42e142015-10-10 18:15:15 -0700500 EXPECT_CALL(
501 http_client_,
502 SendRequest(HttpClient::Method::kPost, dev_reg_->GetOAuthURL("token"),
Vitaly Buka74763422015-10-11 00:39:52 -0700503 HttpClient::Headers{GetFormHeader()}, _, _))
Vitaly Buka17280372015-10-10 17:11:47 -0700504 .WillOnce(WithArgs<3, 4>(Invoke([](
505 const std::string& data,
Vitaly Buka74763422015-10-11 00:39:52 -0700506 const HttpClient::SendRequestCallback& callback) {
Vitaly Buka10206182015-08-05 11:17:43 -0700507 EXPECT_EQ("authorization_code", GetFormField(data, "grant_type"));
508 EXPECT_EQ(test_data::kRobotAccountAuthCode, GetFormField(data, "code"));
509 EXPECT_EQ(test_data::kClientId, GetFormField(data, "client_id"));
510 EXPECT_EQ(test_data::kClientSecret,
511 GetFormField(data, "client_secret"));
Vitaly Buka10206182015-08-05 11:17:43 -0700512 EXPECT_EQ("oob", GetFormField(data, "redirect_uri"));
Vitaly Buka10206182015-08-05 11:17:43 -0700513
514 base::DictionaryValue json;
515 json.SetString("access_token", test_data::kAccessToken);
516 json.SetString("token_type", "Bearer");
517 json.SetString("refresh_token", test_data::kRefreshToken);
518 json.SetInteger("expires_in", 3600);
519
Vitaly Buka74763422015-10-11 00:39:52 -0700520 callback.Run(ReplyWithJson(200, json), nullptr);
Vitaly Buka10206182015-08-05 11:17:43 -0700521 })));
522
Vitaly Buka12870bd2015-10-08 23:49:39 -0700523 bool done = false;
524 dev_reg_->RegisterDevice(
Vitaly Buka74763422015-10-11 00:39:52 -0700525 test_data::kClaimTicketId, base::Bind([this, &done](ErrorPtr error) {
526 EXPECT_FALSE(error);
Vitaly Buka12870bd2015-10-08 23:49:39 -0700527 done = true;
528 task_runner_.Break();
529 EXPECT_EQ(GcdState::kConnecting, GetGcdState());
Anton Muhinbeb1c5b2014-10-16 18:59:57 +0400530
Vitaly Buka12870bd2015-10-08 23:49:39 -0700531 // Validate the device info saved to storage...
Vitaly Buka6a9d0e22015-12-06 17:49:11 -0800532 EXPECT_EQ(test_data::kCloudId, dev_reg_->GetSettings().cloud_id);
Vitaly Buka12870bd2015-10-08 23:49:39 -0700533 EXPECT_EQ(test_data::kRefreshToken,
534 dev_reg_->GetSettings().refresh_token);
535 EXPECT_EQ(test_data::kRobotAccountEmail,
536 dev_reg_->GetSettings().robot_account);
Vitaly Buka74763422015-10-11 00:39:52 -0700537 }));
Vitaly Buka12870bd2015-10-08 23:49:39 -0700538 task_runner_.Run();
539 EXPECT_TRUE(done);
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700540}
541
Christopher Wileyc900e482015-02-15 15:42:04 -0800542TEST_F(DeviceRegistrationInfoTest, OOBRegistrationStatus) {
Vitaly Buka10206182015-08-05 11:17:43 -0700543 // After we've been initialized, we should be either offline or
544 // unregistered, depending on whether or not we've found credentials.
Vitaly Bukac3c6dab2015-10-01 19:41:02 -0700545 EXPECT_EQ(GcdState::kUnconfigured, GetGcdState());
Christopher Wileyc900e482015-02-15 15:42:04 -0800546 // Put some credentials into our state, make sure we call that offline.
Vitaly Bukac11a17d2015-08-15 10:36:10 -0700547 ReloadSettings();
Vitaly Bukac3c6dab2015-10-01 19:41:02 -0700548 EXPECT_EQ(GcdState::kConnecting, GetGcdState());
Christopher Wileyc900e482015-02-15 15:42:04 -0800549}
550
Vitaly Buka47a1f6f2015-10-07 18:09:57 -0700551class DeviceRegistrationInfoUpdateCommandTest
552 : public DeviceRegistrationInfoTest {
553 protected:
554 void SetUp() override {
555 DeviceRegistrationInfoTest::SetUp();
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700556
Vitaly Buka47a1f6f2015-10-07 18:09:57 -0700557 ReloadSettings();
558 SetAccessToken();
559
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800560 auto json_traits = CreateDictionaryValue(R"({
Vitaly Buka47a1f6f2015-10-07 18:09:57 -0700561 'robot': {
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800562 'commands': {
563 '_jump': {
564 'parameters': {'_height': 'integer'},
565 'progress': {'progress': 'integer'},
566 'results': {'status': 'string'},
567 'minimalRole': 'user'
568 }
Vitaly Buka47a1f6f2015-10-07 18:09:57 -0700569 }
Alex Vakulenko9e2f8cd2015-04-07 16:28:09 -0700570 }
Vitaly Buka47a1f6f2015-10-07 18:09:57 -0700571 })");
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800572 EXPECT_TRUE(component_manager_.LoadTraits(*json_traits, nullptr));
573 EXPECT_TRUE(component_manager_.AddComponent("", "comp", {"robot"},
574 nullptr));
Alex Vakulenko9e2f8cd2015-04-07 16:28:09 -0700575
Vitaly Buka47a1f6f2015-10-07 18:09:57 -0700576 command_url_ = dev_reg_->GetServiceURL("commands/1234");
Alex Vakulenko808e2d82015-04-08 15:45:56 -0700577
Vitaly Buka47a1f6f2015-10-07 18:09:57 -0700578 auto commands_json = CreateValue(R"([{
579 'name':'robot._jump',
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800580 'component': 'comp',
Vitaly Buka47a1f6f2015-10-07 18:09:57 -0700581 'id':'1234',
582 'parameters': {'_height': 100},
583 'minimalRole': 'user'
584 }])");
585 ASSERT_NE(nullptr, commands_json.get());
586 const base::ListValue* command_list = nullptr;
587 ASSERT_TRUE(commands_json->GetAsList(&command_list));
588 PublishCommands(*command_list);
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800589 command_ = component_manager_.FindCommand("1234");
Vitaly Buka47a1f6f2015-10-07 18:09:57 -0700590 ASSERT_NE(nullptr, command_);
591 }
Alex Vakulenko9e2f8cd2015-04-07 16:28:09 -0700592
Vitaly Bukaff1d1862015-10-07 20:40:36 -0700593 void TearDown() override {
594 task_runner_.RunOnce();
595 DeviceRegistrationInfoTest::TearDown();
596 }
597
Vitaly Buka47a1f6f2015-10-07 18:09:57 -0700598 Command* command_{nullptr};
599 std::string command_url_;
600};
601
602TEST_F(DeviceRegistrationInfoUpdateCommandTest, SetProgress) {
Vitaly Buka74763422015-10-11 00:39:52 -0700603 EXPECT_CALL(
604 http_client_,
605 SendRequest(HttpClient::Method::kPatch, command_url_,
606 HttpClient::Headers{GetAuthHeader(), GetJsonHeader()}, _, _))
Vitaly Buka17280372015-10-10 17:11:47 -0700607 .WillOnce(WithArgs<3, 4>(Invoke([](
608 const std::string& data,
Vitaly Buka74763422015-10-11 00:39:52 -0700609 const HttpClient::SendRequestCallback& callback) {
Vitaly Buka17280372015-10-10 17:11:47 -0700610 EXPECT_JSON_EQ((R"({"state":"inProgress","progress":{"progress":18}})"),
Vitaly Buka10206182015-08-05 11:17:43 -0700611 *CreateDictionaryValue(data));
612 base::DictionaryValue json;
Vitaly Buka74763422015-10-11 00:39:52 -0700613 callback.Run(ReplyWithJson(200, json), nullptr);
Vitaly Buka10206182015-08-05 11:17:43 -0700614 })));
Vitaly Buka47a1f6f2015-10-07 18:09:57 -0700615 EXPECT_TRUE(command_->SetProgress(*CreateDictionaryValue("{'progress':18}"),
616 nullptr));
Vitaly Buka47a1f6f2015-10-07 18:09:57 -0700617}
Alex Vakulenko808e2d82015-04-08 15:45:56 -0700618
Vitaly Buka2f548972015-10-08 19:34:49 -0700619TEST_F(DeviceRegistrationInfoUpdateCommandTest, Complete) {
Vitaly Buka74763422015-10-11 00:39:52 -0700620 EXPECT_CALL(
621 http_client_,
622 SendRequest(HttpClient::Method::kPatch, command_url_,
623 HttpClient::Headers{GetAuthHeader(), GetJsonHeader()}, _, _))
Vitaly Bukaf0a3b222015-10-16 01:49:36 -0700624 .WillOnce(WithArgs<3, 4>(
625 Invoke([](const std::string& data,
626 const HttpClient::SendRequestCallback& callback) {
627 EXPECT_JSON_EQ(R"({"state":"done", "results":{"status":"Ok"}})",
628 *CreateDictionaryValue(data));
629 base::DictionaryValue json;
630 callback.Run(ReplyWithJson(200, json), nullptr);
631 })));
Vitaly Buka2f548972015-10-08 19:34:49 -0700632 EXPECT_TRUE(
633 command_->Complete(*CreateDictionaryValue("{'status': 'Ok'}"), nullptr));
Vitaly Buka47a1f6f2015-10-07 18:09:57 -0700634}
635
636TEST_F(DeviceRegistrationInfoUpdateCommandTest, Cancel) {
Vitaly Buka74763422015-10-11 00:39:52 -0700637 EXPECT_CALL(
638 http_client_,
639 SendRequest(HttpClient::Method::kPatch, command_url_,
640 HttpClient::Headers{GetAuthHeader(), GetJsonHeader()}, _, _))
Vitaly Buka17280372015-10-10 17:11:47 -0700641 .WillOnce(WithArgs<3, 4>(Invoke([](
642 const std::string& data,
Vitaly Buka74763422015-10-11 00:39:52 -0700643 const HttpClient::SendRequestCallback& callback) {
Vitaly Buka10206182015-08-05 11:17:43 -0700644 EXPECT_JSON_EQ(R"({"state":"cancelled"})",
645 *CreateDictionaryValue(data));
646 base::DictionaryValue json;
Vitaly Buka74763422015-10-11 00:39:52 -0700647 callback.Run(ReplyWithJson(200, json), nullptr);
Vitaly Buka10206182015-08-05 11:17:43 -0700648 })));
Vitaly Buka47a1f6f2015-10-07 18:09:57 -0700649 EXPECT_TRUE(command_->Cancel(nullptr));
Alex Vakulenko9e2f8cd2015-04-07 16:28:09 -0700650}
651
Vitaly Bukab6f015a2015-07-09 14:59:23 -0700652} // namespace weave