blob: 9d70886694d4b070b34e36818fa22f485187db03 [file] [log] [blame]
Alex Vakulenko8e34d392014-04-29 11:02:56 -07001// Copyright 2014 The Chromium OS 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
Alex Deymof6cbe322014-11-10 19:55:35 -08005#include "buffet/device_registration_info.h"
6
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 Vakulenko266b2b12015-06-19 11:01:42 -07009#include <base/message_loop/message_loop.h>
10#include <base/run_loop.h>
Alex Vakulenko8e34d392014-04-29 11:02:56 -070011#include <base/values.h>
Alex Vakulenkocf0b33a2014-08-20 15:02:10 -070012#include <chromeos/bind_lambda.h>
Alex Vakulenkoa8b95bc2014-08-27 11:00:57 -070013#include <chromeos/http/http_request.h>
14#include <chromeos/http/http_transport_fake.h>
Anton Muhin332df192014-11-22 05:59:14 +040015#include <chromeos/key_value_store.h>
Alex Vakulenko3aeea1c2014-08-20 16:33:12 -070016#include <chromeos/mime_utils.h>
Alex Vakulenko8e34d392014-04-29 11:02:56 -070017#include <gtest/gtest.h>
18
Alex Vakulenko1f30a622014-07-23 11:13:15 -070019#include "buffet/commands/command_manager.h"
Alex Vakulenko45109442014-07-29 11:07:10 -070020#include "buffet/commands/unittest_utils.h"
Alex Vakulenko57123b22014-10-28 13:50:16 -070021#include "buffet/states/mock_state_change_queue_interface.h"
Alex Vakulenko07216fe2014-09-19 15:31:09 -070022#include "buffet/states/state_manager.h"
Christopher Wiley006e94e2014-05-02 13:44:48 -070023#include "buffet/storage_impls.h"
Alex Vakulenko8e34d392014-04-29 11:02:56 -070024
Vitaly Buka32005de2015-05-01 12:33:31 -070025namespace buffet {
26
Alex Vakulenkocca20932014-08-20 17:35:12 -070027using chromeos::http::request_header::kAuthorization;
28using chromeos::http::fake::ServerRequest;
29using chromeos::http::fake::ServerResponse;
30
Alex Vakulenko8e34d392014-04-29 11:02:56 -070031namespace {
Alex Vakulenko8e34d392014-04-29 11:02:56 -070032
33namespace test_data {
34
35const char kServiceURL[] = "http://gcd.server.com/";
36const char kOAuthURL[] = "http://oauth.server.com/";
37const char kApiKey[] = "GOadRdTf9FERf0k4w6EFOof56fUJ3kFDdFL3d7f";
38const char kClientId[] = "123543821385-sfjkjshdkjhfk234sdfsdfkskd"
39 "fkjh7f.apps.googleusercontent.com";
40const char kClientSecret[] = "5sdGdGlfolGlrFKfdFlgP6FG";
41const char kDeviceId[] = "4a7ea2d1-b331-1e1f-b206-e863c7635196";
42const char kClaimTicketId[] = "RTcUE";
43const char kAccessToken[] = "ya29.1.AADtN_V-dLUM-sVZ0qVjG9Dxm5NgdS9J"
44 "Mx_JLUqhC9bED_YFjzHZtYt65ZzXCS35NMAeaVZ"
45 "Dei530-w0yE2urpQ";
46const char kRefreshToken[] = "1/zQmxR6PKNvhcxf9SjXUrCjcmCrcqRKXctc6cp"
47 "1nI-GQ";
48const char kRobotAccountAuthCode[] = "4/Mf_ujEhPejVhOq-OxW9F5cSOnWzx."
49 "YgciVjTYGscRshQV0ieZDAqiTIjMigI";
50const char kRobotAccountEmail[] = "6ed0b3f54f9bd619b942f4ad2441c252@"
51 "clouddevices.gserviceaccount.com";
52const char kUserAccountAuthCode[] = "2/sd_GD1TGFKpJOLJ34-0g5fK0fflp.GlT"
53 "I0F5g7hNtFgj5HFGOf8FlGK9eflO";
54const char kUserAccessToken[] = "sd56.4.FGDjG_F-gFGF-dFG6gGOG9Dxm5NgdS9"
55 "JMx_JLUqhC9bED_YFjLKjlkjLKJlkjLKjlKJea"
56 "VZDei530-w0yE2urpQ";
57const char kUserRefreshToken[] = "1/zQLKjlKJlkLkLKjLkjLKjLkjLjLkjl0ftc6"
58 "cp1nI-GQ";
Alex Vakulenko8e34d392014-04-29 11:02:56 -070059} // namespace test_data
60
Alex Vakulenko8e34d392014-04-29 11:02:56 -070061// Add the test device registration information.
62void SetDefaultDeviceRegistration(base::DictionaryValue* data) {
Vitaly Bukacad2e332015-05-14 23:33:32 -070063 data->SetString("refresh_token", test_data::kRefreshToken);
64 data->SetString("device_id", test_data::kDeviceId);
65 data->SetString("robot_account", test_data::kRobotAccountEmail);
Alex Vakulenko8e34d392014-04-29 11:02:56 -070066}
67
Alex Vakulenkocca20932014-08-20 17:35:12 -070068void OAuth2Handler(const ServerRequest& request, ServerResponse* response) {
Alex Vakulenko8e34d392014-04-29 11:02:56 -070069 base::DictionaryValue json;
70 if (request.GetFormField("grant_type") == "refresh_token") {
71 // Refresh device access token.
72 EXPECT_EQ(test_data::kRefreshToken, request.GetFormField("refresh_token"));
73 EXPECT_EQ(test_data::kClientId, request.GetFormField("client_id"));
74 EXPECT_EQ(test_data::kClientSecret, request.GetFormField("client_secret"));
75 json.SetString("access_token", test_data::kAccessToken);
76 } else if (request.GetFormField("grant_type") == "authorization_code") {
77 // Obtain access token.
78 std::string code = request.GetFormField("code");
79 if (code == test_data::kUserAccountAuthCode) {
80 // Get user access token.
81 EXPECT_EQ(test_data::kClientId, request.GetFormField("client_id"));
82 EXPECT_EQ(test_data::kClientSecret,
83 request.GetFormField("client_secret"));
84 EXPECT_EQ("urn:ietf:wg:oauth:2.0:oob",
85 request.GetFormField("redirect_uri"));
86 json.SetString("access_token", test_data::kUserAccessToken);
87 json.SetString("token_type", "Bearer");
88 json.SetString("refresh_token", test_data::kUserRefreshToken);
89 } else if (code == test_data::kRobotAccountAuthCode) {
90 // Get device access token.
91 EXPECT_EQ(test_data::kClientId, request.GetFormField("client_id"));
92 EXPECT_EQ(test_data::kClientSecret,
93 request.GetFormField("client_secret"));
94 EXPECT_EQ("oob", request.GetFormField("redirect_uri"));
95 EXPECT_EQ("https://www.googleapis.com/auth/clouddevices",
96 request.GetFormField("scope"));
97 json.SetString("access_token", test_data::kAccessToken);
98 json.SetString("token_type", "Bearer");
99 json.SetString("refresh_token", test_data::kRefreshToken);
100 } else {
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700101 FAIL() << "Unexpected authorization code";
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700102 }
103 } else {
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700104 FAIL() << "Unexpected grant type";
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700105 }
106 json.SetInteger("expires_in", 3600);
Alex Vakulenkocca20932014-08-20 17:35:12 -0700107 response->ReplyJson(chromeos::http::status_code::Ok, &json);
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700108}
109
Nathan Bullock24d189f2015-02-26 13:09:18 -0500110void OAuth2HandlerFail(const ServerRequest& request,
111 ServerResponse* response) {
112 base::DictionaryValue json;
113 EXPECT_EQ("refresh_token", request.GetFormField("grant_type"));
114 EXPECT_EQ(test_data::kRefreshToken, request.GetFormField("refresh_token"));
115 EXPECT_EQ(test_data::kClientId, request.GetFormField("client_id"));
116 EXPECT_EQ(test_data::kClientSecret, request.GetFormField("client_secret"));
117 json.SetString("error", "unable_to_authenticate");
118 response->ReplyJson(chromeos::http::status_code::BadRequest, &json);
119}
120
121void OAuth2HandlerDeregister(const ServerRequest& request,
122 ServerResponse* response) {
123 base::DictionaryValue json;
124 EXPECT_EQ("refresh_token", request.GetFormField("grant_type"));
125 EXPECT_EQ(test_data::kRefreshToken, request.GetFormField("refresh_token"));
126 EXPECT_EQ(test_data::kClientId, request.GetFormField("client_id"));
127 EXPECT_EQ(test_data::kClientSecret, request.GetFormField("client_secret"));
128 json.SetString("error", "invalid_grant");
129 response->ReplyJson(chromeos::http::status_code::BadRequest, &json);
130}
131
Alex Vakulenkocca20932014-08-20 17:35:12 -0700132void DeviceInfoHandler(const ServerRequest& request, ServerResponse* response) {
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700133 std::string auth = "Bearer ";
134 auth += test_data::kAccessToken;
Alex Vakulenkocca20932014-08-20 17:35:12 -0700135 EXPECT_EQ(auth,
136 request.GetHeader(chromeos::http::request_header::kAuthorization));
137 response->ReplyJson(chromeos::http::status_code::Ok, {
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700138 {"channel.supportedType", "xmpp"},
139 {"deviceKind", "vendor"},
140 {"id", test_data::kDeviceId},
141 {"kind", "clouddevices#device"},
142 });
143}
144
Alex Vakulenkocca20932014-08-20 17:35:12 -0700145void FinalizeTicketHandler(const ServerRequest& request,
146 ServerResponse* response) {
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700147 EXPECT_EQ(test_data::kApiKey, request.GetFormField("key"));
148 EXPECT_TRUE(request.GetData().empty());
149
Alex Vakulenkocca20932014-08-20 17:35:12 -0700150 response->ReplyJson(chromeos::http::status_code::Ok, {
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700151 {"id", test_data::kClaimTicketId},
152 {"kind", "clouddevices#registrationTicket"},
153 {"oauthClientId", test_data::kClientId},
154 {"userEmail", "user@email.com"},
155 {"deviceDraft.id", test_data::kDeviceId},
156 {"deviceDraft.kind", "clouddevices#device"},
157 {"deviceDraft.channel.supportedType", "xmpp"},
158 {"robotAccountEmail", test_data::kRobotAccountEmail},
159 {"robotAccountAuthorizationCode", test_data::kRobotAccountAuthCode},
160 });
161}
162
163} // anonymous namespace
164
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700165class DeviceRegistrationInfoTest : public ::testing::Test {
166 protected:
Alex Vakulenko5a9e7182014-08-11 15:59:58 -0700167 void SetUp() override {
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700168 std::unique_ptr<StorageInterface> storage{new MemStorage};
169 storage_ = storage.get();
170 storage->Save(data_);
Alex Vakulenkocca20932014-08-20 17:35:12 -0700171 transport_ = std::make_shared<chromeos::http::fake::Transport>();
Vitaly Bukaae0f3a12015-05-11 16:27:30 -0700172 command_manager_ = std::make_shared<CommandManager>();
Alex Vakulenko57123b22014-10-28 13:50:16 -0700173 state_manager_ = std::make_shared<StateManager>(&mock_state_change_queue_);
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700174
175 std::unique_ptr<BuffetConfig> config{new BuffetConfig{std::move(storage)}};
176 config_ = config.get();
Vitaly Buka63cc3d22015-06-23 20:11:36 -0700177 dev_reg_.reset(new DeviceRegistrationInfo{command_manager_, state_manager_,
178 std::move(config), transport_,
179 true, nullptr});
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700180
181 ReloadConfig();
182 }
183
184 void ReloadConfig() {
Christopher Wiley583d64b2015-03-24 14:30:17 -0700185 chromeos::KeyValueStore config_store;
186 config_store.SetString("client_id", test_data::kClientId);
187 config_store.SetString("client_secret", test_data::kClientSecret);
188 config_store.SetString("api_key", test_data::kApiKey);
189 config_store.SetString("device_kind", "vendor");
Alex Vakulenko468f7f32015-04-08 10:42:45 -0700190 config_store.SetString("name", "Coffee Pot");
Vitaly Buka867b0882015-04-16 10:03:26 -0700191 config_store.SetString("description", "Easy to clean");
192 config_store.SetString("location", "Kitchen");
Vitaly Buka4a3a9a22015-05-13 16:06:01 -0700193 config_store.SetString("local_anonymous_access_role", "viewer");
Vitaly Buka867b0882015-04-16 10:03:26 -0700194 config_store.SetString("model_id", "AAAAA");
Christopher Wiley583d64b2015-03-24 14:30:17 -0700195 config_store.SetString("oauth_url", test_data::kOAuthURL);
196 config_store.SetString("service_url", test_data::kServiceURL);
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700197 config_->Load(config_store);
198 dev_reg_->Start();
199 }
200
201 void PublishCommands(const base::ListValue& commands) {
202 return dev_reg_->PublishCommands(commands);
203 }
204
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700205 bool RefreshAccessToken(chromeos::ErrorPtr* error) const {
206 base::MessageLoopForIO message_loop;
207 base::RunLoop run_loop;
208
209 bool succeeded = false;
210 auto on_success = [&run_loop, &succeeded]() {
211 succeeded = true;
212 run_loop.Quit();
213 };
214 auto on_failure = [&run_loop, &error](const chromeos::Error* in_error) {
215 if (error)
216 *error = in_error->Clone();
217 run_loop.Quit();
218 };
219 dev_reg_->RefreshAccessToken(base::Bind(on_success),
220 base::Bind(on_failure));
221 run_loop.Run();
222 return succeeded;
223 }
224
225 void SetAccessToken() {
226 dev_reg_->access_token_ = test_data::kAccessToken;
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700227 }
228
229 RegistrationStatus GetRegistrationStatus() const {
230 return dev_reg_->registration_status_;
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700231 }
232
Alex Vakulenko45109442014-07-29 11:07:10 -0700233 base::DictionaryValue data_;
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700234 StorageInterface* storage_{nullptr};
235 BuffetConfig* config_{nullptr};
Alex Vakulenkocca20932014-08-20 17:35:12 -0700236 std::shared_ptr<chromeos::http::fake::Transport> transport_;
Alex Vakulenko45109442014-07-29 11:07:10 -0700237 std::unique_ptr<DeviceRegistrationInfo> dev_reg_;
238 std::shared_ptr<CommandManager> command_manager_;
Alex Vakulenko57123b22014-10-28 13:50:16 -0700239 testing::NiceMock<MockStateChangeQueueInterface> mock_state_change_queue_;
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700240 std::shared_ptr<StateManager> state_manager_;
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700241};
242
243////////////////////////////////////////////////////////////////////////////////
244TEST_F(DeviceRegistrationInfoTest, GetServiceURL) {
Alex Vakulenko45109442014-07-29 11:07:10 -0700245 EXPECT_EQ(test_data::kServiceURL, dev_reg_->GetServiceURL());
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700246 std::string url = test_data::kServiceURL;
247 url += "registrationTickets";
Alex Vakulenko45109442014-07-29 11:07:10 -0700248 EXPECT_EQ(url, dev_reg_->GetServiceURL("registrationTickets"));
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700249 url += "?key=";
250 url += test_data::kApiKey;
Alex Vakulenko45109442014-07-29 11:07:10 -0700251 EXPECT_EQ(url, dev_reg_->GetServiceURL("registrationTickets", {
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700252 {"key", test_data::kApiKey}
253 }));
254 url += "&restart=true";
Alex Vakulenko45109442014-07-29 11:07:10 -0700255 EXPECT_EQ(url, dev_reg_->GetServiceURL("registrationTickets", {
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700256 {"key", test_data::kApiKey},
257 {"restart", "true"},
258 }));
259}
260
261TEST_F(DeviceRegistrationInfoTest, GetOAuthURL) {
Alex Vakulenko45109442014-07-29 11:07:10 -0700262 EXPECT_EQ(test_data::kOAuthURL, dev_reg_->GetOAuthURL());
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700263 std::string url = test_data::kOAuthURL;
264 url += "auth?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fclouddevices&";
265 url += "redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&";
266 url += "response_type=code&";
267 url += "client_id=";
268 url += test_data::kClientId;
Alex Vakulenko45109442014-07-29 11:07:10 -0700269 EXPECT_EQ(url, dev_reg_->GetOAuthURL("auth", {
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700270 {"scope", "https://www.googleapis.com/auth/clouddevices"},
271 {"redirect_uri", "urn:ietf:wg:oauth:2.0:oob"},
272 {"response_type", "code"},
273 {"client_id", test_data::kClientId}
274 }));
275}
276
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700277TEST_F(DeviceRegistrationInfoTest, HaveRegistrationCredentials) {
278 EXPECT_FALSE(dev_reg_->HaveRegistrationCredentials(nullptr));
Alex Vakulenko45109442014-07-29 11:07:10 -0700279 EXPECT_EQ(0, transport_->GetRequestCount());
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700280
Alex Vakulenko45109442014-07-29 11:07:10 -0700281 SetDefaultDeviceRegistration(&data_);
Vitaly Buka56363222015-05-13 15:09:21 -0700282 storage_->Save(data_);
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700283 ReloadConfig();
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700284
Alex Vakulenkocca20932014-08-20 17:35:12 -0700285 transport_->AddHandler(dev_reg_->GetOAuthURL("token"),
286 chromeos::http::request_type::kPost,
Alex Vakulenko45109442014-07-29 11:07:10 -0700287 base::Bind(OAuth2Handler));
288 transport_->ResetRequestCount();
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700289 EXPECT_TRUE(RefreshAccessToken(nullptr));
Alex Vakulenko45109442014-07-29 11:07:10 -0700290 EXPECT_EQ(1, transport_->GetRequestCount());
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700291 EXPECT_TRUE(dev_reg_->HaveRegistrationCredentials(nullptr));
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700292}
293
Nathan Bullock24d189f2015-02-26 13:09:18 -0500294TEST_F(DeviceRegistrationInfoTest, CheckAuthenticationFailure) {
295 SetDefaultDeviceRegistration(&data_);
Vitaly Buka56363222015-05-13 15:09:21 -0700296 storage_->Save(data_);
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700297 ReloadConfig();
298 EXPECT_EQ(RegistrationStatus::kConnecting, GetRegistrationStatus());
Nathan Bullock24d189f2015-02-26 13:09:18 -0500299
300 transport_->AddHandler(dev_reg_->GetOAuthURL("token"),
301 chromeos::http::request_type::kPost,
302 base::Bind(OAuth2HandlerFail));
303 transport_->ResetRequestCount();
304 chromeos::ErrorPtr error;
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700305 EXPECT_FALSE(RefreshAccessToken(&error));
Nathan Bullock24d189f2015-02-26 13:09:18 -0500306 EXPECT_EQ(1, transport_->GetRequestCount());
Vitaly Buka32005de2015-05-01 12:33:31 -0700307 EXPECT_TRUE(error->HasError(kErrorDomainOAuth2, "unable_to_authenticate"));
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700308 EXPECT_EQ(RegistrationStatus::kConnecting, GetRegistrationStatus());
Nathan Bullock24d189f2015-02-26 13:09:18 -0500309}
310
311TEST_F(DeviceRegistrationInfoTest, CheckDeregistration) {
312 SetDefaultDeviceRegistration(&data_);
Vitaly Buka56363222015-05-13 15:09:21 -0700313 storage_->Save(data_);
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700314 ReloadConfig();
315 EXPECT_EQ(RegistrationStatus::kConnecting, GetRegistrationStatus());
Nathan Bullock24d189f2015-02-26 13:09:18 -0500316
317 transport_->AddHandler(dev_reg_->GetOAuthURL("token"),
318 chromeos::http::request_type::kPost,
319 base::Bind(OAuth2HandlerDeregister));
320 transport_->ResetRequestCount();
321 chromeos::ErrorPtr error;
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700322 EXPECT_FALSE(RefreshAccessToken(&error));
Nathan Bullock24d189f2015-02-26 13:09:18 -0500323 EXPECT_EQ(1, transport_->GetRequestCount());
Vitaly Buka32005de2015-05-01 12:33:31 -0700324 EXPECT_TRUE(error->HasError(kErrorDomainOAuth2, "invalid_grant"));
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700325 EXPECT_EQ(RegistrationStatus::kInvalidCredentials, GetRegistrationStatus());
Nathan Bullock24d189f2015-02-26 13:09:18 -0500326}
327
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700328TEST_F(DeviceRegistrationInfoTest, GetDeviceInfo) {
Alex Vakulenko45109442014-07-29 11:07:10 -0700329 SetDefaultDeviceRegistration(&data_);
Vitaly Buka56363222015-05-13 15:09:21 -0700330 storage_->Save(data_);
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700331 ReloadConfig();
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700332 SetAccessToken();
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700333
Alex Vakulenkocca20932014-08-20 17:35:12 -0700334 transport_->AddHandler(dev_reg_->GetDeviceURL(),
335 chromeos::http::request_type::kGet,
Alex Vakulenko45109442014-07-29 11:07:10 -0700336 base::Bind(DeviceInfoHandler));
337 transport_->ResetRequestCount();
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700338 base::MessageLoopForIO message_loop;
339 base::RunLoop run_loop;
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700340
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700341 bool succeeded = false;
342 auto on_success =
343 [&run_loop, &succeeded, this](const base::DictionaryValue& info) {
344 EXPECT_EQ(1, transport_->GetRequestCount());
345 std::string id;
346 EXPECT_TRUE(info.GetString("id", &id));
347 EXPECT_EQ(test_data::kDeviceId, id);
348 succeeded = true;
349 run_loop.Quit();
350 };
351 auto on_failure = [&run_loop](const chromeos::Error* error) {
352 run_loop.Quit();
353 FAIL() << "Should not be called";
354 };
355 dev_reg_->GetDeviceInfo(base::Bind(on_success), base::Bind(on_failure));
356 run_loop.Run();
357 EXPECT_TRUE(succeeded);
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700358}
359
Anton Muhinbeb1c5b2014-10-16 18:59:57 +0400360TEST_F(DeviceRegistrationInfoTest, RegisterDevice) {
Anton Muhina4803142014-09-24 19:30:45 +0400361 auto update_ticket = [](const ServerRequest& request,
Alex Vakulenkocca20932014-08-20 17:35:12 -0700362 ServerResponse* response) {
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700363 EXPECT_EQ(test_data::kApiKey, request.GetFormField("key"));
364 auto json = request.GetDataAsJson();
365 EXPECT_NE(nullptr, json.get());
366 std::string value;
Anton Muhina4803142014-09-24 19:30:45 +0400367 EXPECT_TRUE(json->GetString("id", &value));
368 EXPECT_EQ(test_data::kClaimTicketId, value);
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700369 EXPECT_TRUE(json->GetString("deviceDraft.channel.supportedType", &value));
Alex Vakulenkod05725f2015-05-27 15:48:19 -0700370 EXPECT_EQ("pull", value);
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700371 EXPECT_TRUE(json->GetString("oauthClientId", &value));
372 EXPECT_EQ(test_data::kClientId, value);
373 EXPECT_TRUE(json->GetString("deviceDraft.deviceKind", &value));
374 EXPECT_EQ("vendor", value);
Vitaly Bukad3eb19c2014-11-21 13:39:43 -0800375 EXPECT_TRUE(json->GetString("deviceDraft.description", &value));
376 EXPECT_EQ("Easy to clean", value);
377 EXPECT_TRUE(json->GetString("deviceDraft.location", &value));
378 EXPECT_EQ("Kitchen", value);
Vitaly Buka6522ab62015-02-19 10:26:31 -0800379 EXPECT_TRUE(json->GetString("deviceDraft.modelManifestId", &value));
Vitaly Buka867b0882015-04-16 10:03:26 -0700380 EXPECT_EQ("AAAAA", value);
Alex Vakulenko468f7f32015-04-08 10:42:45 -0700381 EXPECT_TRUE(json->GetString("deviceDraft.name", &value));
Vitaly Bukad3eb19c2014-11-21 13:39:43 -0800382 EXPECT_EQ("Coffee Pot", value);
Alex Vakulenko45109442014-07-29 11:07:10 -0700383 base::DictionaryValue* commandDefs = nullptr;
384 EXPECT_TRUE(json->GetDictionary("deviceDraft.commandDefs", &commandDefs));
385 EXPECT_FALSE(commandDefs->empty());
Vitaly Buka7c82d292015-05-03 18:08:12 -0700386
387 auto expected = R"({
388 'base': {
389 'reboot': {
390 'parameters': {
391 'delay': {
392 'minimum': 10,
393 'type': 'integer'
394 }
Vitaly Buka6fed0532015-05-14 16:57:23 -0700395 },
396 'minimalRole': 'user'
Vitaly Buka7c82d292015-05-03 18:08:12 -0700397 }
398 },
399 'robot': {
400 '_jump': {
401 'parameters': {
402 '_height': {
403 'type': 'integer'
404 }
Vitaly Buka6fed0532015-05-14 16:57:23 -0700405 },
406 'minimalRole': 'user'
Vitaly Buka7c82d292015-05-03 18:08:12 -0700407 }
408 }
409 })";
410 EXPECT_JSON_EQ(expected, *commandDefs);
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700411
412 base::DictionaryValue json_resp;
413 json_resp.SetString("id", test_data::kClaimTicketId);
414 json_resp.SetString("kind", "clouddevices#registrationTicket");
415 json_resp.SetString("oauthClientId", test_data::kClientId);
416 base::DictionaryValue* device_draft = nullptr;
417 EXPECT_TRUE(json->GetDictionary("deviceDraft", &device_draft));
418 device_draft = device_draft->DeepCopy();
419 device_draft->SetString("id", test_data::kDeviceId);
420 device_draft->SetString("kind", "clouddevices#device");
421 json_resp.Set("deviceDraft", device_draft);
422
Alex Vakulenkocca20932014-08-20 17:35:12 -0700423 response->ReplyJson(chromeos::http::status_code::Ok, &json_resp);
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700424 };
425
Vitaly Buka32005de2015-05-01 12:33:31 -0700426 auto json_base = unittests::CreateDictionaryValue(R"({
Alex Vakulenko45109442014-07-29 11:07:10 -0700427 'base': {
428 'reboot': {
Anton Muhin71fb9d52014-11-21 22:22:39 +0400429 'parameters': {'delay': 'integer'},
Vitaly Buka6fed0532015-05-14 16:57:23 -0700430 'minimalRole': 'user',
Anton Muhin71fb9d52014-11-21 22:22:39 +0400431 'results': {}
Alex Vakulenko45109442014-07-29 11:07:10 -0700432 },
433 'shutdown': {
Anton Muhin71fb9d52014-11-21 22:22:39 +0400434 'parameters': {},
Vitaly Buka6fed0532015-05-14 16:57:23 -0700435 'minimalRole': 'user',
Anton Muhin71fb9d52014-11-21 22:22:39 +0400436 'results': {}
Alex Vakulenko45109442014-07-29 11:07:10 -0700437 }
438 }
439 })");
440 EXPECT_TRUE(command_manager_->LoadBaseCommands(*json_base, nullptr));
Vitaly Buka32005de2015-05-01 12:33:31 -0700441 auto json_cmds = unittests::CreateDictionaryValue(R"({
Alex Vakulenko45109442014-07-29 11:07:10 -0700442 'base': {
443 'reboot': {
Anton Muhin71fb9d52014-11-21 22:22:39 +0400444 'parameters': {'delay': {'minimum': 10}},
Vitaly Buka6fed0532015-05-14 16:57:23 -0700445 'minimalRole': 'user',
Anton Muhin71fb9d52014-11-21 22:22:39 +0400446 'results': {}
Alex Vakulenko45109442014-07-29 11:07:10 -0700447 }
448 },
449 'robot': {
450 '_jump': {
Anton Muhin71fb9d52014-11-21 22:22:39 +0400451 'parameters': {'_height': 'integer'},
Vitaly Buka6fed0532015-05-14 16:57:23 -0700452 'minimalRole': 'user',
Anton Muhin71fb9d52014-11-21 22:22:39 +0400453 'results': {}
Alex Vakulenko45109442014-07-29 11:07:10 -0700454 }
455 }
456 })");
457 EXPECT_TRUE(command_manager_->LoadCommands(*json_cmds, "", nullptr));
458
Anton Muhina4803142014-09-24 19:30:45 +0400459 transport_->AddHandler(dev_reg_->GetServiceURL(
460 std::string("registrationTickets/") + test_data::kClaimTicketId),
461 chromeos::http::request_type::kPatch,
462 base::Bind(update_ticket));
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700463 std::string ticket_url =
Alex Vakulenko45109442014-07-29 11:07:10 -0700464 dev_reg_->GetServiceURL("registrationTickets/" +
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700465 std::string(test_data::kClaimTicketId));
Alex Vakulenkocca20932014-08-20 17:35:12 -0700466 transport_->AddHandler(ticket_url + "/finalize",
467 chromeos::http::request_type::kPost,
Alex Vakulenko45109442014-07-29 11:07:10 -0700468 base::Bind(FinalizeTicketHandler));
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700469
Alex Vakulenkocca20932014-08-20 17:35:12 -0700470 transport_->AddHandler(dev_reg_->GetOAuthURL("token"),
471 chromeos::http::request_type::kPost,
Alex Vakulenko45109442014-07-29 11:07:10 -0700472 base::Bind(OAuth2Handler));
Vitaly Bukacad2e332015-05-14 23:33:32 -0700473 std::string device_id =
474 dev_reg_->RegisterDevice(test_data::kClaimTicketId, nullptr);
Anton Muhinbeb1c5b2014-10-16 18:59:57 +0400475
476 EXPECT_EQ(test_data::kDeviceId, device_id);
Anton Muhinbeb1c5b2014-10-16 18:59:57 +0400477 EXPECT_EQ(3, transport_->GetRequestCount());
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700478 EXPECT_EQ(RegistrationStatus::kConnecting, GetRegistrationStatus());
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700479
480 // Validate the device info saved to storage...
Alex Vakulenko45109442014-07-29 11:07:10 -0700481 auto storage_data = storage_->Load();
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700482 base::DictionaryValue* dict = nullptr;
483 EXPECT_TRUE(storage_data->GetAsDictionary(&dict));
484 std::string value;
Vitaly Bukacad2e332015-05-14 23:33:32 -0700485 EXPECT_TRUE(dict->GetString("device_id", &value));
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700486 EXPECT_EQ(test_data::kDeviceId, value);
Vitaly Bukacad2e332015-05-14 23:33:32 -0700487 EXPECT_TRUE(dict->GetString("refresh_token", &value));
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700488 EXPECT_EQ(test_data::kRefreshToken, value);
Vitaly Bukacad2e332015-05-14 23:33:32 -0700489 EXPECT_TRUE(dict->GetString("robot_account", &value));
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700490 EXPECT_EQ(test_data::kRobotAccountEmail, value);
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700491}
492
Christopher Wileyc900e482015-02-15 15:42:04 -0800493TEST_F(DeviceRegistrationInfoTest, OOBRegistrationStatus) {
494 // After we've been initialized, we should be either offline or unregistered,
495 // depending on whether or not we've found credentials.
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700496 EXPECT_EQ(RegistrationStatus::kUnconfigured, GetRegistrationStatus());
Christopher Wileyc900e482015-02-15 15:42:04 -0800497 // Put some credentials into our state, make sure we call that offline.
498 SetDefaultDeviceRegistration(&data_);
Vitaly Buka56363222015-05-13 15:09:21 -0700499 storage_->Save(data_);
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700500 ReloadConfig();
501 EXPECT_EQ(RegistrationStatus::kConnecting, GetRegistrationStatus());
Christopher Wileyc900e482015-02-15 15:42:04 -0800502}
503
Alex Vakulenko9e2f8cd2015-04-07 16:28:09 -0700504TEST_F(DeviceRegistrationInfoTest, UpdateCommand) {
Alex Vakulenko266b2b12015-06-19 11:01:42 -0700505 SetDefaultDeviceRegistration(&data_);
506 storage_->Save(data_);
507 ReloadConfig();
508 SetAccessToken();
509
Vitaly Buka32005de2015-05-01 12:33:31 -0700510 auto json_cmds = unittests::CreateDictionaryValue(R"({
Alex Vakulenko9e2f8cd2015-04-07 16:28:09 -0700511 'robot': {
512 '_jump': {
513 'parameters': {'_height': 'integer'},
Vitaly Buka6fed0532015-05-14 16:57:23 -0700514 'results': {'status': 'string'},
515 'minimalRole': 'user'
Alex Vakulenko9e2f8cd2015-04-07 16:28:09 -0700516 }
517 }
518 })");
519 EXPECT_TRUE(command_manager_->LoadCommands(*json_cmds, "", nullptr));
520
Alex Vakulenko808e2d82015-04-08 15:45:56 -0700521 const std::string command_url = dev_reg_->GetServiceURL("commands/1234");
522
Vitaly Buka32005de2015-05-01 12:33:31 -0700523 auto commands_json = unittests::CreateValue(R"([{
Alex Vakulenko9e2f8cd2015-04-07 16:28:09 -0700524 'name':'robot._jump',
525 'id':'1234',
Vitaly Buka6fed0532015-05-14 16:57:23 -0700526 'parameters': {'_height': 100},
527 'minimalRole': 'user'
Alex Vakulenko9e2f8cd2015-04-07 16:28:09 -0700528 }])");
529 ASSERT_NE(nullptr, commands_json.get());
530 const base::ListValue* command_list = nullptr;
531 ASSERT_TRUE(commands_json->GetAsList(&command_list));
Vitaly Bukaee7a3af2015-05-14 16:57:23 -0700532 PublishCommands(*command_list);
Alex Vakulenko9e2f8cd2015-04-07 16:28:09 -0700533 auto command = command_manager_->FindCommand("1234");
534 ASSERT_NE(nullptr, command);
535 StringPropType string_type;
536 native_types::Object results{
537 {"status", string_type.CreateValue(std::string{"Ok"}, nullptr)}
538 };
539
Alex Vakulenko808e2d82015-04-08 15:45:56 -0700540 // UpdateCommand when setting command results.
541 auto update_command_results = [](const ServerRequest& request,
542 ServerResponse* response) {
543 EXPECT_EQ(R"({"results":{"status":"Ok"}})",
544 request.GetDataAsNormalizedJsonString());
Alex Vakulenkob211c102015-04-21 11:43:23 -0700545 response->ReplyJson(chromeos::http::status_code::Ok,
546 chromeos::http::FormFieldList{});
Alex Vakulenko9e2f8cd2015-04-07 16:28:09 -0700547 };
548
Alex Vakulenko808e2d82015-04-08 15:45:56 -0700549 transport_->AddHandler(command_url,
550 chromeos::http::request_type::kPatch,
551 base::Bind(update_command_results));
Alex Vakulenko9e2f8cd2015-04-07 16:28:09 -0700552
553 command->SetResults(results);
Alex Vakulenko808e2d82015-04-08 15:45:56 -0700554
555 // UpdateCommand when setting command progress.
556 int count = 0; // This will be called twice...
557 auto update_command_progress = [&count](const ServerRequest& request,
558 ServerResponse* response) {
559 if (count++ == 0) {
560 EXPECT_EQ(R"({"state":"inProgress"})",
561 request.GetDataAsNormalizedJsonString());
562 } else {
563 EXPECT_EQ(R"({"progress":{"progress":18}})",
564 request.GetDataAsNormalizedJsonString());
565 }
Alex Vakulenkob211c102015-04-21 11:43:23 -0700566 response->ReplyJson(chromeos::http::status_code::Ok,
567 chromeos::http::FormFieldList{});
Alex Vakulenko808e2d82015-04-08 15:45:56 -0700568 };
569
570 transport_->AddHandler(command_url,
571 chromeos::http::request_type::kPatch,
572 base::Bind(update_command_progress));
573
Vitaly Buka4129dfa2015-04-29 12:16:58 -0700574 native_types::Object progress{
575 {"progress", unittests::make_int_prop_value(18)}};
576 command->SetProgress(progress);
Alex Vakulenko808e2d82015-04-08 15:45:56 -0700577
578 // UpdateCommand when changing command status.
579 auto update_command_state = [](const ServerRequest& request,
580 ServerResponse* response) {
581 EXPECT_EQ(R"({"state":"cancelled"})",
582 request.GetDataAsNormalizedJsonString());
Alex Vakulenkob211c102015-04-21 11:43:23 -0700583 response->ReplyJson(chromeos::http::status_code::Ok,
584 chromeos::http::FormFieldList{});
Alex Vakulenko808e2d82015-04-08 15:45:56 -0700585 };
586
587 transport_->AddHandler(command_url,
588 chromeos::http::request_type::kPatch,
589 base::Bind(update_command_state));
590
591 command->Cancel();
Alex Vakulenko9e2f8cd2015-04-07 16:28:09 -0700592}
593
594
Alex Vakulenkocca20932014-08-20 17:35:12 -0700595} // namespace buffet