libweave: Update unittests to work with HttpClient http::Transport is being replaced with HttpClient. BUG=brillo:1250 TEST='FEATURES=test emerge-gizmo libweave buffet' Change-Id: Id9dfc68cf40fb61ea7cc2778661797b566172faa
diff --git a/libweave/include/weave/mock_http_client.h b/libweave/include/weave/mock_http_client.h new file mode 100644 index 0000000..015ed0b --- /dev/null +++ b/libweave/include/weave/mock_http_client.h
@@ -0,0 +1,57 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBWEAVE_INCLUDE_WEAVE_MOCK_HTTP_CLIENT_H_ +#define LIBWEAVE_INCLUDE_WEAVE_MOCK_HTTP_CLIENT_H_ + +#include <weave/http_client.h> + +#include <memory> +#include <string> + +#include <gmock/gmock.h> + +namespace weave { +namespace unittests { + +class MockHttpClientResponse : public HttpClient::Response { + public: + MOCK_CONST_METHOD0(GetStatusCode, int()); + MOCK_CONST_METHOD0(GetContentType, std::string()); + MOCK_CONST_METHOD0(GetData, const std::string&()); +}; + +class MockHttpClient : public HttpClient { + public: + ~MockHttpClient() override = default; + + MOCK_METHOD6(MockSendRequest, + Response*(const std::string&, + const std::string&, + const std::string&, + const std::string&, + const Headers&, + chromeos::ErrorPtr*)); + + std::unique_ptr<Response> SendRequestAndBlock( + const std::string& method, + const std::string& url, + const std::string& data, + const std::string& mime_type, + const Headers& headers, + chromeos::ErrorPtr* error) override; + + int SendRequest(const std::string& method, + const std::string& url, + const std::string& data, + const std::string& mime_type, + const Headers& headers, + const SuccessCallback& success_callback, + const ErrorCallback& error_callback) override; +}; + +} // namespace unittests +} // namespace weave + +#endif // LIBWEAVE_INCLUDE_WEAVE_MOCK_HTTP_CLIENT_H_
diff --git a/libweave/libweave.gyp b/libweave/libweave.gyp index 9a1f2b6..be4d1d7 100644 --- a/libweave/libweave.gyp +++ b/libweave/libweave.gyp
@@ -81,6 +81,7 @@ 'sources': [ 'src/commands/mock_command.cc', 'src/commands/unittest_utils.cc', + 'src/mock_http_client.cc', ], 'includes': ['../common-mk/deps.gypi'], },
diff --git a/libweave/src/base_api_handler_unittest.cc b/libweave/src/base_api_handler_unittest.cc index 685aa55..548d0c1 100644 --- a/libweave/src/base_api_handler_unittest.cc +++ b/libweave/src/base_api_handler_unittest.cc
@@ -6,8 +6,8 @@ #include <base/strings/string_number_conversions.h> #include <base/values.h> -#include <chromeos/http/http_transport_fake.h> #include <gtest/gtest.h> +#include <weave/mock_http_client.h> #include "libweave/src/buffet_config.h" #include "libweave/src/commands/command_manager.h" @@ -59,7 +59,7 @@ command_manager_, state_manager_, std::unique_ptr<BuffetConfig>{new BuffetConfig{ std::unique_ptr<StorageInterface>{new MemStorage}}}, - transport_, nullptr, true, nullptr)); + &http_client_, nullptr, true, nullptr)); handler_.reset( new BaseApiHandler{dev_reg_.get(), state_manager_, command_manager_}); } @@ -84,7 +84,7 @@ command_manager_->FindCommand(id)->GetStatus()); } - std::shared_ptr<chromeos::http::fake::Transport> transport_; + StrictMock<unittests::MockHttpClient> http_client_; std::unique_ptr<DeviceRegistrationInfo> dev_reg_; std::shared_ptr<CommandManager> command_manager_; testing::StrictMock<MockStateChangeQueueInterface> mock_state_change_queue_;
diff --git a/libweave/src/device_manager.cc b/libweave/src/device_manager.cc index 0b8bf88..344c725 100644 --- a/libweave/src/device_manager.cc +++ b/libweave/src/device_manager.cc
@@ -13,6 +13,7 @@ #include "libweave/src/buffet_config.h" #include "libweave/src/commands/command_manager.h" #include "libweave/src/device_registration_info.h" +#include "libweave/src/http_transport_client.h" #include "libweave/src/privet/privet_manager.h" #include "libweave/src/states/state_change_queue.h" #include "libweave/src/states/state_manager.h" @@ -50,10 +51,11 @@ std::unique_ptr<BuffetConfig> config{new BuffetConfig{options.state_path}}; config->Load(options.config_path); + http_client_.reset(new buffet::HttpTransportClient{transport}); // TODO(avakulenko): Figure out security implications of storing // device info state data unencrypted. device_info_.reset(new DeviceRegistrationInfo( - command_manager_, state_manager_, std::move(config), transport, + command_manager_, state_manager_, std::move(config), http_client_.get(), base::MessageLoop::current()->task_runner(), options.xmpp_enabled, network)); base_api_handler_.reset(
diff --git a/libweave/src/device_manager.h b/libweave/src/device_manager.h index 2474281..2e65ffe 100644 --- a/libweave/src/device_manager.h +++ b/libweave/src/device_manager.h
@@ -8,6 +8,10 @@ #include <base/memory/weak_ptr.h> #include <weave/device.h> +namespace buffet { +class HttpTransportClient; +} // namespace buffet + namespace weave { class BaseApiHandler; @@ -45,6 +49,8 @@ void OnWiFiBootstrapStateChanged(weave::WifiSetupState state); + // TODO(vitalybuka): Move to buffet. + std::unique_ptr<buffet::HttpTransportClient> http_client_; std::shared_ptr<CommandManager> command_manager_; std::unique_ptr<StateChangeQueue> state_change_queue_; std::shared_ptr<StateManager> state_manager_;
diff --git a/libweave/src/device_registration_info.cc b/libweave/src/device_registration_info.cc index 948b485..bcebaba 100644 --- a/libweave/src/device_registration_info.cc +++ b/libweave/src/device_registration_info.cc
@@ -29,7 +29,6 @@ #include "libweave/src/commands/command_definition.h" #include "libweave/src/commands/command_manager.h" #include "libweave/src/commands/schema_constants.h" -#include "libweave/src/http_transport_client.h" #include "libweave/src/notification/xmpp_channel.h" #include "libweave/src/states/state_manager.h" #include "libweave/src/utils.h" @@ -223,12 +222,11 @@ const std::shared_ptr<CommandManager>& command_manager, const std::shared_ptr<StateManager>& state_manager, std::unique_ptr<BuffetConfig> config, - const std::shared_ptr<chromeos::http::Transport>& transport, + HttpClient* http_client, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, bool notifications_enabled, Network* network) - : http_client_owner_{new buffet::HttpTransportClient{transport}}, - http_client_{http_client_owner_.get()}, + : http_client_{http_client}, task_runner_{task_runner}, command_manager_{command_manager}, state_manager_{state_manager},
diff --git a/libweave/src/device_registration_info.h b/libweave/src/device_registration_info.h index dccb6dd..3c8dff3 100644 --- a/libweave/src/device_registration_info.h +++ b/libweave/src/device_registration_info.h
@@ -43,10 +43,6 @@ class KeyValueStore; } // namespace chromeos -namespace buffet { -class HttpTransportClient; -} // namespace buffet - namespace weave { class Network; @@ -70,7 +66,7 @@ const std::shared_ptr<CommandManager>& command_manager, const std::shared_ptr<StateManager>& state_manager, std::unique_ptr<BuffetConfig> config, - const std::shared_ptr<chromeos::http::Transport>& transport, + HttpClient* http_client, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, bool notifications_enabled, weave::Network* network); @@ -305,9 +301,7 @@ bool connected_to_cloud_{false}; // HTTP transport used for communications. - // TODO(vitalybuka): Move to buffet. - std::unique_ptr<buffet::HttpTransportClient> http_client_owner_; - HttpClient* http_client_; + HttpClient* http_client_{nullptr}; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; // Global command manager.
diff --git a/libweave/src/device_registration_info_unittest.cc b/libweave/src/device_registration_info_unittest.cc index 7d204bd..d76d9eb 100644 --- a/libweave/src/device_registration_info_unittest.cc +++ b/libweave/src/device_registration_info_unittest.cc
@@ -10,11 +10,10 @@ #include <base/run_loop.h> #include <base/values.h> #include <chromeos/bind_lambda.h> -#include <chromeos/http/http_request.h> -#include <chromeos/http/http_transport_fake.h> #include <chromeos/key_value_store.h> #include <chromeos/mime_utils.h> #include <gtest/gtest.h> +#include <weave/mock_http_client.h> #include "libweave/src/commands/command_manager.h" #include "libweave/src/commands/unittest_utils.h" @@ -23,15 +22,22 @@ #include "libweave/src/storage_impls.h" using testing::_; +using testing::AtLeast; +using testing::Invoke; +using testing::InvokeWithoutArgs; +using testing::Mock; using testing::Return; +using testing::ReturnRef; +using testing::ReturnRefOfCopy; using testing::StrictMock; +using testing::WithArgs; namespace weave { -using chromeos::http::fake::ServerRequest; -using chromeos::http::fake::ServerResponse; -using chromeos::http::request_header::kAuthorization; using unittests::CreateDictionaryValue; +using unittests::CreateValue; +using unittests::MockHttpClient; +using unittests::MockHttpClientResponse; namespace { @@ -59,16 +65,7 @@ const char kRobotAccountEmail[] = "6ed0b3f54f9bd619b942f4ad2441c252@" "clouddevices.gserviceaccount.com"; -const char kUserAccountAuthCode[] = - "2/sd_GD1TGFKpJOLJ34-0g5fK0fflp.GlT" - "I0F5g7hNtFgj5HFGOf8FlGK9eflO"; -const char kUserAccessToken[] = - "sd56.4.FGDjG_F-gFGF-dFG6gGOG9Dxm5NgdS9" - "JMx_JLUqhC9bED_YFjLKjlkjLKJlkjLKjlKJea" - "VZDei530-w0yE2urpQ"; -const char kUserRefreshToken[] = - "1/zQLKjlKJlkLkLKjLkjLKjLkjLjLkjl0ftc6" - "cp1nI-GQ"; + } // namespace test_data // Add the test device registration information. @@ -78,101 +75,36 @@ data->SetString("robot_account", test_data::kRobotAccountEmail); } -void OAuth2Handler(const ServerRequest& request, ServerResponse* response) { - base::DictionaryValue json; - if (request.GetFormField("grant_type") == "refresh_token") { - // Refresh device access token. - EXPECT_EQ(test_data::kRefreshToken, request.GetFormField("refresh_token")); - EXPECT_EQ(test_data::kClientId, request.GetFormField("client_id")); - EXPECT_EQ(test_data::kClientSecret, request.GetFormField("client_secret")); - json.SetString("access_token", test_data::kAccessToken); - } else if (request.GetFormField("grant_type") == "authorization_code") { - // Obtain access token. - std::string code = request.GetFormField("code"); - if (code == test_data::kUserAccountAuthCode) { - // Get user access token. - EXPECT_EQ(test_data::kClientId, request.GetFormField("client_id")); - EXPECT_EQ(test_data::kClientSecret, - request.GetFormField("client_secret")); - EXPECT_EQ("urn:ietf:wg:oauth:2.0:oob", - request.GetFormField("redirect_uri")); - json.SetString("access_token", test_data::kUserAccessToken); - json.SetString("token_type", "Bearer"); - json.SetString("refresh_token", test_data::kUserRefreshToken); - } else if (code == test_data::kRobotAccountAuthCode) { - // Get device access token. - EXPECT_EQ(test_data::kClientId, request.GetFormField("client_id")); - EXPECT_EQ(test_data::kClientSecret, - request.GetFormField("client_secret")); - EXPECT_EQ("oob", request.GetFormField("redirect_uri")); - EXPECT_EQ("https://www.googleapis.com/auth/clouddevices", - request.GetFormField("scope")); - json.SetString("access_token", test_data::kAccessToken); - json.SetString("token_type", "Bearer"); - json.SetString("refresh_token", test_data::kRefreshToken); - } else { - FAIL() << "Unexpected authorization code"; - } - } else { - FAIL() << "Unexpected grant type"; +std::string GetFormField(const std::string& data, const std::string& name) { + EXPECT_FALSE(data.empty()); + for (const auto& i : chromeos::data_encoding::WebParamsDecode(data)) { + if (i.first == name) + return i.second; } - json.SetInteger("expires_in", 3600); - response->ReplyJson(chromeos::http::status_code::Ok, &json); + return {}; } -void OAuth2HandlerFail(const ServerRequest& request, ServerResponse* response) { - base::DictionaryValue json; - EXPECT_EQ("refresh_token", request.GetFormField("grant_type")); - EXPECT_EQ(test_data::kRefreshToken, request.GetFormField("refresh_token")); - EXPECT_EQ(test_data::kClientId, request.GetFormField("client_id")); - EXPECT_EQ(test_data::kClientSecret, request.GetFormField("client_secret")); - json.SetString("error", "unable_to_authenticate"); - response->ReplyJson(chromeos::http::status_code::BadRequest, &json); +HttpClient::Response* ReplyWithJson(int status_code, const base::Value& json) { + std::string text; + base::JSONWriter::WriteWithOptions( + json, base::JSONWriter::OPTIONS_PRETTY_PRINT, &text); + + MockHttpClientResponse* response = new StrictMock<MockHttpClientResponse>; + EXPECT_CALL(*response, GetStatusCode()) + .Times(AtLeast(1)) + .WillRepeatedly(Return(status_code)); + EXPECT_CALL(*response, GetContentType()) + .Times(AtLeast(1)) + .WillRepeatedly(Return("application/json")); + EXPECT_CALL(*response, GetData()) + .Times(AtLeast(1)) + .WillRepeatedly(ReturnRefOfCopy(text)); + return response; } -void OAuth2HandlerDeregister(const ServerRequest& request, - ServerResponse* response) { - base::DictionaryValue json; - EXPECT_EQ("refresh_token", request.GetFormField("grant_type")); - EXPECT_EQ(test_data::kRefreshToken, request.GetFormField("refresh_token")); - EXPECT_EQ(test_data::kClientId, request.GetFormField("client_id")); - EXPECT_EQ(test_data::kClientSecret, request.GetFormField("client_secret")); - json.SetString("error", "invalid_grant"); - response->ReplyJson(chromeos::http::status_code::BadRequest, &json); -} - -void DeviceInfoHandler(const ServerRequest& request, ServerResponse* response) { - std::string auth = "Bearer "; - auth += test_data::kAccessToken; - EXPECT_EQ(auth, - request.GetHeader(chromeos::http::request_header::kAuthorization)); - response->ReplyJson(chromeos::http::status_code::Ok, - { - {"channel.supportedType", "xmpp"}, - {"deviceKind", "vendor"}, - {"id", test_data::kDeviceId}, - {"kind", "clouddevices#device"}, - }); -} - -void FinalizeTicketHandler(const ServerRequest& request, - ServerResponse* response) { - EXPECT_EQ(test_data::kApiKey, request.GetFormField("key")); - EXPECT_TRUE(request.GetData().empty()); - - response->ReplyJson( - chromeos::http::status_code::Ok, - { - {"id", test_data::kClaimTicketId}, - {"kind", "clouddevices#registrationTicket"}, - {"oauthClientId", test_data::kClientId}, - {"userEmail", "user@email.com"}, - {"deviceDraft.id", test_data::kDeviceId}, - {"deviceDraft.kind", "clouddevices#device"}, - {"deviceDraft.channel.supportedType", "xmpp"}, - {"robotAccountEmail", test_data::kRobotAccountEmail}, - {"robotAccountAuthorizationCode", test_data::kRobotAccountAuthCode}, - }); +std::pair<std::string, std::string> GetAuthHeader() { + return {std::string{"Authorization"}, + std::string{"Bearer "} + test_data::kAccessToken}; } } // anonymous namespace @@ -188,19 +120,14 @@ std::unique_ptr<StorageInterface> storage{new MemStorage}; storage_ = storage.get(); storage->Save(data_); - transport_ = std::make_shared<chromeos::http::fake::Transport>(); command_manager_ = std::make_shared<CommandManager>(); state_manager_ = std::make_shared<StateManager>(&mock_state_change_queue_); std::unique_ptr<BuffetConfig> config{new BuffetConfig{std::move(storage)}}; config_ = config.get(); - dev_reg_.reset(new DeviceRegistrationInfo{command_manager_, - state_manager_, - std::move(config), - transport_, - nullptr, - true, - nullptr}); + dev_reg_.reset(new DeviceRegistrationInfo{command_manager_, state_manager_, + std::move(config), &http_client_, + nullptr, true, nullptr}); ReloadConfig(); } @@ -252,10 +179,10 @@ return dev_reg_->registration_status_; } + StrictMock<MockHttpClient> http_client_; base::DictionaryValue data_; StorageInterface* storage_{nullptr}; BuffetConfig* config_{nullptr}; - std::shared_ptr<chromeos::http::fake::Transport> transport_; std::unique_ptr<DeviceRegistrationInfo> dev_reg_; std::shared_ptr<CommandManager> command_manager_; StrictMock<MockStateChangeQueueInterface> mock_state_change_queue_; @@ -298,18 +225,30 @@ TEST_F(DeviceRegistrationInfoTest, HaveRegistrationCredentials) { EXPECT_FALSE(dev_reg_->HaveRegistrationCredentials()); - EXPECT_EQ(0, transport_->GetRequestCount()); - SetDefaultDeviceRegistration(&data_); storage_->Save(data_); ReloadConfig(); - transport_->AddHandler(dev_reg_->GetOAuthURL("token"), - chromeos::http::request_type::kPost, - base::Bind(OAuth2Handler)); - transport_->ResetRequestCount(); + EXPECT_CALL(http_client_, + MockSendRequest("POST", dev_reg_->GetOAuthURL("token"), _, + "application/x-www-form-urlencoded", + HttpClient::Headers{}, _)) + .WillOnce(WithArgs<2>(Invoke([](const std::string& data) { + EXPECT_EQ("refresh_token", GetFormField(data, "grant_type")); + EXPECT_EQ(test_data::kRefreshToken, + GetFormField(data, "refresh_token")); + EXPECT_EQ(test_data::kClientId, GetFormField(data, "client_id")); + EXPECT_EQ(test_data::kClientSecret, + GetFormField(data, "client_secret")); + + base::DictionaryValue json; + json.SetString("access_token", test_data::kAccessToken); + json.SetInteger("expires_in", 3600); + + return ReplyWithJson(200, json); + }))); + EXPECT_TRUE(RefreshAccessToken(nullptr)); - EXPECT_EQ(1, transport_->GetRequestCount()); EXPECT_TRUE(dev_reg_->HaveRegistrationCredentials()); } @@ -319,13 +258,25 @@ ReloadConfig(); EXPECT_EQ(RegistrationStatus::kConnecting, GetRegistrationStatus()); - transport_->AddHandler(dev_reg_->GetOAuthURL("token"), - chromeos::http::request_type::kPost, - base::Bind(OAuth2HandlerFail)); - transport_->ResetRequestCount(); + EXPECT_CALL(http_client_, + MockSendRequest("POST", dev_reg_->GetOAuthURL("token"), _, + "application/x-www-form-urlencoded", + HttpClient::Headers{}, _)) + .WillOnce(WithArgs<2>(Invoke([](const std::string& data) { + EXPECT_EQ("refresh_token", GetFormField(data, "grant_type")); + EXPECT_EQ(test_data::kRefreshToken, + GetFormField(data, "refresh_token")); + EXPECT_EQ(test_data::kClientId, GetFormField(data, "client_id")); + EXPECT_EQ(test_data::kClientSecret, + GetFormField(data, "client_secret")); + + base::DictionaryValue json; + json.SetString("error", "unable_to_authenticate"); + return ReplyWithJson(400, json); + }))); + chromeos::ErrorPtr error; EXPECT_FALSE(RefreshAccessToken(&error)); - EXPECT_EQ(1, transport_->GetRequestCount()); EXPECT_TRUE(error->HasError(kErrorDomainOAuth2, "unable_to_authenticate")); EXPECT_EQ(RegistrationStatus::kConnecting, GetRegistrationStatus()); } @@ -336,13 +287,25 @@ ReloadConfig(); EXPECT_EQ(RegistrationStatus::kConnecting, GetRegistrationStatus()); - transport_->AddHandler(dev_reg_->GetOAuthURL("token"), - chromeos::http::request_type::kPost, - base::Bind(OAuth2HandlerDeregister)); - transport_->ResetRequestCount(); + EXPECT_CALL(http_client_, + MockSendRequest("POST", dev_reg_->GetOAuthURL("token"), _, + "application/x-www-form-urlencoded", + HttpClient::Headers{}, _)) + .WillOnce(WithArgs<2>(Invoke([](const std::string& data) { + EXPECT_EQ("refresh_token", GetFormField(data, "grant_type")); + EXPECT_EQ(test_data::kRefreshToken, + GetFormField(data, "refresh_token")); + EXPECT_EQ(test_data::kClientId, GetFormField(data, "client_id")); + EXPECT_EQ(test_data::kClientSecret, + GetFormField(data, "client_secret")); + + base::DictionaryValue json; + json.SetString("error", "invalid_grant"); + return ReplyWithJson(400, json); + }))); + chromeos::ErrorPtr error; EXPECT_FALSE(RefreshAccessToken(&error)); - EXPECT_EQ(1, transport_->GetRequestCount()); EXPECT_TRUE(error->HasError(kErrorDomainOAuth2, "invalid_grant")); EXPECT_EQ(RegistrationStatus::kInvalidCredentials, GetRegistrationStatus()); } @@ -353,17 +316,25 @@ ReloadConfig(); SetAccessToken(); - transport_->AddHandler(dev_reg_->GetDeviceURL(), - chromeos::http::request_type::kGet, - base::Bind(DeviceInfoHandler)); - transport_->ResetRequestCount(); + EXPECT_CALL(http_client_, + MockSendRequest("GET", dev_reg_->GetDeviceURL(), _, + "application/json; charset=utf-8", + HttpClient::Headers{GetAuthHeader()}, _)) + .WillOnce(WithArgs<2>(Invoke([](const std::string& data) { + base::DictionaryValue json; + json.SetString("channel.supportedType", "xmpp"); + json.SetString("deviceKind", "vendor"); + json.SetString("id", test_data::kDeviceId); + json.SetString("kind", "clouddevices#device"); + return ReplyWithJson(200, json); + }))); + base::MessageLoopForIO message_loop; base::RunLoop run_loop; bool succeeded = false; - auto on_success = [&run_loop, &succeeded, this]( - const base::DictionaryValue& info) { - EXPECT_EQ(1, transport_->GetRequestCount()); + auto on_success = [&run_loop, &succeeded, + this](const base::DictionaryValue& info) { std::string id; EXPECT_TRUE(info.GetString("id", &id)); EXPECT_EQ(test_data::kDeviceId, id); @@ -380,72 +351,7 @@ } TEST_F(DeviceRegistrationInfoTest, RegisterDevice) { - auto update_ticket = [](const ServerRequest& request, - ServerResponse* response) { - EXPECT_EQ(test_data::kApiKey, request.GetFormField("key")); - auto json = request.GetDataAsJson(); - EXPECT_NE(nullptr, json.get()); - std::string value; - EXPECT_TRUE(json->GetString("id", &value)); - EXPECT_EQ(test_data::kClaimTicketId, value); - EXPECT_TRUE(json->GetString("deviceDraft.channel.supportedType", &value)); - EXPECT_EQ("pull", value); - EXPECT_TRUE(json->GetString("oauthClientId", &value)); - EXPECT_EQ(test_data::kClientId, value); - EXPECT_TRUE(json->GetString("deviceDraft.deviceKind", &value)); - EXPECT_EQ("vendor", value); - EXPECT_TRUE(json->GetString("deviceDraft.description", &value)); - EXPECT_EQ("Easy to clean", value); - EXPECT_TRUE(json->GetString("deviceDraft.location", &value)); - EXPECT_EQ("Kitchen", value); - EXPECT_TRUE(json->GetString("deviceDraft.modelManifestId", &value)); - EXPECT_EQ("AAAAA", value); - EXPECT_TRUE(json->GetString("deviceDraft.name", &value)); - EXPECT_EQ("Coffee Pot", value); - base::DictionaryValue* commandDefs = nullptr; - EXPECT_TRUE(json->GetDictionary("deviceDraft.commandDefs", &commandDefs)); - EXPECT_FALSE(commandDefs->empty()); - - auto expected = R"({ - 'base': { - 'reboot': { - 'parameters': { - 'delay': { - 'minimum': 10, - 'type': 'integer' - } - }, - 'minimalRole': 'user' - } - }, - 'robot': { - '_jump': { - 'parameters': { - '_height': { - 'type': 'integer' - } - }, - 'minimalRole': 'user' - } - } - })"; - EXPECT_JSON_EQ(expected, *commandDefs); - - base::DictionaryValue json_resp; - json_resp.SetString("id", test_data::kClaimTicketId); - json_resp.SetString("kind", "clouddevices#registrationTicket"); - json_resp.SetString("oauthClientId", test_data::kClientId); - base::DictionaryValue* device_draft = nullptr; - EXPECT_TRUE(json->GetDictionary("deviceDraft", &device_draft)); - device_draft = device_draft->DeepCopy(); - device_draft->SetString("id", test_data::kDeviceId); - device_draft->SetString("kind", "clouddevices#device"); - json_resp.Set("deviceDraft", device_draft); - - response->ReplyJson(chromeos::http::status_code::Ok, &json_resp); - }; - - auto json_base = unittests::CreateDictionaryValue(R"({ + auto json_base = CreateDictionaryValue(R"({ 'base': { 'reboot': { 'parameters': {'delay': 'integer'}, @@ -460,7 +366,7 @@ } })"); EXPECT_TRUE(command_manager_->LoadBaseCommands(*json_base, nullptr)); - auto json_cmds = unittests::CreateDictionaryValue(R"({ + auto json_cmds = CreateDictionaryValue(R"({ 'base': { 'reboot': { 'parameters': {'delay': {'minimum': 10}}, @@ -478,24 +384,124 @@ })"); EXPECT_TRUE(command_manager_->LoadCommands(*json_cmds, "", nullptr)); - transport_->AddHandler( - dev_reg_->GetServiceURL(std::string("registrationTickets/") + - test_data::kClaimTicketId), - chromeos::http::request_type::kPatch, base::Bind(update_ticket)); - std::string ticket_url = dev_reg_->GetServiceURL( - "registrationTickets/" + std::string(test_data::kClaimTicketId)); - transport_->AddHandler(ticket_url + "/finalize", - chromeos::http::request_type::kPost, - base::Bind(FinalizeTicketHandler)); + std::string ticket_url = dev_reg_->GetServiceURL("registrationTickets/") + + test_data::kClaimTicketId; + EXPECT_CALL(http_client_, + MockSendRequest( + "PATCH", ticket_url + "?key=" + test_data::kApiKey, _, + "application/json; charset=utf-8", HttpClient::Headers{}, _)) + .WillOnce(WithArgs<2>(Invoke([](const std::string& data) { + auto json = unittests::CreateDictionaryValue(data); + EXPECT_NE(nullptr, json.get()); + std::string value; + EXPECT_TRUE(json->GetString("id", &value)); + EXPECT_EQ(test_data::kClaimTicketId, value); + EXPECT_TRUE( + json->GetString("deviceDraft.channel.supportedType", &value)); + EXPECT_EQ("pull", value); + EXPECT_TRUE(json->GetString("oauthClientId", &value)); + EXPECT_EQ(test_data::kClientId, value); + EXPECT_TRUE(json->GetString("deviceDraft.deviceKind", &value)); + EXPECT_EQ("vendor", value); + EXPECT_TRUE(json->GetString("deviceDraft.description", &value)); + EXPECT_EQ("Easy to clean", value); + EXPECT_TRUE(json->GetString("deviceDraft.location", &value)); + EXPECT_EQ("Kitchen", value); + EXPECT_TRUE(json->GetString("deviceDraft.modelManifestId", &value)); + EXPECT_EQ("AAAAA", value); + EXPECT_TRUE(json->GetString("deviceDraft.name", &value)); + EXPECT_EQ("Coffee Pot", value); + base::DictionaryValue* commandDefs = nullptr; + EXPECT_TRUE( + json->GetDictionary("deviceDraft.commandDefs", &commandDefs)); + EXPECT_FALSE(commandDefs->empty()); - transport_->AddHandler(dev_reg_->GetOAuthURL("token"), - chromeos::http::request_type::kPost, - base::Bind(OAuth2Handler)); + auto expected = R"({ + 'base': { + 'reboot': { + 'parameters': { + 'delay': { + 'minimum': 10, + 'type': 'integer' + } + }, + 'minimalRole': 'user' + } + }, + 'robot': { + '_jump': { + 'parameters': { + '_height': { + 'type': 'integer' + } + }, + 'minimalRole': 'user' + } + } + })"; + EXPECT_JSON_EQ(expected, *commandDefs); + + base::DictionaryValue json_resp; + json_resp.SetString("id", test_data::kClaimTicketId); + json_resp.SetString("kind", "clouddevices#registrationTicket"); + json_resp.SetString("oauthClientId", test_data::kClientId); + base::DictionaryValue* device_draft = nullptr; + EXPECT_TRUE(json->GetDictionary("deviceDraft", &device_draft)); + device_draft = device_draft->DeepCopy(); + device_draft->SetString("id", test_data::kDeviceId); + device_draft->SetString("kind", "clouddevices#device"); + json_resp.Set("deviceDraft", device_draft); + + return ReplyWithJson(200, json_resp); + }))); + + EXPECT_CALL(http_client_, + MockSendRequest( + "POST", ticket_url + "/finalize?key=" + test_data::kApiKey, + "", _, HttpClient::Headers{}, _)) + .WillOnce(InvokeWithoutArgs([]() { + base::DictionaryValue json; + json.SetString("id", test_data::kClaimTicketId); + json.SetString("kind", "clouddevices#registrationTicket"); + json.SetString("oauthClientId", test_data::kClientId); + json.SetString("userEmail", "user@email.com"); + json.SetString("deviceDraft.id", test_data::kDeviceId); + json.SetString("deviceDraft.kind", "clouddevices#device"); + json.SetString("deviceDraft.channel.supportedType", "xmpp"); + json.SetString("robotAccountEmail", test_data::kRobotAccountEmail); + json.SetString("robotAccountAuthorizationCode", + test_data::kRobotAccountAuthCode); + return ReplyWithJson(200, json); + })); + + EXPECT_CALL(http_client_, + MockSendRequest("POST", dev_reg_->GetOAuthURL("token"), _, + "application/x-www-form-urlencoded", + HttpClient::Headers{}, _)) + .WillOnce(WithArgs<2>(Invoke([](const std::string& data) { + EXPECT_EQ("authorization_code", GetFormField(data, "grant_type")); + EXPECT_EQ(test_data::kRobotAccountAuthCode, GetFormField(data, "code")); + EXPECT_EQ(test_data::kClientId, GetFormField(data, "client_id")); + EXPECT_EQ(test_data::kClientSecret, + GetFormField(data, "client_secret")); + + EXPECT_EQ("oob", GetFormField(data, "redirect_uri")); + EXPECT_EQ("https://www.googleapis.com/auth/clouddevices", + GetFormField(data, "scope")); + + base::DictionaryValue json; + json.SetString("access_token", test_data::kAccessToken); + json.SetString("token_type", "Bearer"); + json.SetString("refresh_token", test_data::kRefreshToken); + json.SetInteger("expires_in", 3600); + + return ReplyWithJson(200, json); + }))); + std::string device_id = dev_reg_->RegisterDevice(test_data::kClaimTicketId, nullptr); EXPECT_EQ(test_data::kDeviceId, device_id); - EXPECT_EQ(3, transport_->GetRequestCount()); EXPECT_EQ(RegistrationStatus::kConnecting, GetRegistrationStatus()); // Validate the device info saved to storage... @@ -512,8 +518,8 @@ } TEST_F(DeviceRegistrationInfoTest, OOBRegistrationStatus) { - // After we've been initialized, we should be either offline or unregistered, - // depending on whether or not we've found credentials. + // After we've been initialized, we should be either offline or + // unregistered, depending on whether or not we've found credentials. EXPECT_EQ(RegistrationStatus::kUnconfigured, GetRegistrationStatus()); // Put some credentials into our state, make sure we call that offline. SetDefaultDeviceRegistration(&data_); @@ -528,7 +534,7 @@ ReloadConfig(); SetAccessToken(); - auto json_cmds = unittests::CreateDictionaryValue(R"({ + auto json_cmds = CreateDictionaryValue(R"({ 'robot': { '_jump': { 'parameters': {'_height': 'integer'}, @@ -542,7 +548,7 @@ const std::string command_url = dev_reg_->GetServiceURL("commands/1234"); - auto commands_json = unittests::CreateValue(R"([{ + auto commands_json = CreateValue(R"([{ 'name':'robot._jump', 'id':'1234', 'parameters': {'_height': 100}, @@ -555,55 +561,52 @@ auto command = command_manager_->FindCommand("1234"); ASSERT_NE(nullptr, command); - // UpdateCommand when setting command results. - auto update_command_results = [](const ServerRequest& request, - ServerResponse* response) { - EXPECT_EQ(R"({"results":{"status":"Ok"}})", - request.GetDataAsNormalizedJsonString()); - response->ReplyJson(chromeos::http::status_code::Ok, - chromeos::http::FormFieldList{}); - }; - - transport_->AddHandler(command_url, chromeos::http::request_type::kPatch, - base::Bind(update_command_results)); - + EXPECT_CALL(http_client_, + MockSendRequest("PATCH", command_url, _, + "application/json; charset=utf-8", + HttpClient::Headers{GetAuthHeader()}, _)) + .WillOnce(WithArgs<2>(Invoke([](const std::string& data) { + EXPECT_JSON_EQ(R"({"results":{"status":"Ok"}})", + *CreateDictionaryValue(data)); + base::DictionaryValue json; + return ReplyWithJson(200, json); + }))); EXPECT_TRUE( command->SetResults(*CreateDictionaryValue("{'status': 'Ok'}"), nullptr)); + Mock::VerifyAndClearExpectations(&http_client_); - // UpdateCommand when setting command progress. - int count = 0; // This will be called twice... - auto update_command_progress = [&count](const ServerRequest& request, - ServerResponse* response) { - if (count++ == 0) { - EXPECT_EQ(R"({"state":"inProgress"})", - request.GetDataAsNormalizedJsonString()); - } else { - EXPECT_EQ(R"({"progress":{"progress":18}})", - request.GetDataAsNormalizedJsonString()); - } - response->ReplyJson(chromeos::http::status_code::Ok, - chromeos::http::FormFieldList{}); - }; + EXPECT_CALL(http_client_, + MockSendRequest("PATCH", command_url, _, + "application/json; charset=utf-8", + HttpClient::Headers{GetAuthHeader()}, _)) + .WillOnce(WithArgs<2>(Invoke([](const std::string& data) { + EXPECT_JSON_EQ(R"({"state":"inProgress"})", + *CreateDictionaryValue(data)); + base::DictionaryValue json; + return ReplyWithJson(200, json); + }))) + .WillOnce(WithArgs<2>(Invoke([](const std::string& data) { + EXPECT_JSON_EQ(R"({"progress":{"progress":18}})", + *CreateDictionaryValue(data)); + base::DictionaryValue json; + return ReplyWithJson(200, json); + }))); + EXPECT_TRUE( + command->SetProgress(*CreateDictionaryValue("{'progress':18}"), nullptr)); + Mock::VerifyAndClearExpectations(&http_client_); - transport_->AddHandler(command_url, chromeos::http::request_type::kPatch, - base::Bind(update_command_progress)); - - EXPECT_TRUE(command->SetProgress(*CreateDictionaryValue("{'progress': 18}"), - nullptr)); - - // UpdateCommand when changing command status. - auto update_command_state = [](const ServerRequest& request, - ServerResponse* response) { - EXPECT_EQ(R"({"state":"cancelled"})", - request.GetDataAsNormalizedJsonString()); - response->ReplyJson(chromeos::http::status_code::Ok, - chromeos::http::FormFieldList{}); - }; - - transport_->AddHandler(command_url, chromeos::http::request_type::kPatch, - base::Bind(update_command_state)); - + EXPECT_CALL(http_client_, + MockSendRequest("PATCH", command_url, _, + "application/json; charset=utf-8", + HttpClient::Headers{GetAuthHeader()}, _)) + .WillOnce(WithArgs<2>(Invoke([](const std::string& data) { + EXPECT_JSON_EQ(R"({"state":"cancelled"})", + *CreateDictionaryValue(data)); + base::DictionaryValue json; + return ReplyWithJson(200, json); + }))); command->Cancel(); + Mock::VerifyAndClearExpectations(&http_client_); } } // namespace weave
diff --git a/libweave/src/mock_http_client.cc b/libweave/src/mock_http_client.cc new file mode 100644 index 0000000..d4307a4 --- /dev/null +++ b/libweave/src/mock_http_client.cc
@@ -0,0 +1,43 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <weave/mock_http_client.h> + +#include <memory> +#include <string> + +namespace weave { +namespace unittests { + +std::unique_ptr<HttpClient::Response> MockHttpClient::SendRequestAndBlock( + const std::string& method, + const std::string& url, + const std::string& data, + const std::string& mime_type, + const Headers& headers, + chromeos::ErrorPtr* error) { + return std::unique_ptr<Response>{ + MockSendRequest(method, url, data, mime_type, headers, error)}; +} + +int MockHttpClient::SendRequest(const std::string& method, + const std::string& url, + const std::string& data, + const std::string& mime_type, + const Headers& headers, + const SuccessCallback& success_callback, + const ErrorCallback& error_callback) { + chromeos::ErrorPtr error; + std::unique_ptr<Response> response{ + MockSendRequest(method, url, data, mime_type, headers, &error)}; + if (response) { + success_callback.Run(0, *response); + } else { + error_callback.Run(0, error.get()); + } + return 0; +} + +} // namespace unittests +} // namespace weave