1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include <string>
6dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
7dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "base/format_macros.h"
8dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "base/string_util.h"
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/engine/apply_updates_command.h"
10dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "chrome/browser/sync/engine/syncer.h"
11dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "chrome/browser/sync/engine/syncer_util.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/sessions/sync_session.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/syncable/directory_manager.h"
15dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "chrome/browser/sync/syncable/nigori_util.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/syncable/syncable.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/syncable/syncable_id.h"
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/test/sync/engine/syncer_command_test.h"
19dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "chrome/test/sync/engine/test_id_factory.h"
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "testing/gtest/include/gtest/gtest.h"
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace browser_sync {
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing sessions::SyncSession;
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing std::string;
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing syncable::Entry;
27dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenusing syncable::GetEncryptedDataTypes;
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing syncable::Id;
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing syncable::MutableEntry;
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing syncable::ReadTransaction;
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing syncable::ScopedDirLookup;
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing syncable::UNITTEST;
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing syncable::WriteTransaction;
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// A test fixture for tests exercising ApplyUpdatesCommand.
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass ApplyUpdatesCommandTest : public SyncerCommandTest {
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch protected:
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ApplyUpdatesCommandTest() : next_revision_(1) {}
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual ~ApplyUpdatesCommandTest() {}
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual void SetUp() {
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    workers()->clear();
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    mutable_routing_info()->clear();
45513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    // GROUP_PASSIVE worker.
46513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    workers()->push_back(make_scoped_refptr(new ModelSafeWorker()));
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    (*mutable_routing_info())[syncable::BOOKMARKS] = GROUP_PASSIVE;
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    (*mutable_routing_info())[syncable::PASSWORDS] = GROUP_PASSIVE;
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    (*mutable_routing_info())[syncable::NIGORI] = GROUP_PASSIVE;
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SyncerCommandTest::SetUp();
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
53dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Create a new unapplied bookmark node with a parent.
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void CreateUnappliedNewItemWithParent(const string& item_id,
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                        const string& parent_id) {
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ASSERT_TRUE(dir.good());
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        Id::CreateFromServerId(item_id));
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ASSERT_TRUE(entry.good());
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entry.Put(syncable::SERVER_VERSION, next_revision_++);
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entry.Put(syncable::SERVER_NON_UNIQUE_NAME, item_id);
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entry.Put(syncable::SERVER_PARENT_ID, Id::CreateFromServerId(parent_id));
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entry.Put(syncable::SERVER_IS_DIR, true);
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    sync_pb::EntitySpecifics default_bookmark_specifics;
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    default_bookmark_specifics.MutableExtension(sync_pb::bookmark);
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entry.Put(syncable::SERVER_SPECIFICS, default_bookmark_specifics);
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
73dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Create a new unapplied update without a parent.
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void CreateUnappliedNewItem(const string& item_id,
75dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                              const sync_pb::EntitySpecifics& specifics,
76dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                              bool is_unique) {
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ASSERT_TRUE(dir.good());
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        Id::CreateFromServerId(item_id));
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ASSERT_TRUE(entry.good());
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entry.Put(syncable::SERVER_VERSION, next_revision_++);
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entry.Put(syncable::SERVER_NON_UNIQUE_NAME, item_id);
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entry.Put(syncable::SERVER_PARENT_ID, syncable::kNullId);
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entry.Put(syncable::SERVER_IS_DIR, false);
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entry.Put(syncable::SERVER_SPECIFICS, specifics);
89dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    if (is_unique)  // For top-level nodes.
90dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      entry.Put(syncable::UNIQUE_SERVER_TAG, item_id);
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
93dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Create an unsynced item in the database.  If item_id is a local ID, it
94dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // will be treated as a create-new.  Otherwise, if it's a server ID, we'll
95dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // fake the server data so that it looks like it exists on the server.
96dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Returns the methandle of the created item in |metahandle_out| if not NULL.
97dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  void CreateUnsyncedItem(const Id& item_id,
98dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                          const Id& parent_id,
99dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                          const string& name,
100dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                          bool is_folder,
101dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                          syncable::ModelType model_type,
102dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                          int64* metahandle_out) {
103dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
104dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ASSERT_TRUE(dir.good());
105dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
106dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    Id predecessor_id = dir->GetLastChildId(&trans, parent_id);
107dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    MutableEntry entry(&trans, syncable::CREATE, parent_id, name);
108dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ASSERT_TRUE(entry.good());
109dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    entry.Put(syncable::ID, item_id);
110dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    entry.Put(syncable::BASE_VERSION,
111dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        item_id.ServerKnows() ? next_revision_++ : 0);
112dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    entry.Put(syncable::IS_UNSYNCED, true);
113dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    entry.Put(syncable::IS_DIR, is_folder);
114dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    entry.Put(syncable::IS_DEL, false);
115dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    entry.Put(syncable::PARENT_ID, parent_id);
116dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    entry.PutPredecessor(predecessor_id);
117dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    sync_pb::EntitySpecifics default_specifics;
118dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    syncable::AddDefaultExtensionValue(model_type, &default_specifics);
119dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    entry.Put(syncable::SPECIFICS, default_specifics);
120dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    if (item_id.ServerKnows()) {
121dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      entry.Put(syncable::SERVER_SPECIFICS, default_specifics);
122dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      entry.Put(syncable::SERVER_IS_DIR, is_folder);
123dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      entry.Put(syncable::SERVER_PARENT_ID, parent_id);
124dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      entry.Put(syncable::SERVER_IS_DEL, false);
125dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    }
126dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    if (metahandle_out)
127dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      *metahandle_out = entry.Get(syncable::META_HANDLE);
128dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
130dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  ApplyUpdatesCommand apply_updates_command_;
131dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  TestIdFactory id_factory_;
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int64 next_revision_;
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(ApplyUpdatesCommandTest);
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTEST_F(ApplyUpdatesCommandTest, Simple) {
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  string root_server_id = syncable::kNullId.GetServerId();
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateUnappliedNewItemWithParent("parent", root_server_id);
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateUnappliedNewItemWithParent("child", "parent");
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  apply_updates_command_.ExecuteImpl(session());
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::StatusController* status = session()->status_controller();
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(2, status->update_progress().AppliedUpdatesSize())
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "All updates should have been attempted";
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(0, status->conflict_progress().ConflictingItemsSize())
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "Simple update shouldn't result in conflicts";
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(2, status->update_progress().SuccessfullyAppliedUpdateCount())
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "All items should have been successfully applied";
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTEST_F(ApplyUpdatesCommandTest, UpdateWithChildrenBeforeParents) {
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Set a bunch of updates which are difficult to apply in the order
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // they're received due to dependencies on other unseen items.
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  string root_server_id = syncable::kNullId.GetServerId();
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateUnappliedNewItemWithParent("a_child_created_first", "parent");
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateUnappliedNewItemWithParent("x_child_created_first", "parent");
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateUnappliedNewItemWithParent("parent", root_server_id);
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateUnappliedNewItemWithParent("a_child_created_second", "parent");
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateUnappliedNewItemWithParent("x_child_created_second", "parent");
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  apply_updates_command_.ExecuteImpl(session());
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::StatusController* status = session()->status_controller();
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(5, status->update_progress().AppliedUpdatesSize())
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "All updates should have been attempted";
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(0, status->conflict_progress().ConflictingItemsSize())
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "Simple update shouldn't result in conflicts, even if out-of-order";
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(5, status->update_progress().SuccessfullyAppliedUpdateCount())
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "All updates should have been successfully applied";
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTEST_F(ApplyUpdatesCommandTest, NestedItemsWithUnknownParent) {
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We shouldn't be able to do anything with either of these items.
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateUnappliedNewItemWithParent("some_item", "unknown_parent");
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateUnappliedNewItemWithParent("some_other_item", "some_item");
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  apply_updates_command_.ExecuteImpl(session());
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::StatusController* status = session()->status_controller();
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(2, status->update_progress().AppliedUpdatesSize())
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "All updates should have been attempted";
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(2, status->conflict_progress().ConflictingItemsSize())
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "All updates with an unknown ancestors should be in conflict";
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(0, status->update_progress().SuccessfullyAppliedUpdateCount())
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "No item with an unknown ancestor should be applied";
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTEST_F(ApplyUpdatesCommandTest, ItemsBothKnownAndUnknown) {
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // See what happens when there's a mixture of good and bad updates.
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  string root_server_id = syncable::kNullId.GetServerId();
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateUnappliedNewItemWithParent("first_unknown_item", "unknown_parent");
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateUnappliedNewItemWithParent("first_known_item", root_server_id);
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateUnappliedNewItemWithParent("second_unknown_item", "unknown_parent");
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateUnappliedNewItemWithParent("second_known_item", "first_known_item");
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateUnappliedNewItemWithParent("third_known_item", "fourth_known_item");
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateUnappliedNewItemWithParent("fourth_known_item", root_server_id);
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  apply_updates_command_.ExecuteImpl(session());
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::StatusController* status = session()->status_controller();
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(6, status->update_progress().AppliedUpdatesSize())
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "All updates should have been attempted";
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(2, status->conflict_progress().ConflictingItemsSize())
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "The updates with unknown ancestors should be in conflict";
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(4, status->update_progress().SuccessfullyAppliedUpdateCount())
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "The updates with known ancestors should be successfully applied";
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTEST_F(ApplyUpdatesCommandTest, DecryptablePassword) {
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Decryptable password updates should be applied.
218ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  Cryptographer* cryptographer;
219ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  {
220ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // Storing the cryptographer separately is bad, but for this test we
221ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // know it's safe.
222ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
223ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      ASSERT_TRUE(dir.good());
224ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      ReadTransaction trans(dir, __FILE__, __LINE__);
225ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      cryptographer =
226ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          session()->context()->directory_manager()->GetCryptographer(&trans);
227ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  browser_sync::KeyParams params = {"localhost", "dummy", "foobar"};
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cryptographer->AddKey(params);
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_pb::EntitySpecifics specifics;
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_pb::PasswordSpecificsData data;
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  data.set_origin("http://example.com");
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cryptographer->Encrypt(data,
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      specifics.MutableExtension(sync_pb::password)->mutable_encrypted());
238dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  CreateUnappliedNewItem("item", specifics, false);
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  apply_updates_command_.ExecuteImpl(session());
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::StatusController* status = session()->status_controller();
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(1, status->update_progress().AppliedUpdatesSize())
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "All updates should have been attempted";
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(0, status->conflict_progress().ConflictingItemsSize())
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "No update should be in conflict because they're all decryptable";
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(1, status->update_progress().SuccessfullyAppliedUpdateCount())
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "The updates that can be decrypted should be applied";
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTEST_F(ApplyUpdatesCommandTest, UndecryptablePassword) {
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Undecryptable password updates should not be applied.
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_pb::EntitySpecifics specifics;
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  specifics.MutableExtension(sync_pb::password);
256dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  CreateUnappliedNewItem("item", specifics, false);
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  apply_updates_command_.ExecuteImpl(session());
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::StatusController* status = session()->status_controller();
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(1, status->update_progress().AppliedUpdatesSize())
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "All updates should have been attempted";
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(1, status->conflict_progress().ConflictingItemsSize())
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "The updates that can't be decrypted should be in conflict";
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(0, status->update_progress().SuccessfullyAppliedUpdateCount())
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "No update that can't be decrypted should be applied";
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTEST_F(ApplyUpdatesCommandTest, SomeUndecryptablePassword) {
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Only decryptable password updates should be applied.
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  {
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    sync_pb::EntitySpecifics specifics;
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    sync_pb::PasswordSpecificsData data;
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    data.set_origin("http://example.com/1");
276ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    {
277ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
278ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      ASSERT_TRUE(dir.good());
279ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      ReadTransaction trans(dir, __FILE__, __LINE__);
280ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      Cryptographer* cryptographer =
281ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          session()->context()->directory_manager()->GetCryptographer(&trans);
282ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
283ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      KeyParams params = {"localhost", "dummy", "foobar"};
284ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      cryptographer->AddKey(params);
285ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
286ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      cryptographer->Encrypt(data,
287ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          specifics.MutableExtension(sync_pb::password)->mutable_encrypted());
288ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
289dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    CreateUnappliedNewItem("item1", specifics, false);
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  {
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Create a new cryptographer, independent of the one in the session.
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Cryptographer cryptographer;
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    KeyParams params = {"localhost", "dummy", "bazqux"};
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    cryptographer.AddKey(params);
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    sync_pb::EntitySpecifics specifics;
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    sync_pb::PasswordSpecificsData data;
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    data.set_origin("http://example.com/2");
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    cryptographer.Encrypt(data,
302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        specifics.MutableExtension(sync_pb::password)->mutable_encrypted());
303dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    CreateUnappliedNewItem("item2", specifics, false);
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  apply_updates_command_.ExecuteImpl(session());
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::StatusController* status = session()->status_controller();
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(2, status->update_progress().AppliedUpdatesSize())
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "All updates should have been attempted";
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(1, status->conflict_progress().ConflictingItemsSize())
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "The decryptable password update should be applied";
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(1, status->update_progress().SuccessfullyAppliedUpdateCount())
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "The undecryptable password update shouldn't be applied";
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTEST_F(ApplyUpdatesCommandTest, NigoriUpdate) {
319ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Storing the cryptographer separately is bad, but for this test we
320ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // know it's safe.
321ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  Cryptographer* cryptographer;
322dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  syncable::ModelTypeSet encrypted_types;
323dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  {
324dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
325dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ASSERT_TRUE(dir.good());
326dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ReadTransaction trans(dir, __FILE__, __LINE__);
327dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_EQ(encrypted_types, GetEncryptedDataTypes(&trans));
328ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    cryptographer =
329ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        session()->context()->directory_manager()->GetCryptographer(&trans);
330dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
331dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Nigori node updates should update the Cryptographer.
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Cryptographer other_cryptographer;
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  KeyParams params = {"localhost", "dummy", "foobar"};
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  other_cryptographer.AddKey(params);
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_pb::EntitySpecifics specifics;
338dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  sync_pb::NigoriSpecifics* nigori =
339dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      specifics.MutableExtension(sync_pb::nigori);
340dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  other_cryptographer.GetKeys(nigori->mutable_encrypted());
341dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  nigori->set_encrypt_bookmarks(true);
342dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  encrypted_types.insert(syncable::BOOKMARKS);
343dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI),
344dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                         specifics, true);
345dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_FALSE(cryptographer->has_pending_keys());
346dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
347dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  apply_updates_command_.ExecuteImpl(session());
348dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
349dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  sessions::StatusController* status = session()->status_controller();
350dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
351dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_EQ(1, status->update_progress().AppliedUpdatesSize())
352dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      << "All updates should have been attempted";
353dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_EQ(0, status->conflict_progress().ConflictingItemsSize())
354dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      << "The nigori update shouldn't be in conflict";
355dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_EQ(1, status->update_progress().SuccessfullyAppliedUpdateCount())
356dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      << "The nigori update should be applied";
357dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
358dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_FALSE(cryptographer->is_ready());
359dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_TRUE(cryptographer->has_pending_keys());
360dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
361dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
362dc0f95d653279beabeb9817299e2902918ba123eKristian MonsenTEST_F(ApplyUpdatesCommandTest, EncryptUnsyncedChanges) {
363ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Storing the cryptographer separately is bad, but for this test we
364ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // know it's safe.
365ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  Cryptographer* cryptographer;
366dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  syncable::ModelTypeSet encrypted_types;
367dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  {
368dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
369dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ASSERT_TRUE(dir.good());
370dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ReadTransaction trans(dir, __FILE__, __LINE__);
371dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_EQ(encrypted_types, GetEncryptedDataTypes(&trans));
372ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    cryptographer =
373ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        session()->context()->directory_manager()->GetCryptographer(&trans);
374dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
375dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    // With empty encrypted_types, this should be true.
376dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
377dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
378dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    Syncer::UnsyncedMetaHandles handles;
379dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    SyncerUtil::GetUnsyncedEntries(&trans, &handles);
380dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_TRUE(handles.empty());
381dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
383dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Create unsynced bookmarks without encryption.
384dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // First item is a folder
385dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  Id folder_id = id_factory_.NewLocalId();
386dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  CreateUnsyncedItem(folder_id, id_factory_.root(), "folder",
387dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                     true, syncable::BOOKMARKS, NULL);
388dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Next five items are children of the folder
389dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  size_t i;
390dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  size_t batch_s = 5;
391dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  for (i = 0; i < batch_s; ++i) {
392dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    CreateUnsyncedItem(id_factory_.NewLocalId(), folder_id,
393dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                       StringPrintf("Item %"PRIuS"", i), false,
394dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                       syncable::BOOKMARKS, NULL);
395dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
396dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Next five items are children of the root.
397dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  for (; i < 2*batch_s; ++i) {
398dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    CreateUnsyncedItem(id_factory_.NewLocalId(), id_factory_.root(),
399dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                       StringPrintf("Item %"PRIuS"", i), false,
400dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                       syncable::BOOKMARKS, NULL);
401dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
403dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  KeyParams params = {"localhost", "dummy", "foobar"};
404dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  cryptographer->AddKey(params);
405dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  sync_pb::EntitySpecifics specifics;
406dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  sync_pb::NigoriSpecifics* nigori =
407dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      specifics.MutableExtension(sync_pb::nigori);
408dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  cryptographer->GetKeys(nigori->mutable_encrypted());
409dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  nigori->set_encrypt_bookmarks(true);
410dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  encrypted_types.insert(syncable::BOOKMARKS);
411dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI),
412dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                         specifics, true);
413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_FALSE(cryptographer->has_pending_keys());
414dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_TRUE(cryptographer->is_ready());
415dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
416dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  {
417dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    // Ensure we have unsynced nodes that aren't properly encrypted.
418dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
419dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ASSERT_TRUE(dir.good());
420dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ReadTransaction trans(dir, __FILE__, __LINE__);
421dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
422dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
423dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    Syncer::UnsyncedMetaHandles handles;
424dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    SyncerUtil::GetUnsyncedEntries(&trans, &handles);
425dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_EQ(2*batch_s+1, handles.size());
426dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  apply_updates_command_.ExecuteImpl(session());
429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::StatusController* status = session()->status_controller();
431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
432c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(1, status->update_progress().AppliedUpdatesSize())
433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "All updates should have been attempted";
434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(0, status->conflict_progress().ConflictingItemsSize())
435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "The nigori update shouldn't be in conflict";
436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_EQ(1, status->update_progress().SuccessfullyAppliedUpdateCount())
437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "The nigori update should be applied";
438dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_FALSE(cryptographer->has_pending_keys());
439dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_TRUE(cryptographer->is_ready());
440dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  {
441dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
442dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ASSERT_TRUE(dir.good());
443dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ReadTransaction trans(dir, __FILE__, __LINE__);
444dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
445dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    // If ProcessUnsyncedChangesForEncryption worked, all our unsynced changes
446dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    // should be encrypted now.
447dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_EQ(encrypted_types, GetEncryptedDataTypes(&trans));
448dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
449dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
450dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    Syncer::UnsyncedMetaHandles handles;
451dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    SyncerUtil::GetUnsyncedEntries(&trans, &handles);
452dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_EQ(2*batch_s+1, handles.size());
453dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
454dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
455dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
456dc0f95d653279beabeb9817299e2902918ba123eKristian MonsenTEST_F(ApplyUpdatesCommandTest, CannotEncryptUnsyncedChanges) {
457ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Storing the cryptographer separately is bad, but for this test we
458ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // know it's safe.
459ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  Cryptographer* cryptographer;
460dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  syncable::ModelTypeSet encrypted_types;
461dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  {
462dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
463dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ASSERT_TRUE(dir.good());
464dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ReadTransaction trans(dir, __FILE__, __LINE__);
465dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_EQ(encrypted_types, GetEncryptedDataTypes(&trans));
466ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    cryptographer =
467ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        session()->context()->directory_manager()->GetCryptographer(&trans);
468dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
469dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    // With empty encrypted_types, this should be true.
470dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
471dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
472dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    Syncer::UnsyncedMetaHandles handles;
473dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    SyncerUtil::GetUnsyncedEntries(&trans, &handles);
474dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_TRUE(handles.empty());
475dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
476dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
477dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Create unsynced bookmarks without encryption.
478dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // First item is a folder
479dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  Id folder_id = id_factory_.NewLocalId();
480dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  CreateUnsyncedItem(folder_id, id_factory_.root(), "folder", true,
481dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                     syncable::BOOKMARKS, NULL);
482dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Next five items are children of the folder
483dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  size_t i;
484dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  size_t batch_s = 5;
485dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  for (i = 0; i < batch_s; ++i) {
486dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    CreateUnsyncedItem(id_factory_.NewLocalId(), folder_id,
487dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                       StringPrintf("Item %"PRIuS"", i), false,
488dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                       syncable::BOOKMARKS, NULL);
489dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
490dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Next five items are children of the root.
491dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  for (; i < 2*batch_s; ++i) {
492dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    CreateUnsyncedItem(id_factory_.NewLocalId(), id_factory_.root(),
493dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                       StringPrintf("Item %"PRIuS"", i), false,
494dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                       syncable::BOOKMARKS, NULL);
495dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
496dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
497dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // We encrypt with new keys, triggering the local cryptographer to be unready
498dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // and unable to decrypt data (once updated).
499dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  Cryptographer other_cryptographer;
500dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  KeyParams params = {"localhost", "dummy", "foobar"};
501dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  other_cryptographer.AddKey(params);
502dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  sync_pb::EntitySpecifics specifics;
503dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  sync_pb::NigoriSpecifics* nigori =
504dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      specifics.MutableExtension(sync_pb::nigori);
505dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  other_cryptographer.GetKeys(nigori->mutable_encrypted());
506dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  nigori->set_encrypt_bookmarks(true);
507dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  encrypted_types.insert(syncable::BOOKMARKS);
508dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI),
509dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                         specifics, true);
510dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_FALSE(cryptographer->has_pending_keys());
511dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
512dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  {
513dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    // Ensure we have unsynced nodes that aren't properly encrypted.
514dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
515dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ASSERT_TRUE(dir.good());
516dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ReadTransaction trans(dir, __FILE__, __LINE__);
517dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
518dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    Syncer::UnsyncedMetaHandles handles;
519dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    SyncerUtil::GetUnsyncedEntries(&trans, &handles);
520dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_EQ(2*batch_s+1, handles.size());
521dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
522dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
523dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  apply_updates_command_.ExecuteImpl(session());
524c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
525dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  sessions::StatusController* status = session()->status_controller();
526dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
527dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_EQ(1, status->update_progress().AppliedUpdatesSize())
528dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      << "All updates should have been attempted";
529dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_EQ(1, status->conflict_progress().ConflictingItemsSize())
530dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      << "The unsynced chnages trigger a conflict with the nigori update.";
531dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_EQ(0, status->update_progress().SuccessfullyAppliedUpdateCount())
532dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      << "The nigori update should not be applied";
533c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_FALSE(cryptographer->is_ready());
534c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EXPECT_TRUE(cryptographer->has_pending_keys());
535dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  {
536dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    // Ensure the unsynced nodes are still not encrypted.
537dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
538dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ASSERT_TRUE(dir.good());
539dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ReadTransaction trans(dir, __FILE__, __LINE__);
540dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
541dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    // Since we're in conflict, the specifics don't reflect the unapplied
542dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    // changes.
543dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
544dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    encrypted_types.clear();
545dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_EQ(encrypted_types, GetEncryptedDataTypes(&trans));
546dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
547dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    Syncer::UnsyncedMetaHandles handles;
548dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    SyncerUtil::GetUnsyncedEntries(&trans, &handles);
549dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_EQ(2*batch_s+1, handles.size());
550dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
551c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace browser_sync
554