Added version field to state and migrate from unversioned data

State in older format must be migrated to new one.

BUG:24869252
Change-Id: Iacadd1efb9e45e840b029652f8582d815ae0380e
Reviewed-on: https://weave-review.googlesource.com/1305
Reviewed-by: Vitaly Buka <vitalybuka@google.com>
diff --git a/libweave/src/config.cc b/libweave/src/config.cc
index 4f59497..b1b3e30 100644
--- a/libweave/src/config.cc
+++ b/libweave/src/config.cc
@@ -22,6 +22,8 @@
 
 namespace config_keys {
 
+const char kVersion[] = "version";
+
 const char kClientId[] = "client_id";
 const char kClientSecret[] = "client_secret";
 const char kApiKey[] = "api_key";
@@ -44,6 +46,17 @@
 
 namespace {
 
+const int kCurrentConfigVersion = 1;
+
+void MigrateFromV0(base::DictionaryValue* dict) {
+  std::string cloud_id;
+  if (dict->GetString(config_keys::kCloudId, &cloud_id) && !cloud_id.empty())
+    return;
+  scoped_ptr<base::Value> tmp;
+  if (dict->Remove(config_keys::kDeviceId, &tmp))
+    dict->Set(config_keys::kCloudId, std::move(tmp));
+}
+
 Config::Settings CreateDefaultSettings() {
   Config::Settings result;
   result.oauth_url = "https://accounts.google.com/o/oauth2/";
@@ -115,12 +128,25 @@
     return;
 
   auto value = base::JSONReader::Read(json_string);
-  const base::DictionaryValue* dict = nullptr;
+  base::DictionaryValue* dict = nullptr;
   if (!value || !value->GetAsDictionary(&dict)) {
     LOG(ERROR) << "Failed to parse settings.";
     return;
   }
 
+  int loaded_version = 0;
+  dict->GetInteger(config_keys::kVersion, &loaded_version);
+
+  if (loaded_version != kCurrentConfigVersion) {
+    LOG(INFO) << "State version mismatch. expected: " << kCurrentConfigVersion
+              << ", loaded: " << loaded_version;
+    save_ = true;
+  }
+
+  if (loaded_version == 0) {
+    MigrateFromV0(dict);
+  }
+
   std::string tmp;
   bool tmp_bool{false};
 
@@ -184,6 +210,8 @@
     return;
 
   base::DictionaryValue dict;
+  dict.SetInteger(config_keys::kVersion, kCurrentConfigVersion);
+
   dict.SetString(config_keys::kClientId, settings_.client_id);
   dict.SetString(config_keys::kClientSecret, settings_.client_secret);
   dict.SetString(config_keys::kApiKey, settings_.api_key);
diff --git a/libweave/src/config_unittest.cc b/libweave/src/config_unittest.cc
index f65f203..3078b1d 100644
--- a/libweave/src/config_unittest.cc
+++ b/libweave/src/config_unittest.cc
@@ -79,8 +79,35 @@
   EXPECT_EQ("", GetSettings().secret);
 }
 
+TEST_F(ConfigTest, LoadStateV0) {
+  EXPECT_CALL(config_store_, LoadSettings())
+      .WillOnce(Return(R"({
+    "device_id": "state_device_id"
+  })"));
+
+  EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
+  config_.Load();
+
+  EXPECT_EQ("state_device_id", GetSettings().cloud_id);
+  EXPECT_FALSE(GetSettings().device_id.empty());
+  EXPECT_NE(GetSettings().cloud_id, GetSettings().device_id);
+
+  EXPECT_CALL(config_store_, LoadSettings())
+      .WillOnce(Return(R"({
+    "device_id": "state_device_id",
+    "cloud_id": "state_cloud_id"
+  })"));
+
+  EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
+  config_.Load();
+
+  EXPECT_EQ("state_cloud_id", GetSettings().cloud_id);
+  EXPECT_EQ("state_device_id", GetSettings().device_id);
+}
+
 TEST_F(ConfigTest, LoadState) {
   auto state = R"({
+    "version": 1,
     "api_key": "state_api_key",
     "client_id": "state_client_id",
     "client_secret": "state_client_secret",
@@ -205,6 +232,7 @@
   EXPECT_CALL(config_store_, SaveSettings(_))
       .WillOnce(Invoke([](const std::string& json) {
         auto expected = R"({
+          'version': 1,
           'api_key': 'set_api_key',
           'client_id': 'set_client_id',
           'client_secret': 'set_client_secret',
diff --git a/libweave/src/weave_unittest.cc b/libweave/src/weave_unittest.cc
index 74e8c41..c61bc69 100644
--- a/libweave/src/weave_unittest.cc
+++ b/libweave/src/weave_unittest.cc
@@ -195,6 +195,7 @@
     }
 
     EXPECT_CALL(dns_sd_, PublishService("_privet._tcp", 11, MatchTxt(txt)))
+        .Times(AtMost(1))
         .WillOnce(Return());
   }