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