1 // Copyright 2018 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "google/cloud/bigtable/testing/table_integration_test.h"
16 #include "google/cloud/testing_util/assert_ok.h"
17 #include "google/cloud/testing_util/chrono_literals.h"
18 
19 namespace {
20 
21 namespace bigtable = google::cloud::bigtable;
22 using ::google::cloud::testing_util::chrono_literals::operator"" _us;
23 
24 using MutationIntegrationTest = bigtable::testing::TableIntegrationTest;
25 /**
26  * This function creates Cell by ignoring the timestamp.
27  * In this case Cloud Bigtable will insert the default server
28  * side timestamp for the cells.
29  */
CreateCellsIgnoringTimestamp(bigtable::Table & table,std::vector<bigtable::Cell> const & cells)30 void CreateCellsIgnoringTimestamp(bigtable::Table& table,
31                                   std::vector<bigtable::Cell> const& cells) {
32   std::map<bigtable::RowKeyType, bigtable::SingleRowMutation> mutations;
33   for (auto const& cell : cells) {
34     auto key = cell.row_key();
35     auto inserted = mutations.emplace(key, bigtable::SingleRowMutation(key));
36     inserted.first->second.emplace_back(bigtable::SetCell(
37         cell.family_name(), cell.column_qualifier(), cell.value()));
38   }
39 
40   bigtable::BulkMutation bulk;
41   for (auto& kv : mutations) {
42     bulk.emplace_back(std::move(kv.second));
43   }
44   ASSERT_NE(0, bulk.size());
45   auto failures = table.BulkApply(std::move(bulk));
46   ASSERT_TRUE(failures.empty());
47 }
48 
49 std::string const kColumnFamily1 = "family1";
50 std::string const kColumnFamily2 = "family2";
51 std::string const kColumnFamily3 = "family3";
52 
53 }  // namespace
54 
55 /**
56  * Check if the values inserted by SetCell are correctly inserted into
57  * Cloud Bigtable
58  */
TEST_F(MutationIntegrationTest,SetCellTest)59 TEST_F(MutationIntegrationTest, SetCellTest) {
60   auto table = GetTable();
61 
62   // Create a vector of cells which will be inserted into bigtable
63   std::string const row_key = "SetCellRowKey";
64   std::vector<bigtable::Cell> created_cells{
65       {row_key, kColumnFamily1, "column_id1", 0, "v-c-0-0"},
66       {row_key, kColumnFamily1, "column_id1", 1000, "v-c-0-1"},
67       {row_key, kColumnFamily1, "column_id1", 2000, "v-c-0-2"},
68       {row_key, kColumnFamily2, "column_id2", 0, "v-c0-0-0"},
69       {row_key, kColumnFamily2, "column_id3", 1000, "v-c1-0-1"},
70       {row_key, kColumnFamily3, "column_id1", 2000, "v-c1-0-2"},
71   };
72 
73   CreateCells(table, created_cells);
74   auto actual_cells = ReadRows(table, bigtable::Filter::PassAllFilter());
75 
76   CheckEqualUnordered(created_cells, actual_cells);
77 }
78 
79 /**
80  * Check if the numeric and string values inserted by SetCell are
81  * correctly inserted into Cloud Bigtable
82  */
TEST_F(MutationIntegrationTest,SetCellNumericValueTest)83 TEST_F(MutationIntegrationTest, SetCellNumericValueTest) {
84   auto table = GetTable();
85 
86   // Create a vector of cells which will be inserted into bigtable
87   std::string const row_key = "SetCellNumRowKey";
88   std::vector<bigtable::Cell> created_cells{
89       {row_key, kColumnFamily1, "column_id1", 0, "v-c-0-0"},
90       {row_key, kColumnFamily1, "column_id1", 1000, 2000},
91       {row_key, kColumnFamily1, "column_id1", 2000, 3000},
92       {row_key, kColumnFamily2, "column_id2", 0, "v-c0-0-0"},
93       {row_key, kColumnFamily2, "column_id3", 1000, 5000},
94       {row_key, kColumnFamily3, "column_id1", 2000, "v-c1-0-2"}};
95 
96   CreateCells(table, created_cells);
97   auto actual_cells = ReadRows(table, bigtable::Filter::PassAllFilter());
98 
99   CheckEqualUnordered(created_cells, actual_cells);
100 }
101 
102 /**
103  * Check if an error is returned when a string value was set and a numeric
104  * value was retrieved. NOTE: This error happens only when/because the length
105  * of the string != sizeof(int64_t).
106  */
TEST_F(MutationIntegrationTest,SetCellNumericValueErrorTest)107 TEST_F(MutationIntegrationTest, SetCellNumericValueErrorTest) {
108   std::string const table_id = RandomTableId();
109   bigtable::Cell new_cell(
110       "row-key", "column_family", "column_id", 1000,
111       "some string value that is longer than sizeof(int64_t)");
112   auto decoded = new_cell.decode_big_endian_integer<std::int64_t>();
113   EXPECT_FALSE(decoded);
114 
115   // To be explicit, setting a string value that happens to be 8-bytes long
116   // *will* be decodeable to in int64_t. I don't know what value it will be,
117   // but it's decodeable.
118   new_cell =
119       bigtable::Cell("row-key", "column_family", "column_id", 1000, "12345678");
120   decoded = new_cell.decode_big_endian_integer<std::int64_t>();
121   EXPECT_STATUS_OK(decoded);
122 }
123 
124 /**
125  * Verify that the values inserted by SetCell with server-side timestamp are
126  * correctly inserted into Cloud Bigtable.
127  */
TEST_F(MutationIntegrationTest,SetCellIgnoreTimestampTest)128 TEST_F(MutationIntegrationTest, SetCellIgnoreTimestampTest) {
129   auto table = GetTable();
130 
131   // Create a vector of cell which will be inserted into bigtable
132   std::string const row_key = "SetCellRowKey";
133   std::vector<bigtable::Cell> created_cells{
134       {row_key, kColumnFamily1, "column_id1", 0, "v-c-0-0"},
135       {row_key, kColumnFamily1, "column_id2", 1000, "v-c-0-1"},
136       {row_key, kColumnFamily1, "column_id3", 2000, "v-c-0-2"},
137       {row_key, kColumnFamily2, "column_id2", 0, "v-c0-0-0"},
138       {row_key, kColumnFamily2, "column_id3", 1000, "v-c1-0-1"},
139       {row_key, kColumnFamily3, "column_id1", 2000, "v-c1-0-2"},
140   };
141   std::int64_t server_timestamp = -1;
142   std::vector<bigtable::Cell> expected_cells{
143       {row_key, kColumnFamily1, "column_id1", server_timestamp, "v-c-0-0"},
144       {row_key, kColumnFamily1, "column_id2", server_timestamp, "v-c-0-1"},
145       {row_key, kColumnFamily1, "column_id3", server_timestamp, "v-c-0-2"},
146       {row_key, kColumnFamily2, "column_id2", server_timestamp, "v-c0-0-0"},
147       {row_key, kColumnFamily2, "column_id3", server_timestamp, "v-c1-0-1"},
148       {row_key, kColumnFamily3, "column_id1", server_timestamp, "v-c1-0-2"},
149   };
150 
151   CreateCellsIgnoringTimestamp(table, created_cells);
152   auto actual_cells = ReadRows(table, bigtable::Filter::PassAllFilter());
153 
154   // Create the expected_cells and actual_cells with same timestamp
155   auto expected_cells_ignore_time = GetCellsIgnoringTimestamp(expected_cells);
156   auto actual_cells_ignore_time = GetCellsIgnoringTimestamp(actual_cells);
157 
158   CheckEqualUnordered(expected_cells_ignore_time, actual_cells_ignore_time);
159 }
160 
161 /**
162  * Verify that the deletion of records for specific row_key, column_family,
163  * column_identifier and within the time range are deleted from Cloud
164  * Bigtable.
165  */
TEST_F(MutationIntegrationTest,DeleteFromColumnForTimestampRangeTest)166 TEST_F(MutationIntegrationTest, DeleteFromColumnForTimestampRangeTest) {
167   auto table = GetTable();
168   // Create a vector of cell which will be inserted into bigtable
169   std::string const row_key = "DeleteColumn-Key";
170   std::vector<bigtable::Cell> created_cells{
171       {row_key, kColumnFamily1, "column_id1", 0, "v-c-0-0"},
172       {row_key, kColumnFamily1, "column_id2", 1000, "v-c-0-1"},
173       {row_key, kColumnFamily1, "column_id3", 2000, "v-c-0-2"},
174       {row_key, kColumnFamily2, "column_id2", 2000, "v-c0-0-0"},
175       {row_key, kColumnFamily2, "column_id2", 1000, "v-c0-0-1"},
176       {row_key, kColumnFamily2, "column_id2", 3000, "v-c0-0-2"},
177       {row_key, kColumnFamily2, "column_id2", 4000, "v-c0-0-3"},
178       {row_key, kColumnFamily2, "column_id3", 1000, "v-c1-0-1"},
179       {row_key, kColumnFamily2, "column_id2", 2000, "v-c1-0-2"},
180       {row_key, kColumnFamily3, "column_id1", 2000, "v-c1-0-2"},
181   };
182 
183   std::vector<bigtable::Cell> expected_cells{
184       {row_key, kColumnFamily1, "column_id1", 0, "v-c-0-0"},
185       {row_key, kColumnFamily1, "column_id2", 1000, "v-c-0-1"},
186       {row_key, kColumnFamily1, "column_id3", 2000, "v-c-0-2"},
187       {row_key, kColumnFamily2, "column_id2", 1000, "v-c0-0-1"},
188       {row_key, kColumnFamily2, "column_id2", 4000, "v-c0-0-3"},
189       {row_key, kColumnFamily2, "column_id3", 1000, "v-c1-0-1"},
190       {row_key, kColumnFamily3, "column_id1", 2000, "v-c1-0-2"},
191   };
192 
193   // Create records
194   CreateCells(table, created_cells);
195   // Delete the columns with column identifier as column_id2
196   auto status = table.Apply(bigtable::SingleRowMutation(
197       row_key, bigtable::DeleteFromColumn(kColumnFamily2, "column_id2", 2000_us,
198                                           4000_us)));
199   ASSERT_STATUS_OK(status);
200   auto actual_cells = ReadRows(table, bigtable::Filter::PassAllFilter());
201 
202   CheckEqualUnordered(expected_cells, actual_cells);
203 }
204 
205 /**
206  * Verify DeleteFromColumn() with invalid ranges works.
207  *
208  * We expect the server (and not the client library) to reject invalid ranges.
209  */
TEST_F(MutationIntegrationTest,DeleteFromColumnForReversedTimestampRangeTest)210 TEST_F(MutationIntegrationTest, DeleteFromColumnForReversedTimestampRangeTest) {
211   // TODO(#151) - remove workarounds for emulator bug(s).
212   if (UsingCloudBigtableEmulator()) {
213     return;
214   }
215   auto table = GetTable();
216   // Create a vector of cell which will be inserted into bigtable
217   std::string const key = "row";
218   std::vector<bigtable::Cell> created_cells{
219       {key, kColumnFamily1, "c1", 1000, "v1"},
220       {key, kColumnFamily1, "c2", 1000, "v2"},
221       {key, kColumnFamily1, "c3", 2000, "v3"},
222       {key, kColumnFamily2, "c2", 1000, "v4"},
223       {key, kColumnFamily2, "c2", 3000, "v5"},
224       {key, kColumnFamily2, "c2", 4000, "v6"},
225       {key, kColumnFamily2, "c3", 1000, "v7"},
226       {key, kColumnFamily2, "c2", 2000, "v8"},
227       {key, kColumnFamily3, "c1", 2000, "v9"},
228   };
229 
230   CreateCells(table, created_cells);
231 
232   // Try to delete the columns with an invalid range:
233   auto status = table.Apply(bigtable::SingleRowMutation(
234       key, bigtable::DeleteFromColumn(kColumnFamily2, "c2", 4000_us, 2000_us)));
235   EXPECT_FALSE(status.ok());
236   auto actual_cells = ReadRows(table, bigtable::Filter::PassAllFilter());
237 
238   CheckEqualUnordered(created_cells, actual_cells);
239 }
240 
241 /**
242  * Verify DeleteFromColumn() with empty ranges works.
243  *
244  * We expect the server (and not the client library) to reject invalid ranges.
245  */
TEST_F(MutationIntegrationTest,DeleteFromColumnForEmptyTimestampRangeTest)246 TEST_F(MutationIntegrationTest, DeleteFromColumnForEmptyTimestampRangeTest) {
247   // TODO(#151) - remove workarounds for emulator bug(s).
248   if (UsingCloudBigtableEmulator()) {
249     return;
250   }
251   auto table = GetTable();
252   // Create a vector of cell which will be inserted into bigtable
253   std::string const key = "row";
254   std::vector<bigtable::Cell> created_cells{
255       {key, kColumnFamily1, "c3", 2000, "v3"},
256       {key, kColumnFamily2, "c2", 2000, "v2"},
257       {key, kColumnFamily3, "c1", 2000, "v1"},
258   };
259 
260   CreateCells(table, created_cells);
261 
262   auto status = table.Apply(bigtable::SingleRowMutation(
263       key, bigtable::DeleteFromColumn(kColumnFamily2, "c2", 2000_us, 2000_us)));
264   EXPECT_FALSE(status.ok());
265   auto actual_cells = ReadRows(table, bigtable::Filter::PassAllFilter());
266 
267   CheckEqualUnordered(created_cells, actual_cells);
268 }
269 
270 /**
271  * Verify that DeleteFromColumn operation for specific column_identifier
272  * is deleting all records only for that column_identifier.
273  */
TEST_F(MutationIntegrationTest,DeleteFromColumnForAllTest)274 TEST_F(MutationIntegrationTest, DeleteFromColumnForAllTest) {
275   auto table = GetTable();
276   // Create a vector of cell which will be inserted into bigtable
277   std::string const row_key = "DeleteColumnForAll-Key";
278   std::vector<bigtable::Cell> created_cells{
279       {row_key, kColumnFamily1, "column_id1", 0, "v-c-0-0"},
280       {row_key, kColumnFamily1, "column_id3", 1000, "v-c-0-1"},
281       {row_key, kColumnFamily2, "column_id3", 2000, "v-c-0-2"},
282       {row_key, kColumnFamily2, "column_id2", 2000, "v-c0-0-0"},
283       {row_key, kColumnFamily1, "column_id3", 3000, "v-c1-0-2"},
284   };
285   std::vector<bigtable::Cell> expected_cells{
286       {row_key, kColumnFamily1, "column_id1", 0, "v-c-0-0"},
287       {row_key, kColumnFamily2, "column_id3", 2000, "v-c-0-2"},
288       {row_key, kColumnFamily2, "column_id2", 2000, "v-c0-0-0"},
289   };
290 
291   // Create records
292   CreateCells(table, created_cells);
293   // Delete the columns with column identifier column_id3
294   auto status = table.Apply(bigtable::SingleRowMutation(
295       row_key, bigtable::DeleteFromColumn(kColumnFamily1, "column_id3")));
296   ASSERT_STATUS_OK(status);
297   auto actual_cells = ReadRows(table, bigtable::Filter::PassAllFilter());
298 
299   CheckEqualUnordered(expected_cells, actual_cells);
300 }
301 
302 /**
303  * Verify that DeleteFromColumn operation for specific column_identifier
304  * and starting from specific timestamp is deleting all records after that
305  * timestamp only.
306  */
TEST_F(MutationIntegrationTest,DeleteFromColumnStartingFromTest)307 TEST_F(MutationIntegrationTest, DeleteFromColumnStartingFromTest) {
308   auto table = GetTable();
309   // Create a vector of cell which will be inserted into bigtable
310   std::string const row_key = "DeleteColumnStartingFrom-Key";
311   std::vector<bigtable::Cell> created_cells{
312       {row_key, kColumnFamily1, "column_id1", 0, "v-c-0-0"},
313       {row_key, kColumnFamily1, "column_id1", 1000, "v-c-0-1"},
314       {row_key, kColumnFamily1, "column_id1", 2000, "v-c-0-1"},
315       {row_key, kColumnFamily2, "column_id3", 2000, "v-c-0-2"},
316       {row_key, kColumnFamily2, "column_id2", 2000, "v-c0-0-0"},
317       {row_key, kColumnFamily1, "column_id3", 3000, "v-c1-0-2"},
318   };
319   std::vector<bigtable::Cell> expected_cells{
320       {row_key, kColumnFamily1, "column_id1", 0, "v-c-0-0"},
321       {row_key, kColumnFamily2, "column_id3", 2000, "v-c-0-2"},
322       {row_key, kColumnFamily2, "column_id2", 2000, "v-c0-0-0"},
323       {row_key, kColumnFamily1, "column_id3", 3000, "v-c1-0-2"},
324   };
325 
326   // Create records
327   CreateCells(table, created_cells);
328   // Delete the columns with column identifier column_id1
329   auto status = table.Apply(bigtable::SingleRowMutation(
330       row_key, bigtable::DeleteFromColumnStartingFrom(kColumnFamily1,
331                                                       "column_id1", 1000_us)));
332   ASSERT_STATUS_OK(status);
333   auto actual_cells = ReadRows(table, bigtable::Filter::PassAllFilter());
334 
335   CheckEqualUnordered(expected_cells, actual_cells);
336 }
337 
338 /**
339  * Verify that DeleteFromColumn operation for specific column_identifier
340  * and ending at specific timestamp is deleting all records before that
341  * timestamp only. end_timestamp is not inclusive.
342  */
TEST_F(MutationIntegrationTest,DeleteFromColumnEndingAtTest)343 TEST_F(MutationIntegrationTest, DeleteFromColumnEndingAtTest) {
344   auto table = GetTable();
345   // Create a vector of cell which will be inserted into bigtable cloud
346   std::string const row_key = "DeleteColumnEndingAt-Key";
347   std::vector<bigtable::Cell> created_cells{
348       {row_key, kColumnFamily1, "column_id1", 0, "v-c-0-0"},
349       {row_key, kColumnFamily1, "column_id1", 1000, "v-c-0-1"},
350       {row_key, kColumnFamily1, "column_id1", 2000, "v-c-0-1"},
351       {row_key, kColumnFamily2, "column_id3", 2000, "v-c-0-2"},
352       {row_key, kColumnFamily2, "column_id2", 2000, "v-c0-0-0"},
353       {row_key, kColumnFamily1, "column_id3", 3000, "v-c1-0-2"},
354   };
355   std::vector<bigtable::Cell> expected_cells{
356       {row_key, kColumnFamily1, "column_id1", 2000, "v-c-0-1"},
357       {row_key, kColumnFamily2, "column_id3", 2000, "v-c-0-2"},
358       {row_key, kColumnFamily2, "column_id2", 2000, "v-c0-0-0"},
359       {row_key, kColumnFamily1, "column_id3", 3000, "v-c1-0-2"},
360   };
361 
362   // Create records
363   CreateCells(table, created_cells);
364   // end_time is not inclusive, only records with timestamp < time_end
365   // will be deleted
366   // Delete the columns with column identifier column_id1
367   auto status = table.Apply(bigtable::SingleRowMutation(
368       row_key, bigtable::DeleteFromColumnEndingAt(kColumnFamily1, "column_id1",
369                                                   2000_us)));
370   ASSERT_STATUS_OK(status);
371   auto actual_cells = ReadRows(table, bigtable::Filter::PassAllFilter());
372 
373   CheckEqualUnordered(expected_cells, actual_cells);
374 }
375 
376 /**
377  * Verify that records deleted for a specific family will delete correct
378  * records for that family only
379  */
TEST_F(MutationIntegrationTest,DeleteFromFamilyTest)380 TEST_F(MutationIntegrationTest, DeleteFromFamilyTest) {
381   auto table = GetTable();
382 
383   // Create a vector of cell which will be inserted into bigtable
384   std::string const row_key = "DeleteFamily-Key";
385   std::vector<bigtable::Cell> created_cells{
386       {row_key, kColumnFamily1, "column_id1", 0, "v-c-0-0"},
387       {row_key, kColumnFamily1, "column_id1", 1000, "v-c-0-1"},
388       {row_key, kColumnFamily2, "column_id3", 2000, "v-c-0-2"},
389       {row_key, kColumnFamily2, "column_id2", 2000, "v-c0-0-0"},
390       {row_key, kColumnFamily1, "column_id3", 3000, "v-c1-0-2"},
391   };
392   std::vector<bigtable::Cell> expected_cells{
393       {row_key, kColumnFamily2, "column_id3", 2000, "v-c-0-2"},
394       {row_key, kColumnFamily2, "column_id2", 2000, "v-c0-0-0"},
395   };
396 
397   // Create records
398   CreateCells(table, created_cells);
399   // Delete all the records for family
400   auto status = table.Apply(bigtable::SingleRowMutation(
401       row_key, bigtable::DeleteFromFamily(kColumnFamily1)));
402   ASSERT_STATUS_OK(status);
403   auto actual_cells = ReadRows(table, bigtable::Filter::PassAllFilter());
404 
405   CheckEqualUnordered(expected_cells, actual_cells);
406 }
407 
408 /**
409  * Verify that records deleted for a specific row will delete correct
410  * records for that row only
411  */
TEST_F(MutationIntegrationTest,DeleteFromRowTest)412 TEST_F(MutationIntegrationTest, DeleteFromRowTest) {
413   auto table = GetTable();
414 
415   // Create a vector of cell which will be inserted into bigtable
416   std::string const row_key1 = "DeleteRowKey1";
417   std::string const row_key2 = "DeleteRowKey2";
418   std::vector<bigtable::Cell> created_cells{
419       {row_key1, kColumnFamily1, "column_id1", 0, "v-c-0-0"},
420       {row_key1, kColumnFamily1, "column_id1", 1000, "v-c-0-1"},
421       {row_key1, kColumnFamily2, "column_id3", 2000, "v-c-0-2"},
422       {row_key2, kColumnFamily2, "column_id2", 2000, "v-c0-0-0"},
423       {row_key2, kColumnFamily3, "column_id3", 3000, "v-c1-0-2"},
424   };
425   std::vector<bigtable::Cell> expected_cells{
426       {row_key2, kColumnFamily2, "column_id2", 2000, "v-c0-0-0"},
427       {row_key2, kColumnFamily3, "column_id3", 3000, "v-c1-0-2"},
428   };
429 
430   // Create records
431   CreateCells(table, created_cells);
432   // Delete all the records for a row
433   auto status = table.Apply(
434       bigtable::SingleRowMutation(row_key1, bigtable::DeleteFromRow()));
435   ASSERT_STATUS_OK(status);
436   auto actual_cells = ReadRows(table, bigtable::Filter::PassAllFilter());
437 
438   CheckEqualUnordered(expected_cells, actual_cells);
439 }
440 // Test Cases Finished
441 
main(int argc,char * argv[])442 int main(int argc, char* argv[]) {
443   ::testing::InitGoogleMock(&argc, argv);
444   (void)::testing::AddGlobalTestEnvironment(
445       new ::bigtable::testing::TableTestEnvironment);
446 
447   return RUN_ALL_TESTS();
448 }
449