1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/files/file_path.h"
6 #include "base/files/file_util.h"
7 #include "base/guid.h"
8 #include "base/test/task_environment.h"
9 #include "components/sync/engine_impl/loopback_server/loopback_connection_manager.h"
10 #include "components/sync/engine_impl/syncer_proto_util.h"
11 #include "components/sync/protocol/sync.pb.h"
12 #include "components/sync/protocol/sync_enums.pb.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 using sync_pb::ClientToServerMessage;
16 using sync_pb::ClientToServerResponse;
17 using sync_pb::EntitySpecifics;
18 using sync_pb::SyncEnums;
19 using sync_pb::SyncEntity;
20
21 namespace syncer {
22
23 namespace {
24
25 const char kUrl1[] = "http://www.one.com";
26 const char kUrl2[] = "http://www.two.com";
27 const char kUrl3[] = "http://www.three.com";
28 const char kBookmarkBar[] = "bookmark_bar";
29
NewBookmarkEntity(const std::string & url,const std::string & parent_id)30 SyncEntity NewBookmarkEntity(const std::string& url,
31 const std::string& parent_id) {
32 SyncEntity entity;
33 entity.mutable_specifics()->mutable_bookmark()->set_url(url);
34 entity.set_parent_id_string(parent_id);
35 entity.set_id_string(base::GenerateGUID());
36 return entity;
37 }
38
UpdatedBookmarkEntity(const std::string & url,const std::string & id,const std::string & parent_id,int version)39 SyncEntity UpdatedBookmarkEntity(const std::string& url,
40 const std::string& id,
41 const std::string& parent_id,
42 int version) {
43 SyncEntity entity;
44 entity.mutable_specifics()->mutable_bookmark()->set_url(url);
45 entity.set_id_string(id);
46 entity.set_parent_id_string(parent_id);
47 entity.set_version(version);
48 return entity;
49 }
50
DeletedBookmarkEntity(const std::string & id,int version)51 SyncEntity DeletedBookmarkEntity(const std::string& id, int version) {
52 SyncEntity entity;
53 entity.mutable_specifics()->mutable_bookmark();
54 entity.set_id_string(id);
55 entity.set_deleted(true);
56 entity.set_version(version);
57 return entity;
58 }
59
ResponseToMap(const ClientToServerResponse & response)60 std::map<std::string, SyncEntity> ResponseToMap(
61 const ClientToServerResponse& response) {
62 EXPECT_TRUE(response.has_get_updates());
63 std::map<std::string, SyncEntity> results;
64 for (const SyncEntity& entity : response.get_updates().entries()) {
65 results[entity.id_string()] = entity;
66 }
67 return results;
68 }
69
70 } // namespace
71
72 class LoopbackServerTest : public testing::Test {
73 public:
SetUp()74 void SetUp() override {
75 base::CreateTemporaryFile(&persistent_file_);
76 lcm_ = std::make_unique<LoopbackConnectionManager>(persistent_file_);
77 }
78
CallPostAndProcessHeaders(ServerConnectionManager * scm,SyncCycle * cycle,const ClientToServerMessage & msg,ClientToServerResponse * response)79 static bool CallPostAndProcessHeaders(ServerConnectionManager* scm,
80 SyncCycle* cycle,
81 const ClientToServerMessage& msg,
82 ClientToServerResponse* response) {
83 return SyncerProtoUtil::PostAndProcessHeaders(scm, cycle, msg, response);
84 }
85
86 protected:
GetUpdatesForType(int field_number)87 ClientToServerResponse GetUpdatesForType(int field_number) {
88 ClientToServerMessage request;
89 SyncerProtoUtil::SetProtocolVersion(&request);
90 request.set_share("required");
91 request.set_message_contents(ClientToServerMessage::GET_UPDATES);
92 request.mutable_get_updates()->add_from_progress_marker()->set_data_type_id(
93 field_number);
94
95 ClientToServerResponse response;
96 EXPECT_TRUE(
97 CallPostAndProcessHeaders(lcm_.get(), nullptr, request, &response));
98 EXPECT_EQ(SyncEnums::SUCCESS, response.error_code());
99 return response;
100 }
101
SingleEntryCommit(const std::vector<SyncEntity> & entity_vector)102 ClientToServerMessage SingleEntryCommit(
103 const std::vector<SyncEntity>& entity_vector) {
104 ClientToServerMessage request;
105 SyncerProtoUtil::SetProtocolVersion(&request);
106 request.set_share("required");
107 request.set_message_contents(ClientToServerMessage::COMMIT);
108 request.set_invalidator_client_id("client_id");
109 auto* commit = request.mutable_commit();
110 commit->set_cache_guid("cache_guid");
111 for (const SyncEntity& entity : entity_vector) {
112 *commit->add_entries() = entity;
113 }
114 return request;
115 }
116
CommitVerifySuccess(const SyncEntity & entity)117 std::string CommitVerifySuccess(const SyncEntity& entity) {
118 ClientToServerMessage request = SingleEntryCommit({entity});
119 ClientToServerResponse response;
120 EXPECT_TRUE(
121 CallPostAndProcessHeaders(lcm_.get(), nullptr, request, &response));
122 EXPECT_EQ(SyncEnums::SUCCESS, response.error_code());
123 EXPECT_TRUE(response.has_commit());
124 return response.commit().entryresponse(0).id_string();
125 }
126
CommitVerifyFailure(const SyncEntity & entity)127 void CommitVerifyFailure(const SyncEntity& entity) {
128 ClientToServerMessage request = SingleEntryCommit({entity});
129 ClientToServerResponse response;
130 EXPECT_FALSE(
131 CallPostAndProcessHeaders(lcm_.get(), nullptr, request, &response));
132 EXPECT_NE(SyncEnums::SUCCESS, response.error_code());
133 EXPECT_FALSE(response.has_commit());
134 }
135
136 base::test::TaskEnvironment task_environment_;
137
138 base::FilePath persistent_file_;
139 std::unique_ptr<LoopbackConnectionManager> lcm_;
140 };
141
TEST_F(LoopbackServerTest,WrongBirthday)142 TEST_F(LoopbackServerTest, WrongBirthday) {
143 ClientToServerMessage msg;
144 SyncerProtoUtil::SetProtocolVersion(&msg);
145 msg.set_share("required");
146 msg.set_store_birthday("not_your_birthday");
147 msg.set_message_contents(ClientToServerMessage::GET_UPDATES);
148 msg.mutable_get_updates()->add_from_progress_marker()->set_data_type_id(
149 EntitySpecifics::kBookmarkFieldNumber);
150 ClientToServerResponse response;
151
152 EXPECT_TRUE(CallPostAndProcessHeaders(lcm_.get(), nullptr, msg, &response));
153 EXPECT_EQ(SyncEnums::NOT_MY_BIRTHDAY, response.error_code());
154 }
155
TEST_F(LoopbackServerTest,GetUpdateCommand)156 TEST_F(LoopbackServerTest, GetUpdateCommand) {
157 ClientToServerResponse response =
158 GetUpdatesForType(EntitySpecifics::kBookmarkFieldNumber);
159 // Expect to see the four top-level folders in this update already.
160 EXPECT_EQ(4, response.get_updates().entries_size());
161 }
162
TEST_F(LoopbackServerTest,GetUpdateCommandShouldFilterByDataType)163 TEST_F(LoopbackServerTest, GetUpdateCommandShouldFilterByDataType) {
164 ClientToServerResponse response =
165 GetUpdatesForType(EntitySpecifics::kPreferenceFieldNumber);
166 // Expect bookmark nodes to be ignored.
167 EXPECT_EQ(0, response.get_updates().entries_size());
168 EXPECT_EQ(1, response.get_updates().new_progress_marker_size());
169 }
170
TEST_F(LoopbackServerTest,ClearServerDataCommand)171 TEST_F(LoopbackServerTest, ClearServerDataCommand) {
172 ClientToServerMessage msg;
173 SyncerProtoUtil::SetProtocolVersion(&msg);
174 msg.set_share("required");
175 msg.set_message_contents(ClientToServerMessage::CLEAR_SERVER_DATA);
176 ClientToServerResponse response;
177
178 EXPECT_TRUE(CallPostAndProcessHeaders(lcm_.get(), nullptr, msg, &response));
179 EXPECT_EQ(SyncEnums::SUCCESS, response.error_code());
180 EXPECT_TRUE(response.has_clear_server_data());
181 }
182
TEST_F(LoopbackServerTest,CommitCommand)183 TEST_F(LoopbackServerTest, CommitCommand) {
184 CommitVerifySuccess(NewBookmarkEntity(kUrl1, kBookmarkBar));
185 }
186
TEST_F(LoopbackServerTest,CommitFailureNoTag)187 TEST_F(LoopbackServerTest, CommitFailureNoTag) {
188 // Non-bookmarks and non-commit only types must have a
189 // client_defined_unique_tag, which we don't set.
190 SyncEntity entity;
191 entity.mutable_specifics()->mutable_preference();
192 CommitVerifyFailure(entity);
193 }
194
TEST_F(LoopbackServerTest,CommitBookmarkTombstoneSuccess)195 TEST_F(LoopbackServerTest, CommitBookmarkTombstoneSuccess) {
196 std::string id1 = CommitVerifySuccess(NewBookmarkEntity(kUrl1, kBookmarkBar));
197 std::string id2 = CommitVerifySuccess(NewBookmarkEntity(kUrl2, id1));
198 std::string id3 = CommitVerifySuccess(NewBookmarkEntity(kUrl3, kBookmarkBar));
199
200 // Because 2 is a child of 1, deleting 1 will also delete 2.
201 CommitVerifySuccess(DeletedBookmarkEntity(id1, 10));
202
203 std::map<std::string, SyncEntity> bookmarks =
204 ResponseToMap(GetUpdatesForType(EntitySpecifics::kBookmarkFieldNumber));
205 EXPECT_TRUE(bookmarks[id1].deleted());
206 EXPECT_TRUE(bookmarks[id2].deleted());
207 EXPECT_FALSE(bookmarks[id3].deleted());
208 }
209
TEST_F(LoopbackServerTest,CommitBookmarkTombstoneFailure)210 TEST_F(LoopbackServerTest, CommitBookmarkTombstoneFailure) {
211 std::string id1 = CommitVerifySuccess(NewBookmarkEntity(kUrl1, kBookmarkBar));
212 std::string id2 = CommitVerifySuccess(NewBookmarkEntity(kUrl2, "9" + id1));
213
214 // This write is going to fail, the id is supposed to encode the model type as
215 // as prefix, by adding 9 we're creating a fake model type.
216 SyncEntity entity = DeletedBookmarkEntity("9" + id1, 1);
217 CommitVerifyFailure(entity);
218
219 std::map<std::string, SyncEntity> bookmarks =
220 ResponseToMap(GetUpdatesForType(EntitySpecifics::kBookmarkFieldNumber));
221 EXPECT_FALSE(bookmarks[id1].deleted());
222 // This is the point of this test, making sure the child doesn't get deleted.
223 EXPECT_FALSE(bookmarks[id2].deleted());
224 }
225
TEST_F(LoopbackServerTest,LoadSavedState)226 TEST_F(LoopbackServerTest, LoadSavedState) {
227 std::string id = CommitVerifySuccess(NewBookmarkEntity(kUrl1, kBookmarkBar));
228
229 ClientToServerMessage get_updates_msg;
230 SyncerProtoUtil::SetProtocolVersion(&get_updates_msg);
231 get_updates_msg.set_share("required");
232 get_updates_msg.set_message_contents(ClientToServerMessage::GET_UPDATES);
233 get_updates_msg.mutable_get_updates()
234 ->add_from_progress_marker()
235 ->set_data_type_id(EntitySpecifics::kBookmarkFieldNumber);
236
237 ClientToServerResponse expected_response;
238 EXPECT_TRUE(CallPostAndProcessHeaders(lcm_.get(), nullptr, get_updates_msg,
239 &expected_response));
240 EXPECT_EQ(SyncEnums::SUCCESS, expected_response.error_code());
241 ASSERT_TRUE(expected_response.has_get_updates());
242 ASSERT_TRUE(expected_response.has_store_birthday());
243
244 lcm_.reset();
245 task_environment_.RunUntilIdle();
246
247 LoopbackConnectionManager second_user(persistent_file_);
248
249 ClientToServerResponse response;
250 EXPECT_TRUE(CallPostAndProcessHeaders(&second_user, nullptr, get_updates_msg,
251 &response));
252 EXPECT_EQ(SyncEnums::SUCCESS, response.error_code());
253 ASSERT_TRUE(response.has_get_updates());
254 // Expect to see the four top-level folders and the newly added bookmark!
255 EXPECT_EQ(5, response.get_updates().entries_size());
256 EXPECT_EQ(1U, ResponseToMap(response).count(id));
257
258 EXPECT_EQ(expected_response.store_birthday(), response.store_birthday());
259 }
260
TEST_F(LoopbackServerTest,CommitCommandUpdate)261 TEST_F(LoopbackServerTest, CommitCommandUpdate) {
262 std::string id = CommitVerifySuccess(NewBookmarkEntity(kUrl1, kBookmarkBar));
263 EXPECT_EQ(1U, ResponseToMap(
264 GetUpdatesForType(EntitySpecifics::kBookmarkFieldNumber))
265 .count(id));
266 CommitVerifySuccess(UpdatedBookmarkEntity(kUrl2, id, "other_bookmarks", 1));
267
268 ClientToServerResponse response =
269 GetUpdatesForType(EntitySpecifics::kBookmarkFieldNumber);
270 ASSERT_TRUE(response.has_get_updates());
271 // Expect to see no sixth bookmark!
272 EXPECT_EQ(5, response.get_updates().entries_size());
273 EXPECT_EQ(kUrl2, ResponseToMap(response)[id].specifics().bookmark().url());
274 }
275
276 } // namespace syncer
277