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