buffet: Added unit tests for DeviceRegistrationInfo class

Added unit tests for GCD registration workflow in Buffet.

BUG=chromium:367381
TEST=Unit tests pass (old and new).

Change-Id: Ia3ad5f028ae6fc7f3d2acdf4648ceb88cc4e00ef
Reviewed-on: https://chromium-review.googlesource.com/197568
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/device_registration_info.cc b/buffet/device_registration_info.cc
index de89aff..c9aea9f 100644
--- a/buffet/device_registration_info.cc
+++ b/buffet/device_registration_info.cc
@@ -4,13 +4,15 @@
 
 #include "buffet/device_registration_info.h"
 
+#include <base/file_util.h>
+#include <base/files/important_file_writer.h>
 #include <base/json/json_reader.h>
 #include <base/json/json_writer.h>
 #include <base/values.h>
-#include <base/file_util.h>
 #include <memory>
 
 #include "buffet/data_encoding.h"
+#include "buffet/device_registration_storage_keys.h"
 #include "buffet/http_transport_curl.h"
 #include "buffet/http_utils.h"
 #include "buffet/mime_utils.h"
@@ -20,7 +22,9 @@
 using namespace chromeos;
 using namespace chromeos::data_encoding;
 
-namespace {
+namespace buffet {
+namespace storage_keys {
+
 // Persistent keys
 const char kClientId[]      = "client_id";
 const char kClientSecret[]  = "client_secret";
@@ -35,6 +39,11 @@
 const char kSystemName[]    = "system_name";
 const char kDisplayName[]   = "display_name";
 
+}  // namespace storage_keys
+}  // namespace buffet
+
+namespace {
+
 const base::FilePath::CharType kDeviceInfoFilePath[] =
     FILE_PATH_LITERAL("/var/lib/buffet/device_reg_info");
 
@@ -85,21 +94,52 @@
   return url::AppendQueryParams(result, params);
 }
 
+class FileStorage : public buffet::DeviceRegistrationInfo::StorageInterface {
+ public:
+  virtual std::unique_ptr<base::Value> Load() override {
+    // TODO(avakulenko): Figure out security implications of storing
+    // this data unencrypted.
+    std::string json;
+    if (!base::ReadFileToString(GetFilePath(), &json))
+      return std::unique_ptr<base::Value>();
 
-} // anonymous namespace
+    return std::unique_ptr<base::Value>(base::JSONReader::Read(json));
+  }
+
+  virtual bool Save(const base::Value* config) {
+    // TODO(avakulenko): Figure out security implications of storing
+    // this data unencrypted.
+    std::string json;
+    base::JSONWriter::WriteWithOptions(config,
+                                       base::JSONWriter::OPTIONS_PRETTY_PRINT,
+                                       &json);
+    return base::ImportantFileWriter::WriteFileAtomically(GetFilePath(), json);
+  }
+
+ private:
+  base::FilePath GetFilePath() const{
+    return base::FilePath(kDeviceInfoFilePath);
+  }
+};
+
+}  // anonymous namespace
 
 namespace buffet {
+
 DeviceRegistrationInfo::DeviceRegistrationInfo()
-    : transport_(new http::curl::Transport()){
+    : transport_(new http::curl::Transport()),
+      storage_(new FileStorage()) {
 }
 
 DeviceRegistrationInfo::DeviceRegistrationInfo(
-    std::shared_ptr<http::Transport> transport) : transport_(transport) {
+    std::shared_ptr<http::Transport> transport,
+    std::shared_ptr<StorageInterface> storage) : transport_(transport),
+                                                 storage_(storage) {
 }
 
 std::pair<std::string, std::string>
     DeviceRegistrationInfo::GetAuthorizationHeader() const {
-  return BuildAuthHeader(/*"Bearer"*/"OAuth", access_token_);
+  return BuildAuthHeader("Bearer", access_token_);
 }
 
 std::string DeviceRegistrationInfo::GetServiceURL(
@@ -123,13 +163,7 @@
 }
 
 bool DeviceRegistrationInfo::Load() {
-  // TODO(avakulenko): Figure out security implications of storing
-  // this data unencrypted.
-  std::string json;
-  if (!base::ReadFileToString(base::FilePath(kDeviceInfoFilePath), &json))
-    return false;
-
-  auto value = std::unique_ptr<base::Value>(base::JSONReader::Read(json));
+  auto value = storage_->Load();
   const base::DictionaryValue* dict = nullptr;
   if (!value || !value->GetAsDictionary(&dict))
     return false;
@@ -137,28 +171,28 @@
   // Get the values into temp variables first to make sure we can get
   // all the data correctly before changing the state of this object.
   std::string client_id;
-  if (!dict->GetString(kClientId, &client_id))
+  if (!dict->GetString(storage_keys::kClientId, &client_id))
     return false;
   std::string client_secret;
-  if (!dict->GetString(kClientSecret, &client_secret))
+  if (!dict->GetString(storage_keys::kClientSecret, &client_secret))
     return false;
   std::string api_key;
-  if (!dict->GetString(kApiKey, &api_key))
+  if (!dict->GetString(storage_keys::kApiKey, &api_key))
     return false;
   std::string refresh_token;
-  if (!dict->GetString(kRefreshToken, &refresh_token))
+  if (!dict->GetString(storage_keys::kRefreshToken, &refresh_token))
     return false;
   std::string device_id;
-  if (!dict->GetString(kDeviceId, &device_id))
+  if (!dict->GetString(storage_keys::kDeviceId, &device_id))
     return false;
   std::string oauth_url;
-  if (!dict->GetString(kOAuthURL, &oauth_url))
+  if (!dict->GetString(storage_keys::kOAuthURL, &oauth_url))
     return false;
   std::string service_url;
-  if (!dict->GetString(kServiceURL, &service_url))
+  if (!dict->GetString(storage_keys::kServiceURL, &service_url))
     return false;
   std::string device_robot_account;
-  if (!dict->GetString(kRobotAccount, &device_robot_account))
+  if (!dict->GetString(storage_keys::kRobotAccount, &device_robot_account))
     return false;
 
   client_id_            = client_id;
@@ -173,26 +207,16 @@
 }
 
 bool DeviceRegistrationInfo::Save() const {
-  // TODO(avakulenko): Figure out security implications of storing
-  // this data unencrypted.
   base::DictionaryValue dict;
-  dict.SetString(kClientId,     client_id_);
-  dict.SetString(kClientSecret, client_secret_);
-  dict.SetString(kApiKey,       api_key_);
-  dict.SetString(kRefreshToken, refresh_token_);
-  dict.SetString(kDeviceId,     device_id_);
-  dict.SetString(kOAuthURL,     oauth_url_);
-  dict.SetString(kServiceURL,   service_url_);
-  dict.SetString(kRobotAccount, device_robot_account_);
-
-  std::string json;
-  base::JSONWriter::WriteWithOptions(&dict,
-                                     base::JSONWriter::OPTIONS_PRETTY_PRINT,
-                                     &json);
-  int count = file_util::WriteFile(base::FilePath(kDeviceInfoFilePath),
-                                   json.data(), static_cast<int>(json.size()));
-
-  return (count > 0);
+  dict.SetString(storage_keys::kClientId,     client_id_);
+  dict.SetString(storage_keys::kClientSecret, client_secret_);
+  dict.SetString(storage_keys::kApiKey,       api_key_);
+  dict.SetString(storage_keys::kRefreshToken, refresh_token_);
+  dict.SetString(storage_keys::kDeviceId,     device_id_);
+  dict.SetString(storage_keys::kOAuthURL,     oauth_url_);
+  dict.SetString(storage_keys::kServiceURL,   service_url_);
+  dict.SetString(storage_keys::kRobotAccount, device_robot_account_);
+  return storage_->Save(&dict);
 }
 
 bool DeviceRegistrationInfo::CheckRegistration() {
@@ -209,7 +233,7 @@
 }
 
 bool DeviceRegistrationInfo::ValidateAndRefreshAccessToken() {
-  LOG(INFO) << " Checking access token expiration.";
+  LOG(INFO) << "Checking access token expiration.";
   if (!access_token_.empty() &&
       !access_token_expiration_.is_null() &&
       access_token_expiration_ > base::Time::Now()) {
@@ -284,29 +308,29 @@
 std::string DeviceRegistrationInfo::StartRegistration(
     const std::map<std::string, std::shared_ptr<base::Value>>& params,
   std::string* error_msg) {
-  GetParamValue(params, kClientId, &client_id_);
-  GetParamValue(params, kClientSecret, &client_secret_);
-  GetParamValue(params, kApiKey, &api_key_);
-  GetParamValue(params, kDeviceId, &device_id_);
-  GetParamValue(params, kDeviceKind, &device_kind_);
-  GetParamValue(params, kSystemName, &system_name_);
-  GetParamValue(params, kDisplayName, &display_name_);
-  GetParamValue(params, kOAuthURL, &oauth_url_);
-  GetParamValue(params, kServiceURL, &service_url_);
+  GetParamValue(params, storage_keys::kClientId, &client_id_);
+  GetParamValue(params, storage_keys::kClientSecret, &client_secret_);
+  GetParamValue(params, storage_keys::kApiKey, &api_key_);
+  GetParamValue(params, storage_keys::kDeviceId, &device_id_);
+  GetParamValue(params, storage_keys::kDeviceKind, &device_kind_);
+  GetParamValue(params, storage_keys::kSystemName, &system_name_);
+  GetParamValue(params, storage_keys::kDisplayName, &display_name_);
+  GetParamValue(params, storage_keys::kOAuthURL, &oauth_url_);
+  GetParamValue(params, storage_keys::kServiceURL, &service_url_);
 
-  if (!CheckParam(kClientId, client_id_, error_msg))
+  if (!CheckParam(storage_keys::kClientId, client_id_, error_msg))
     return std::string();
-  if (!CheckParam(kClientSecret, client_secret_, error_msg))
+  if (!CheckParam(storage_keys::kClientSecret, client_secret_, error_msg))
     return std::string();
-  if (!CheckParam(kApiKey, api_key_, error_msg))
+  if (!CheckParam(storage_keys::kApiKey, api_key_, error_msg))
     return std::string();
-  if (!CheckParam(kDeviceKind, device_kind_, error_msg))
+  if (!CheckParam(storage_keys::kDeviceKind, device_kind_, error_msg))
     return std::string();
-  if (!CheckParam(kSystemName, system_name_, error_msg))
+  if (!CheckParam(storage_keys::kSystemName, system_name_, error_msg))
     return std::string();
-  if (!CheckParam(kOAuthURL, oauth_url_, error_msg))
+  if (!CheckParam(storage_keys::kOAuthURL, oauth_url_, error_msg))
     return std::string();
-  if (!CheckParam(kServiceURL, service_url_, error_msg))
+  if (!CheckParam(storage_keys::kServiceURL, service_url_, error_msg))
     return std::string();
 
   std::vector<std::pair<std::string, std::vector<std::string>>> commands = {
@@ -416,18 +440,9 @@
 
   std::string auth_code;
   url += "/finalize?key=" + api_key_;
-  do {
-    LOG(INFO) << "Sending request to: " << url;
-    response = http::PostBinary(url, nullptr, 0, transport_);
-    if (response) {
-      if (response->GetStatusCode() == http::status_code::BadRequest)
-        sleep(1);
-    }
-  }
-  while (response &&
-         response->GetStatusCode() == http::status_code::BadRequest);
-  if (response &&
-      response->GetStatusCode() == http::status_code::Ok) {
+  LOG(INFO) << "Sending request to: " << url;
+  response = http::PostBinary(url, nullptr, 0, transport_);
+  if (response && response->IsSuccessful()) {
     auto json_resp = http::ParseJsonResponse(response.get(), nullptr, nullptr);
     if (json_resp &&
         json_resp->GetString("robotAccountEmail", &device_robot_account_) &&
@@ -463,8 +478,9 @@
 
       Save();
     }
+    return true;
   }
-  return true;
+  return false;
 }
 
 }  // namespace buffet