1506 lines
50 KiB
Python
1506 lines
50 KiB
Python
|
# Copyright 2016 Google Inc. All rights reserved.
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
|
||
|
import unittest2
|
||
|
|
||
|
|
||
|
class Test_make_row(unittest2.TestCase):
|
||
|
|
||
|
def _callFUT(self, *args, **kwargs):
|
||
|
from gcloud.bigtable.happybase.table import make_row
|
||
|
return make_row(*args, **kwargs)
|
||
|
|
||
|
def test_it(self):
|
||
|
with self.assertRaises(NotImplementedError):
|
||
|
self._callFUT({}, False)
|
||
|
|
||
|
|
||
|
class Test_make_ordered_row(unittest2.TestCase):
|
||
|
|
||
|
def _callFUT(self, *args, **kwargs):
|
||
|
from gcloud.bigtable.happybase.table import make_ordered_row
|
||
|
return make_ordered_row(*args, **kwargs)
|
||
|
|
||
|
def test_it(self):
|
||
|
with self.assertRaises(NotImplementedError):
|
||
|
self._callFUT([], False)
|
||
|
|
||
|
|
||
|
class TestTable(unittest2.TestCase):
|
||
|
|
||
|
def _getTargetClass(self):
|
||
|
from gcloud.bigtable.happybase.table import Table
|
||
|
return Table
|
||
|
|
||
|
def _makeOne(self, *args, **kwargs):
|
||
|
return self._getTargetClass()(*args, **kwargs)
|
||
|
|
||
|
def test_constructor(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
|
||
|
name = 'table-name'
|
||
|
instance = object()
|
||
|
connection = _Connection(instance)
|
||
|
tables_constructed = []
|
||
|
|
||
|
def make_low_level_table(*args, **kwargs):
|
||
|
result = _MockLowLevelTable(*args, **kwargs)
|
||
|
tables_constructed.append(result)
|
||
|
return result
|
||
|
|
||
|
with _Monkey(MUT, _LowLevelTable=make_low_level_table):
|
||
|
table = self._makeOne(name, connection)
|
||
|
self.assertEqual(table.name, name)
|
||
|
self.assertEqual(table.connection, connection)
|
||
|
|
||
|
table_instance, = tables_constructed
|
||
|
self.assertEqual(table._low_level_table, table_instance)
|
||
|
self.assertEqual(table_instance.args, (name, instance))
|
||
|
self.assertEqual(table_instance.kwargs, {})
|
||
|
|
||
|
def test_constructor_null_connection(self):
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
self.assertEqual(table.name, name)
|
||
|
self.assertEqual(table.connection, connection)
|
||
|
self.assertEqual(table._low_level_table, None)
|
||
|
|
||
|
def test_families(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
table._low_level_table = _MockLowLevelTable()
|
||
|
|
||
|
# Mock the column families to be returned.
|
||
|
col_fam_name = 'fam'
|
||
|
gc_rule = object()
|
||
|
col_fam = _MockLowLevelColumnFamily(col_fam_name, gc_rule=gc_rule)
|
||
|
col_fams = {col_fam_name: col_fam}
|
||
|
table._low_level_table.column_families = col_fams
|
||
|
|
||
|
to_dict_result = object()
|
||
|
to_dict_calls = []
|
||
|
|
||
|
def mock_gc_rule_to_dict(gc_rule):
|
||
|
to_dict_calls.append(gc_rule)
|
||
|
return to_dict_result
|
||
|
|
||
|
with _Monkey(MUT, _gc_rule_to_dict=mock_gc_rule_to_dict):
|
||
|
result = table.families()
|
||
|
|
||
|
self.assertEqual(result, {col_fam_name: to_dict_result})
|
||
|
self.assertEqual(table._low_level_table.list_column_families_calls, 1)
|
||
|
self.assertEqual(to_dict_calls, [gc_rule])
|
||
|
|
||
|
def test___repr__(self):
|
||
|
name = 'table-name'
|
||
|
table = self._makeOne(name, None)
|
||
|
self.assertEqual(repr(table), '<table.Table name=\'table-name\'>')
|
||
|
|
||
|
def test_regions(self):
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
|
||
|
with self.assertRaises(NotImplementedError):
|
||
|
table.regions()
|
||
|
|
||
|
def test_row_empty_row(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
table._low_level_table = _MockLowLevelTable()
|
||
|
table._low_level_table.read_row_result = None
|
||
|
|
||
|
# Set-up mocks.
|
||
|
fake_filter = object()
|
||
|
mock_filters = []
|
||
|
|
||
|
def mock_filter_chain_helper(**kwargs):
|
||
|
mock_filters.append(kwargs)
|
||
|
return fake_filter
|
||
|
|
||
|
row_key = 'row-key'
|
||
|
timestamp = object()
|
||
|
with _Monkey(MUT, _filter_chain_helper=mock_filter_chain_helper):
|
||
|
result = table.row(row_key, timestamp=timestamp)
|
||
|
|
||
|
# read_row_result == None --> No results.
|
||
|
self.assertEqual(result, {})
|
||
|
|
||
|
read_row_args = (row_key,)
|
||
|
read_row_kwargs = {'filter_': fake_filter}
|
||
|
self.assertEqual(table._low_level_table.read_row_calls, [
|
||
|
(read_row_args, read_row_kwargs),
|
||
|
])
|
||
|
|
||
|
expected_kwargs = {
|
||
|
'filters': [],
|
||
|
'versions': 1,
|
||
|
'timestamp': timestamp,
|
||
|
}
|
||
|
self.assertEqual(mock_filters, [expected_kwargs])
|
||
|
|
||
|
def test_row_with_columns(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
table._low_level_table = _MockLowLevelTable()
|
||
|
table._low_level_table.read_row_result = None
|
||
|
|
||
|
# Set-up mocks.
|
||
|
fake_col_filter = object()
|
||
|
mock_columns = []
|
||
|
|
||
|
def mock_columns_filter_helper(*args):
|
||
|
mock_columns.append(args)
|
||
|
return fake_col_filter
|
||
|
|
||
|
fake_filter = object()
|
||
|
mock_filters = []
|
||
|
|
||
|
def mock_filter_chain_helper(**kwargs):
|
||
|
mock_filters.append(kwargs)
|
||
|
return fake_filter
|
||
|
|
||
|
row_key = 'row-key'
|
||
|
columns = object()
|
||
|
with _Monkey(MUT, _filter_chain_helper=mock_filter_chain_helper,
|
||
|
_columns_filter_helper=mock_columns_filter_helper):
|
||
|
result = table.row(row_key, columns=columns)
|
||
|
|
||
|
# read_row_result == None --> No results.
|
||
|
self.assertEqual(result, {})
|
||
|
|
||
|
read_row_args = (row_key,)
|
||
|
read_row_kwargs = {'filter_': fake_filter}
|
||
|
self.assertEqual(table._low_level_table.read_row_calls, [
|
||
|
(read_row_args, read_row_kwargs),
|
||
|
])
|
||
|
|
||
|
self.assertEqual(mock_columns, [(columns,)])
|
||
|
expected_kwargs = {
|
||
|
'filters': [fake_col_filter],
|
||
|
'versions': 1,
|
||
|
'timestamp': None,
|
||
|
}
|
||
|
self.assertEqual(mock_filters, [expected_kwargs])
|
||
|
|
||
|
def test_row_with_results(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
from gcloud.bigtable.row_data import PartialRowData
|
||
|
|
||
|
row_key = 'row-key'
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
table._low_level_table = _MockLowLevelTable()
|
||
|
partial_row = PartialRowData(row_key)
|
||
|
table._low_level_table.read_row_result = partial_row
|
||
|
|
||
|
# Set-up mocks.
|
||
|
fake_filter = object()
|
||
|
mock_filters = []
|
||
|
|
||
|
def mock_filter_chain_helper(**kwargs):
|
||
|
mock_filters.append(kwargs)
|
||
|
return fake_filter
|
||
|
|
||
|
fake_pair = object()
|
||
|
mock_cells = []
|
||
|
|
||
|
def mock_cells_to_pairs(*args, **kwargs):
|
||
|
mock_cells.append((args, kwargs))
|
||
|
return [fake_pair]
|
||
|
|
||
|
col_fam = u'cf1'
|
||
|
qual = b'qual'
|
||
|
fake_cells = object()
|
||
|
partial_row._cells = {col_fam: {qual: fake_cells}}
|
||
|
include_timestamp = object()
|
||
|
with _Monkey(MUT, _filter_chain_helper=mock_filter_chain_helper,
|
||
|
_cells_to_pairs=mock_cells_to_pairs):
|
||
|
result = table.row(row_key, include_timestamp=include_timestamp)
|
||
|
|
||
|
# The results come from _cells_to_pairs.
|
||
|
expected_result = {col_fam.encode('ascii') + b':' + qual: fake_pair}
|
||
|
self.assertEqual(result, expected_result)
|
||
|
|
||
|
read_row_args = (row_key,)
|
||
|
read_row_kwargs = {'filter_': fake_filter}
|
||
|
self.assertEqual(table._low_level_table.read_row_calls, [
|
||
|
(read_row_args, read_row_kwargs),
|
||
|
])
|
||
|
|
||
|
expected_kwargs = {
|
||
|
'filters': [],
|
||
|
'versions': 1,
|
||
|
'timestamp': None,
|
||
|
}
|
||
|
self.assertEqual(mock_filters, [expected_kwargs])
|
||
|
to_pairs_kwargs = {'include_timestamp': include_timestamp}
|
||
|
self.assertEqual(mock_cells,
|
||
|
[((fake_cells,), to_pairs_kwargs)])
|
||
|
|
||
|
def test_rows_empty_row(self):
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
|
||
|
result = table.rows([])
|
||
|
self.assertEqual(result, [])
|
||
|
|
||
|
def test_rows_with_columns(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
table._low_level_table = _MockLowLevelTable()
|
||
|
rr_result = _MockPartialRowsData()
|
||
|
table._low_level_table.read_rows_result = rr_result
|
||
|
self.assertEqual(rr_result.consume_all_calls, 0)
|
||
|
|
||
|
# Set-up mocks.
|
||
|
fake_col_filter = object()
|
||
|
mock_cols = []
|
||
|
|
||
|
def mock_columns_filter_helper(*args):
|
||
|
mock_cols.append(args)
|
||
|
return fake_col_filter
|
||
|
|
||
|
fake_rows_filter = object()
|
||
|
mock_rows = []
|
||
|
|
||
|
def mock_row_keys_filter_helper(*args):
|
||
|
mock_rows.append(args)
|
||
|
return fake_rows_filter
|
||
|
|
||
|
fake_filter = object()
|
||
|
mock_filters = []
|
||
|
|
||
|
def mock_filter_chain_helper(**kwargs):
|
||
|
mock_filters.append(kwargs)
|
||
|
return fake_filter
|
||
|
|
||
|
rows = ['row-key']
|
||
|
columns = object()
|
||
|
with _Monkey(MUT, _filter_chain_helper=mock_filter_chain_helper,
|
||
|
_row_keys_filter_helper=mock_row_keys_filter_helper,
|
||
|
_columns_filter_helper=mock_columns_filter_helper):
|
||
|
result = table.rows(rows, columns=columns)
|
||
|
|
||
|
# read_rows_result == Empty PartialRowsData --> No results.
|
||
|
self.assertEqual(result, [])
|
||
|
|
||
|
read_rows_args = ()
|
||
|
read_rows_kwargs = {'filter_': fake_filter}
|
||
|
self.assertEqual(table._low_level_table.read_rows_calls, [
|
||
|
(read_rows_args, read_rows_kwargs),
|
||
|
])
|
||
|
self.assertEqual(rr_result.consume_all_calls, 1)
|
||
|
|
||
|
self.assertEqual(mock_cols, [(columns,)])
|
||
|
self.assertEqual(mock_rows, [(rows,)])
|
||
|
expected_kwargs = {
|
||
|
'filters': [fake_col_filter, fake_rows_filter],
|
||
|
'versions': 1,
|
||
|
'timestamp': None,
|
||
|
}
|
||
|
self.assertEqual(mock_filters, [expected_kwargs])
|
||
|
|
||
|
def test_rows_with_results(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
from gcloud.bigtable.row_data import PartialRowData
|
||
|
|
||
|
row_key1 = 'row-key1'
|
||
|
row_key2 = 'row-key2'
|
||
|
rows = [row_key1, row_key2]
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
table._low_level_table = _MockLowLevelTable()
|
||
|
|
||
|
row1 = PartialRowData(row_key1)
|
||
|
# Return row1 but not row2
|
||
|
rr_result = _MockPartialRowsData(rows={row_key1: row1})
|
||
|
table._low_level_table.read_rows_result = rr_result
|
||
|
self.assertEqual(rr_result.consume_all_calls, 0)
|
||
|
|
||
|
# Set-up mocks.
|
||
|
fake_rows_filter = object()
|
||
|
mock_rows = []
|
||
|
|
||
|
def mock_row_keys_filter_helper(*args):
|
||
|
mock_rows.append(args)
|
||
|
return fake_rows_filter
|
||
|
|
||
|
fake_filter = object()
|
||
|
mock_filters = []
|
||
|
|
||
|
def mock_filter_chain_helper(**kwargs):
|
||
|
mock_filters.append(kwargs)
|
||
|
return fake_filter
|
||
|
|
||
|
fake_pair = object()
|
||
|
mock_cells = []
|
||
|
|
||
|
def mock_cells_to_pairs(*args, **kwargs):
|
||
|
mock_cells.append((args, kwargs))
|
||
|
return [fake_pair]
|
||
|
|
||
|
col_fam = u'cf1'
|
||
|
qual = b'qual'
|
||
|
fake_cells = object()
|
||
|
row1._cells = {col_fam: {qual: fake_cells}}
|
||
|
include_timestamp = object()
|
||
|
with _Monkey(MUT, _row_keys_filter_helper=mock_row_keys_filter_helper,
|
||
|
_filter_chain_helper=mock_filter_chain_helper,
|
||
|
_cells_to_pairs=mock_cells_to_pairs):
|
||
|
result = table.rows(rows, include_timestamp=include_timestamp)
|
||
|
|
||
|
# read_rows_result == PartialRowsData with row_key1
|
||
|
expected_result = {col_fam.encode('ascii') + b':' + qual: fake_pair}
|
||
|
self.assertEqual(result, [(row_key1, expected_result)])
|
||
|
|
||
|
read_rows_args = ()
|
||
|
read_rows_kwargs = {'filter_': fake_filter}
|
||
|
self.assertEqual(table._low_level_table.read_rows_calls, [
|
||
|
(read_rows_args, read_rows_kwargs),
|
||
|
])
|
||
|
self.assertEqual(rr_result.consume_all_calls, 1)
|
||
|
|
||
|
self.assertEqual(mock_rows, [(rows,)])
|
||
|
expected_kwargs = {
|
||
|
'filters': [fake_rows_filter],
|
||
|
'versions': 1,
|
||
|
'timestamp': None,
|
||
|
}
|
||
|
self.assertEqual(mock_filters, [expected_kwargs])
|
||
|
to_pairs_kwargs = {'include_timestamp': include_timestamp}
|
||
|
self.assertEqual(mock_cells,
|
||
|
[((fake_cells,), to_pairs_kwargs)])
|
||
|
|
||
|
def test_cells_empty_row(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
table._low_level_table = _MockLowLevelTable()
|
||
|
table._low_level_table.read_row_result = None
|
||
|
|
||
|
# Set-up mocks.
|
||
|
fake_filter = object()
|
||
|
mock_filters = []
|
||
|
|
||
|
def mock_filter_chain_helper(**kwargs):
|
||
|
mock_filters.append(kwargs)
|
||
|
return fake_filter
|
||
|
|
||
|
row_key = 'row-key'
|
||
|
column = 'fam:col1'
|
||
|
with _Monkey(MUT, _filter_chain_helper=mock_filter_chain_helper):
|
||
|
result = table.cells(row_key, column)
|
||
|
|
||
|
# read_row_result == None --> No results.
|
||
|
self.assertEqual(result, [])
|
||
|
|
||
|
read_row_args = (row_key,)
|
||
|
read_row_kwargs = {'filter_': fake_filter}
|
||
|
self.assertEqual(table._low_level_table.read_row_calls, [
|
||
|
(read_row_args, read_row_kwargs),
|
||
|
])
|
||
|
|
||
|
expected_kwargs = {
|
||
|
'column': column,
|
||
|
'versions': None,
|
||
|
'timestamp': None,
|
||
|
}
|
||
|
self.assertEqual(mock_filters, [expected_kwargs])
|
||
|
|
||
|
def test_cells_with_results(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
from gcloud.bigtable.row_data import PartialRowData
|
||
|
|
||
|
row_key = 'row-key'
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
table._low_level_table = _MockLowLevelTable()
|
||
|
partial_row = PartialRowData(row_key)
|
||
|
table._low_level_table.read_row_result = partial_row
|
||
|
|
||
|
# These are all passed to mocks.
|
||
|
versions = object()
|
||
|
timestamp = object()
|
||
|
include_timestamp = object()
|
||
|
|
||
|
# Set-up mocks.
|
||
|
fake_filter = object()
|
||
|
mock_filters = []
|
||
|
|
||
|
def mock_filter_chain_helper(**kwargs):
|
||
|
mock_filters.append(kwargs)
|
||
|
return fake_filter
|
||
|
|
||
|
fake_result = object()
|
||
|
mock_cells = []
|
||
|
|
||
|
def mock_cells_to_pairs(*args, **kwargs):
|
||
|
mock_cells.append((args, kwargs))
|
||
|
return fake_result
|
||
|
|
||
|
col_fam = 'cf1'
|
||
|
qual = 'qual'
|
||
|
fake_cells = object()
|
||
|
partial_row._cells = {col_fam: {qual: fake_cells}}
|
||
|
column = col_fam + ':' + qual
|
||
|
with _Monkey(MUT, _filter_chain_helper=mock_filter_chain_helper,
|
||
|
_cells_to_pairs=mock_cells_to_pairs):
|
||
|
result = table.cells(row_key, column, versions=versions,
|
||
|
timestamp=timestamp,
|
||
|
include_timestamp=include_timestamp)
|
||
|
|
||
|
self.assertEqual(result, fake_result)
|
||
|
|
||
|
read_row_args = (row_key,)
|
||
|
read_row_kwargs = {'filter_': fake_filter}
|
||
|
self.assertEqual(table._low_level_table.read_row_calls, [
|
||
|
(read_row_args, read_row_kwargs),
|
||
|
])
|
||
|
|
||
|
filter_kwargs = {
|
||
|
'column': column,
|
||
|
'versions': versions,
|
||
|
'timestamp': timestamp,
|
||
|
}
|
||
|
self.assertEqual(mock_filters, [filter_kwargs])
|
||
|
to_pairs_kwargs = {'include_timestamp': include_timestamp}
|
||
|
self.assertEqual(mock_cells,
|
||
|
[((fake_cells,), to_pairs_kwargs)])
|
||
|
|
||
|
def test_scan_with_batch_size(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
|
||
|
warned = []
|
||
|
|
||
|
def mock_warn(msg):
|
||
|
warned.append(msg)
|
||
|
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
# Use unknown to force a TypeError, so we don't need to
|
||
|
# stub out the rest of the method.
|
||
|
with self.assertRaises(TypeError):
|
||
|
with _Monkey(MUT, _WARN=mock_warn):
|
||
|
list(table.scan(batch_size=object(), unknown=None))
|
||
|
|
||
|
self.assertEqual(len(warned), 1)
|
||
|
self.assertIn('batch_size', warned[0])
|
||
|
|
||
|
def test_scan_with_scan_batching(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
|
||
|
warned = []
|
||
|
|
||
|
def mock_warn(msg):
|
||
|
warned.append(msg)
|
||
|
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
# Use unknown to force a TypeError, so we don't need to
|
||
|
# stub out the rest of the method.
|
||
|
with self.assertRaises(TypeError):
|
||
|
with _Monkey(MUT, _WARN=mock_warn):
|
||
|
list(table.scan(scan_batching=object(), unknown=None))
|
||
|
|
||
|
self.assertEqual(len(warned), 1)
|
||
|
self.assertIn('scan_batching', warned[0])
|
||
|
|
||
|
def test_scan_with_sorted_columns(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
|
||
|
warned = []
|
||
|
|
||
|
def mock_warn(msg):
|
||
|
warned.append(msg)
|
||
|
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
# Use unknown to force a TypeError, so we don't need to
|
||
|
# stub out the rest of the method.
|
||
|
with self.assertRaises(TypeError):
|
||
|
with _Monkey(MUT, _WARN=mock_warn):
|
||
|
list(table.scan(sorted_columns=object(), unknown=None))
|
||
|
|
||
|
self.assertEqual(len(warned), 1)
|
||
|
self.assertIn('sorted_columns', warned[0])
|
||
|
|
||
|
def test_scan_with_invalid_limit(self):
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
with self.assertRaises(ValueError):
|
||
|
list(table.scan(limit=-10))
|
||
|
|
||
|
def test_scan_with_row_prefix_and_row_start(self):
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
with self.assertRaises(ValueError):
|
||
|
list(table.scan(row_prefix='a', row_stop='abc'))
|
||
|
|
||
|
def test_scan_with_string_filter(self):
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
with self.assertRaises(TypeError):
|
||
|
list(table.scan(filter='some-string'))
|
||
|
|
||
|
def _scan_test_helper(self, row_limits=(None, None), row_prefix=None,
|
||
|
columns=None, filter_=None, timestamp=None,
|
||
|
include_timestamp=False, limit=None, rr_result=None,
|
||
|
expected_result=None):
|
||
|
import types
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
|
||
|
name = 'table-name'
|
||
|
row_start, row_stop = row_limits
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
table._low_level_table = _MockLowLevelTable()
|
||
|
rr_result = rr_result or _MockPartialRowsData()
|
||
|
table._low_level_table.read_rows_result = rr_result
|
||
|
self.assertEqual(rr_result.consume_next_calls, 0)
|
||
|
|
||
|
# Set-up mocks.
|
||
|
fake_col_filter = object()
|
||
|
mock_columns = []
|
||
|
|
||
|
def mock_columns_filter_helper(*args):
|
||
|
mock_columns.append(args)
|
||
|
return fake_col_filter
|
||
|
|
||
|
fake_filter = object()
|
||
|
mock_filters = []
|
||
|
|
||
|
def mock_filter_chain_helper(**kwargs):
|
||
|
mock_filters.append(kwargs)
|
||
|
return fake_filter
|
||
|
|
||
|
with _Monkey(MUT, _filter_chain_helper=mock_filter_chain_helper,
|
||
|
_columns_filter_helper=mock_columns_filter_helper):
|
||
|
result = table.scan(row_start=row_start, row_stop=row_stop,
|
||
|
row_prefix=row_prefix, columns=columns,
|
||
|
filter=filter_, timestamp=timestamp,
|
||
|
include_timestamp=include_timestamp,
|
||
|
limit=limit)
|
||
|
self.assertTrue(isinstance(result, types.GeneratorType))
|
||
|
# Need to consume the result while the monkey patch is applied.
|
||
|
# read_rows_result == Empty PartialRowsData --> No results.
|
||
|
expected_result = expected_result or []
|
||
|
self.assertEqual(list(result), expected_result)
|
||
|
|
||
|
read_rows_args = ()
|
||
|
if row_prefix:
|
||
|
row_start = row_prefix
|
||
|
row_stop = MUT._string_successor(row_prefix)
|
||
|
read_rows_kwargs = {
|
||
|
'end_key': row_stop,
|
||
|
'filter_': fake_filter,
|
||
|
'limit': limit,
|
||
|
'start_key': row_start,
|
||
|
}
|
||
|
self.assertEqual(table._low_level_table.read_rows_calls, [
|
||
|
(read_rows_args, read_rows_kwargs),
|
||
|
])
|
||
|
self.assertEqual(rr_result.consume_next_calls,
|
||
|
rr_result.iterations + 1)
|
||
|
|
||
|
if columns is not None:
|
||
|
self.assertEqual(mock_columns, [(columns,)])
|
||
|
else:
|
||
|
self.assertEqual(mock_columns, [])
|
||
|
|
||
|
filters = []
|
||
|
if filter_ is not None:
|
||
|
filters.append(filter_)
|
||
|
if columns:
|
||
|
filters.append(fake_col_filter)
|
||
|
expected_kwargs = {
|
||
|
'filters': filters,
|
||
|
'versions': 1,
|
||
|
'timestamp': timestamp,
|
||
|
}
|
||
|
self.assertEqual(mock_filters, [expected_kwargs])
|
||
|
|
||
|
def test_scan_with_columns(self):
|
||
|
columns = object()
|
||
|
self._scan_test_helper(columns=columns)
|
||
|
|
||
|
def test_scan_with_row_start_and_stop(self):
|
||
|
row_start = 'bar'
|
||
|
row_stop = 'foo'
|
||
|
row_limits = (row_start, row_stop)
|
||
|
self._scan_test_helper(row_limits=row_limits)
|
||
|
|
||
|
def test_scan_with_row_prefix(self):
|
||
|
row_prefix = 'row-prefi'
|
||
|
self._scan_test_helper(row_prefix=row_prefix)
|
||
|
|
||
|
def test_scan_with_filter(self):
|
||
|
mock_filter = object()
|
||
|
self._scan_test_helper(filter_=mock_filter)
|
||
|
|
||
|
def test_scan_with_no_results(self):
|
||
|
limit = 1337
|
||
|
timestamp = object()
|
||
|
self._scan_test_helper(timestamp=timestamp, limit=limit)
|
||
|
|
||
|
def test_scan_with_results(self):
|
||
|
from gcloud.bigtable.row_data import PartialRowData
|
||
|
|
||
|
row_key1 = 'row-key1'
|
||
|
row1 = PartialRowData(row_key1)
|
||
|
rr_result = _MockPartialRowsData(rows={row_key1: row1}, iterations=1)
|
||
|
|
||
|
include_timestamp = object()
|
||
|
expected_result = [(row_key1, {})]
|
||
|
self._scan_test_helper(include_timestamp=include_timestamp,
|
||
|
rr_result=rr_result,
|
||
|
expected_result=expected_result)
|
||
|
|
||
|
def test_put(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
from gcloud.bigtable.happybase.table import _WAL_SENTINEL
|
||
|
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
batches_created = []
|
||
|
|
||
|
def make_batch(*args, **kwargs):
|
||
|
result = _MockBatch(*args, **kwargs)
|
||
|
batches_created.append(result)
|
||
|
return result
|
||
|
|
||
|
row = 'row-key'
|
||
|
data = {'fam:col': 'foo'}
|
||
|
timestamp = None
|
||
|
with _Monkey(MUT, Batch=make_batch):
|
||
|
result = table.put(row, data, timestamp=timestamp)
|
||
|
|
||
|
# There is no return value.
|
||
|
self.assertEqual(result, None)
|
||
|
|
||
|
# Check how the batch was created and used.
|
||
|
batch, = batches_created
|
||
|
self.assertTrue(isinstance(batch, _MockBatch))
|
||
|
self.assertEqual(batch.args, (table,))
|
||
|
expected_kwargs = {
|
||
|
'timestamp': timestamp,
|
||
|
'batch_size': None,
|
||
|
'transaction': False,
|
||
|
'wal': _WAL_SENTINEL,
|
||
|
}
|
||
|
self.assertEqual(batch.kwargs, expected_kwargs)
|
||
|
# Make sure it was a successful context manager
|
||
|
self.assertEqual(batch.exit_vals, [(None, None, None)])
|
||
|
self.assertEqual(batch.put_args, [(row, data)])
|
||
|
self.assertEqual(batch.delete_args, [])
|
||
|
|
||
|
def test_delete(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
from gcloud.bigtable.happybase.table import _WAL_SENTINEL
|
||
|
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
batches_created = []
|
||
|
|
||
|
def make_batch(*args, **kwargs):
|
||
|
result = _MockBatch(*args, **kwargs)
|
||
|
batches_created.append(result)
|
||
|
return result
|
||
|
|
||
|
row = 'row-key'
|
||
|
columns = ['fam:col1', 'fam:col2']
|
||
|
timestamp = None
|
||
|
with _Monkey(MUT, Batch=make_batch):
|
||
|
result = table.delete(row, columns=columns, timestamp=timestamp)
|
||
|
|
||
|
# There is no return value.
|
||
|
self.assertEqual(result, None)
|
||
|
|
||
|
# Check how the batch was created and used.
|
||
|
batch, = batches_created
|
||
|
self.assertTrue(isinstance(batch, _MockBatch))
|
||
|
self.assertEqual(batch.args, (table,))
|
||
|
expected_kwargs = {
|
||
|
'timestamp': timestamp,
|
||
|
'batch_size': None,
|
||
|
'transaction': False,
|
||
|
'wal': _WAL_SENTINEL,
|
||
|
}
|
||
|
self.assertEqual(batch.kwargs, expected_kwargs)
|
||
|
# Make sure it was a successful context manager
|
||
|
self.assertEqual(batch.exit_vals, [(None, None, None)])
|
||
|
self.assertEqual(batch.put_args, [])
|
||
|
self.assertEqual(batch.delete_args, [(row, columns)])
|
||
|
|
||
|
def test_batch(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.bigtable.happybase import table as MUT
|
||
|
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
|
||
|
timestamp = object()
|
||
|
batch_size = 42
|
||
|
transaction = False # Must be False when batch_size is non-null
|
||
|
wal = object()
|
||
|
|
||
|
with _Monkey(MUT, Batch=_MockBatch):
|
||
|
result = table.batch(timestamp=timestamp, batch_size=batch_size,
|
||
|
transaction=transaction, wal=wal)
|
||
|
|
||
|
self.assertTrue(isinstance(result, _MockBatch))
|
||
|
self.assertEqual(result.args, (table,))
|
||
|
expected_kwargs = {
|
||
|
'timestamp': timestamp,
|
||
|
'batch_size': batch_size,
|
||
|
'transaction': transaction,
|
||
|
'wal': wal,
|
||
|
}
|
||
|
self.assertEqual(result.kwargs, expected_kwargs)
|
||
|
|
||
|
def test_counter_get(self):
|
||
|
klass = self._getTargetClass()
|
||
|
counter_value = 1337
|
||
|
|
||
|
class TableWithInc(klass):
|
||
|
|
||
|
incremented = []
|
||
|
value = counter_value
|
||
|
|
||
|
def counter_inc(self, row, column, value=1):
|
||
|
self.incremented.append((row, column, value))
|
||
|
self.value += value
|
||
|
return self.value
|
||
|
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = TableWithInc(name, connection)
|
||
|
|
||
|
row = 'row-key'
|
||
|
column = 'fam:col1'
|
||
|
self.assertEqual(TableWithInc.incremented, [])
|
||
|
result = table.counter_get(row, column)
|
||
|
self.assertEqual(result, counter_value)
|
||
|
self.assertEqual(TableWithInc.incremented, [(row, column, 0)])
|
||
|
|
||
|
def test_counter_dec(self):
|
||
|
klass = self._getTargetClass()
|
||
|
counter_value = 42
|
||
|
|
||
|
class TableWithInc(klass):
|
||
|
|
||
|
incremented = []
|
||
|
value = counter_value
|
||
|
|
||
|
def counter_inc(self, row, column, value=1):
|
||
|
self.incremented.append((row, column, value))
|
||
|
self.value += value
|
||
|
return self.value
|
||
|
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = TableWithInc(name, connection)
|
||
|
|
||
|
row = 'row-key'
|
||
|
column = 'fam:col1'
|
||
|
dec_value = 987
|
||
|
self.assertEqual(TableWithInc.incremented, [])
|
||
|
result = table.counter_dec(row, column, value=dec_value)
|
||
|
self.assertEqual(result, counter_value - dec_value)
|
||
|
self.assertEqual(TableWithInc.incremented, [(row, column, -dec_value)])
|
||
|
|
||
|
def _counter_inc_helper(self, row, column, value, commit_result):
|
||
|
import six
|
||
|
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
# Mock the return values.
|
||
|
table._low_level_table = _MockLowLevelTable()
|
||
|
table._low_level_table.row_values[row] = row_obj = _MockLowLevelRow(
|
||
|
row, commit_result=commit_result)
|
||
|
|
||
|
self.assertFalse(row_obj._append)
|
||
|
result = table.counter_inc(row, column, value=value)
|
||
|
self.assertTrue(row_obj._append)
|
||
|
|
||
|
incremented_value = value + _MockLowLevelRow.COUNTER_DEFAULT
|
||
|
self.assertEqual(result, incremented_value)
|
||
|
|
||
|
# Check the row values returned.
|
||
|
row_obj = table._low_level_table.row_values[row]
|
||
|
if isinstance(column, six.binary_type):
|
||
|
column = column.decode('utf-8')
|
||
|
self.assertEqual(row_obj.counts,
|
||
|
{tuple(column.split(':')): incremented_value})
|
||
|
|
||
|
def test_counter_set(self):
|
||
|
name = 'table-name'
|
||
|
connection = None
|
||
|
table = self._makeOne(name, connection)
|
||
|
|
||
|
row = 'row-key'
|
||
|
column = 'fam:col1'
|
||
|
value = 42
|
||
|
with self.assertRaises(NotImplementedError):
|
||
|
table.counter_set(row, column, value=value)
|
||
|
|
||
|
def test_counter_inc(self):
|
||
|
import struct
|
||
|
|
||
|
row = 'row-key'
|
||
|
col_fam = u'fam'
|
||
|
col_qual = u'col1'
|
||
|
column = col_fam + u':' + col_qual
|
||
|
value = 42
|
||
|
packed_value = struct.pack('>q', value)
|
||
|
fake_timestamp = None
|
||
|
commit_result = {
|
||
|
col_fam: {
|
||
|
col_qual: [(packed_value, fake_timestamp)],
|
||
|
}
|
||
|
}
|
||
|
self._counter_inc_helper(row, column, value, commit_result)
|
||
|
|
||
|
def test_counter_inc_column_bytes(self):
|
||
|
import struct
|
||
|
|
||
|
row = 'row-key'
|
||
|
col_fam = b'fam'
|
||
|
col_qual = b'col1'
|
||
|
column = col_fam + b':' + col_qual
|
||
|
value = 42
|
||
|
packed_value = struct.pack('>q', value)
|
||
|
fake_timestamp = None
|
||
|
commit_result = {
|
||
|
col_fam.decode('utf-8'): {
|
||
|
col_qual.decode('utf-8'): [(packed_value, fake_timestamp)],
|
||
|
}
|
||
|
}
|
||
|
self._counter_inc_helper(row, column, value, commit_result)
|
||
|
|
||
|
def test_counter_inc_bad_result(self):
|
||
|
row = 'row-key'
|
||
|
col_fam = 'fam'
|
||
|
col_qual = 'col1'
|
||
|
column = col_fam + ':' + col_qual
|
||
|
value = 42
|
||
|
commit_result = None
|
||
|
with self.assertRaises(TypeError):
|
||
|
self._counter_inc_helper(row, column, value, commit_result)
|
||
|
|
||
|
def test_counter_inc_result_key_error(self):
|
||
|
row = 'row-key'
|
||
|
col_fam = 'fam'
|
||
|
col_qual = 'col1'
|
||
|
column = col_fam + ':' + col_qual
|
||
|
value = 42
|
||
|
commit_result = {}
|
||
|
with self.assertRaises(KeyError):
|
||
|
self._counter_inc_helper(row, column, value, commit_result)
|
||
|
|
||
|
def test_counter_inc_result_nested_key_error(self):
|
||
|
row = 'row-key'
|
||
|
col_fam = 'fam'
|
||
|
col_qual = 'col1'
|
||
|
column = col_fam + ':' + col_qual
|
||
|
value = 42
|
||
|
commit_result = {col_fam: {}}
|
||
|
with self.assertRaises(KeyError):
|
||
|
self._counter_inc_helper(row, column, value, commit_result)
|
||
|
|
||
|
def test_counter_inc_result_non_unique_cell(self):
|
||
|
row = 'row-key'
|
||
|
col_fam = 'fam'
|
||
|
col_qual = 'col1'
|
||
|
column = col_fam + ':' + col_qual
|
||
|
value = 42
|
||
|
fake_timestamp = None
|
||
|
packed_value = None
|
||
|
commit_result = {
|
||
|
col_fam: {
|
||
|
col_qual: [
|
||
|
(packed_value, fake_timestamp),
|
||
|
(packed_value, fake_timestamp),
|
||
|
],
|
||
|
}
|
||
|
}
|
||
|
with self.assertRaises(ValueError):
|
||
|
self._counter_inc_helper(row, column, value, commit_result)
|
||
|
|
||
|
|
||
|
class Test__gc_rule_to_dict(unittest2.TestCase):
|
||
|
|
||
|
def _callFUT(self, *args, **kwargs):
|
||
|
from gcloud.bigtable.happybase.table import _gc_rule_to_dict
|
||
|
return _gc_rule_to_dict(*args, **kwargs)
|
||
|
|
||
|
def test_with_null(self):
|
||
|
gc_rule = None
|
||
|
result = self._callFUT(gc_rule)
|
||
|
self.assertEqual(result, {})
|
||
|
|
||
|
def test_with_max_versions(self):
|
||
|
from gcloud.bigtable.column_family import MaxVersionsGCRule
|
||
|
|
||
|
max_versions = 2
|
||
|
gc_rule = MaxVersionsGCRule(max_versions)
|
||
|
result = self._callFUT(gc_rule)
|
||
|
expected_result = {'max_versions': max_versions}
|
||
|
self.assertEqual(result, expected_result)
|
||
|
|
||
|
def test_with_max_age(self):
|
||
|
import datetime
|
||
|
from gcloud.bigtable.column_family import MaxAgeGCRule
|
||
|
|
||
|
time_to_live = 101
|
||
|
max_age = datetime.timedelta(seconds=time_to_live)
|
||
|
gc_rule = MaxAgeGCRule(max_age)
|
||
|
result = self._callFUT(gc_rule)
|
||
|
expected_result = {'time_to_live': time_to_live}
|
||
|
self.assertEqual(result, expected_result)
|
||
|
|
||
|
def test_with_non_gc_rule(self):
|
||
|
gc_rule = object()
|
||
|
result = self._callFUT(gc_rule)
|
||
|
self.assertTrue(result is gc_rule)
|
||
|
|
||
|
def test_with_gc_rule_union(self):
|
||
|
from gcloud.bigtable.column_family import GCRuleUnion
|
||
|
|
||
|
gc_rule = GCRuleUnion(rules=[])
|
||
|
result = self._callFUT(gc_rule)
|
||
|
self.assertTrue(result is gc_rule)
|
||
|
|
||
|
def test_with_intersection_other_than_two(self):
|
||
|
from gcloud.bigtable.column_family import GCRuleIntersection
|
||
|
|
||
|
gc_rule = GCRuleIntersection(rules=[])
|
||
|
result = self._callFUT(gc_rule)
|
||
|
self.assertTrue(result is gc_rule)
|
||
|
|
||
|
def test_with_intersection_two_max_num_versions(self):
|
||
|
from gcloud.bigtable.column_family import GCRuleIntersection
|
||
|
from gcloud.bigtable.column_family import MaxVersionsGCRule
|
||
|
|
||
|
rule1 = MaxVersionsGCRule(1)
|
||
|
rule2 = MaxVersionsGCRule(2)
|
||
|
gc_rule = GCRuleIntersection(rules=[rule1, rule2])
|
||
|
result = self._callFUT(gc_rule)
|
||
|
self.assertTrue(result is gc_rule)
|
||
|
|
||
|
def test_with_intersection_two_rules(self):
|
||
|
import datetime
|
||
|
from gcloud.bigtable.column_family import GCRuleIntersection
|
||
|
from gcloud.bigtable.column_family import MaxAgeGCRule
|
||
|
from gcloud.bigtable.column_family import MaxVersionsGCRule
|
||
|
|
||
|
time_to_live = 101
|
||
|
max_age = datetime.timedelta(seconds=time_to_live)
|
||
|
rule1 = MaxAgeGCRule(max_age)
|
||
|
max_versions = 2
|
||
|
rule2 = MaxVersionsGCRule(max_versions)
|
||
|
gc_rule = GCRuleIntersection(rules=[rule1, rule2])
|
||
|
result = self._callFUT(gc_rule)
|
||
|
expected_result = {
|
||
|
'max_versions': max_versions,
|
||
|
'time_to_live': time_to_live,
|
||
|
}
|
||
|
self.assertEqual(result, expected_result)
|
||
|
|
||
|
def test_with_intersection_two_nested_rules(self):
|
||
|
from gcloud.bigtable.column_family import GCRuleIntersection
|
||
|
|
||
|
rule1 = GCRuleIntersection(rules=[])
|
||
|
rule2 = GCRuleIntersection(rules=[])
|
||
|
gc_rule = GCRuleIntersection(rules=[rule1, rule2])
|
||
|
result = self._callFUT(gc_rule)
|
||
|
self.assertTrue(result is gc_rule)
|
||
|
|
||
|
|
||
|
class Test__string_successor(unittest2.TestCase):
|
||
|
|
||
|
def _callFUT(self, *args, **kwargs):
|
||
|
from gcloud.bigtable.happybase.table import _string_successor
|
||
|
return _string_successor(*args, **kwargs)
|
||
|
|
||
|
def test_with_alphanumeric(self):
|
||
|
self.assertEqual(self._callFUT(b'boa'), b'bob')
|
||
|
self.assertEqual(self._callFUT(b'abc1'), b'abc2')
|
||
|
|
||
|
def test_with_last_byte(self):
|
||
|
self.assertEqual(self._callFUT(b'boa\xff'), b'bob')
|
||
|
|
||
|
def test_with_empty_string(self):
|
||
|
self.assertEqual(self._callFUT(b''), b'')
|
||
|
|
||
|
def test_with_all_last_bytes(self):
|
||
|
self.assertEqual(self._callFUT(b'\xff\xff\xff'), b'')
|
||
|
|
||
|
def test_with_unicode_input(self):
|
||
|
self.assertEqual(self._callFUT(u'boa'), b'bob')
|
||
|
|
||
|
|
||
|
class Test__convert_to_time_range(unittest2.TestCase):
|
||
|
|
||
|
def _callFUT(self, timestamp=None):
|
||
|
from gcloud.bigtable.happybase.table import _convert_to_time_range
|
||
|
return _convert_to_time_range(timestamp=timestamp)
|
||
|
|
||
|
def test_null(self):
|
||
|
timestamp = None
|
||
|
result = self._callFUT(timestamp=timestamp)
|
||
|
self.assertEqual(result, None)
|
||
|
|
||
|
def test_invalid_type(self):
|
||
|
timestamp = object()
|
||
|
with self.assertRaises(TypeError):
|
||
|
self._callFUT(timestamp=timestamp)
|
||
|
|
||
|
def test_success(self):
|
||
|
from gcloud._helpers import _datetime_from_microseconds
|
||
|
from gcloud.bigtable.row_filters import TimestampRange
|
||
|
|
||
|
timestamp = 1441928298571
|
||
|
ts_dt = _datetime_from_microseconds(1000 * timestamp)
|
||
|
result = self._callFUT(timestamp=timestamp)
|
||
|
self.assertTrue(isinstance(result, TimestampRange))
|
||
|
self.assertEqual(result.start, None)
|
||
|
self.assertEqual(result.end, ts_dt)
|
||
|
|
||
|
|
||
|
class Test__cells_to_pairs(unittest2.TestCase):
|
||
|
|
||
|
def _callFUT(self, *args, **kwargs):
|
||
|
from gcloud.bigtable.happybase.table import _cells_to_pairs
|
||
|
return _cells_to_pairs(*args, **kwargs)
|
||
|
|
||
|
def test_without_timestamp(self):
|
||
|
from gcloud.bigtable.row_data import Cell
|
||
|
|
||
|
value1 = 'foo'
|
||
|
cell1 = Cell(value=value1, timestamp=None)
|
||
|
value2 = 'bar'
|
||
|
cell2 = Cell(value=value2, timestamp=None)
|
||
|
|
||
|
result = self._callFUT([cell1, cell2])
|
||
|
self.assertEqual(result, [value1, value2])
|
||
|
|
||
|
def test_with_timestamp(self):
|
||
|
from gcloud._helpers import _datetime_from_microseconds
|
||
|
from gcloud.bigtable.row_data import Cell
|
||
|
|
||
|
value1 = 'foo'
|
||
|
ts1_millis = 1221934570148
|
||
|
ts1 = _datetime_from_microseconds(ts1_millis * 1000)
|
||
|
cell1 = Cell(value=value1, timestamp=ts1)
|
||
|
|
||
|
value2 = 'bar'
|
||
|
ts2_millis = 1221955575548
|
||
|
ts2 = _datetime_from_microseconds(ts2_millis * 1000)
|
||
|
cell2 = Cell(value=value2, timestamp=ts2)
|
||
|
|
||
|
result = self._callFUT([cell1, cell2], include_timestamp=True)
|
||
|
self.assertEqual(result,
|
||
|
[(value1, ts1_millis), (value2, ts2_millis)])
|
||
|
|
||
|
|
||
|
class Test__partial_row_to_dict(unittest2.TestCase):
|
||
|
|
||
|
def _callFUT(self, partial_row_data, include_timestamp=False):
|
||
|
from gcloud.bigtable.happybase.table import _partial_row_to_dict
|
||
|
return _partial_row_to_dict(partial_row_data,
|
||
|
include_timestamp=include_timestamp)
|
||
|
|
||
|
def test_without_timestamp(self):
|
||
|
from gcloud.bigtable.row_data import Cell
|
||
|
from gcloud.bigtable.row_data import PartialRowData
|
||
|
|
||
|
row_data = PartialRowData(b'row-key')
|
||
|
val1 = b'hi-im-bytes'
|
||
|
val2 = b'bi-im-hytes'
|
||
|
row_data._cells[u'fam1'] = {
|
||
|
b'col1': [Cell(val1, None)],
|
||
|
b'col2': [Cell(val2, None)],
|
||
|
}
|
||
|
result = self._callFUT(row_data)
|
||
|
expected_result = {
|
||
|
b'fam1:col1': val1,
|
||
|
b'fam1:col2': val2,
|
||
|
}
|
||
|
self.assertEqual(result, expected_result)
|
||
|
|
||
|
def test_with_timestamp(self):
|
||
|
from gcloud._helpers import _datetime_from_microseconds
|
||
|
from gcloud.bigtable.row_data import Cell
|
||
|
from gcloud.bigtable.row_data import PartialRowData
|
||
|
|
||
|
row_data = PartialRowData(b'row-key')
|
||
|
val1 = b'hi-im-bytes'
|
||
|
ts1_millis = 1221934570148
|
||
|
ts1 = _datetime_from_microseconds(ts1_millis * 1000)
|
||
|
val2 = b'bi-im-hytes'
|
||
|
ts2_millis = 1331934880000
|
||
|
ts2 = _datetime_from_microseconds(ts2_millis * 1000)
|
||
|
row_data._cells[u'fam1'] = {
|
||
|
b'col1': [Cell(val1, ts1)],
|
||
|
b'col2': [Cell(val2, ts2)],
|
||
|
}
|
||
|
result = self._callFUT(row_data, include_timestamp=True)
|
||
|
expected_result = {
|
||
|
b'fam1:col1': (val1, ts1_millis),
|
||
|
b'fam1:col2': (val2, ts2_millis),
|
||
|
}
|
||
|
self.assertEqual(result, expected_result)
|
||
|
|
||
|
|
||
|
class Test__filter_chain_helper(unittest2.TestCase):
|
||
|
|
||
|
def _callFUT(self, *args, **kwargs):
|
||
|
from gcloud.bigtable.happybase.table import _filter_chain_helper
|
||
|
return _filter_chain_helper(*args, **kwargs)
|
||
|
|
||
|
def test_no_filters(self):
|
||
|
with self.assertRaises(ValueError):
|
||
|
self._callFUT()
|
||
|
|
||
|
def test_single_filter(self):
|
||
|
from gcloud.bigtable.row_filters import CellsColumnLimitFilter
|
||
|
|
||
|
versions = 1337
|
||
|
result = self._callFUT(versions=versions)
|
||
|
self.assertTrue(isinstance(result, CellsColumnLimitFilter))
|
||
|
# Relies on the fact that RowFilter instances can
|
||
|
# only have one value set.
|
||
|
self.assertEqual(result.num_cells, versions)
|
||
|
|
||
|
def test_existing_filters(self):
|
||
|
from gcloud.bigtable.row_filters import CellsColumnLimitFilter
|
||
|
|
||
|
filters = []
|
||
|
versions = 1337
|
||
|
result = self._callFUT(versions=versions, filters=filters)
|
||
|
# Make sure filters has grown.
|
||
|
self.assertEqual(filters, [result])
|
||
|
|
||
|
self.assertTrue(isinstance(result, CellsColumnLimitFilter))
|
||
|
# Relies on the fact that RowFilter instances can
|
||
|
# only have one value set.
|
||
|
self.assertEqual(result.num_cells, versions)
|
||
|
|
||
|
def _column_helper(self, num_filters, versions=None, timestamp=None,
|
||
|
column=None, col_fam=None, qual=None):
|
||
|
from gcloud.bigtable.row_filters import ColumnQualifierRegexFilter
|
||
|
from gcloud.bigtable.row_filters import FamilyNameRegexFilter
|
||
|
from gcloud.bigtable.row_filters import RowFilterChain
|
||
|
|
||
|
if col_fam is None:
|
||
|
col_fam = 'cf1'
|
||
|
if qual is None:
|
||
|
qual = 'qual'
|
||
|
if column is None:
|
||
|
column = col_fam + ':' + qual
|
||
|
result = self._callFUT(column, versions=versions, timestamp=timestamp)
|
||
|
self.assertTrue(isinstance(result, RowFilterChain))
|
||
|
|
||
|
self.assertEqual(len(result.filters), num_filters)
|
||
|
fam_filter = result.filters[0]
|
||
|
qual_filter = result.filters[1]
|
||
|
self.assertTrue(isinstance(fam_filter, FamilyNameRegexFilter))
|
||
|
self.assertTrue(isinstance(qual_filter, ColumnQualifierRegexFilter))
|
||
|
|
||
|
# Relies on the fact that RowFilter instances can
|
||
|
# only have one value set.
|
||
|
self.assertEqual(fam_filter.regex, col_fam.encode('utf-8'))
|
||
|
self.assertEqual(qual_filter.regex, qual.encode('utf-8'))
|
||
|
|
||
|
return result
|
||
|
|
||
|
def test_column_only(self):
|
||
|
self._column_helper(num_filters=2)
|
||
|
|
||
|
def test_column_bytes(self):
|
||
|
self._column_helper(num_filters=2, column=b'cfB:qualY',
|
||
|
col_fam=u'cfB', qual=u'qualY')
|
||
|
|
||
|
def test_column_unicode(self):
|
||
|
self._column_helper(num_filters=2, column=u'cfU:qualN',
|
||
|
col_fam=u'cfU', qual=u'qualN')
|
||
|
|
||
|
def test_with_versions(self):
|
||
|
from gcloud.bigtable.row_filters import CellsColumnLimitFilter
|
||
|
|
||
|
versions = 11
|
||
|
result = self._column_helper(num_filters=3, versions=versions)
|
||
|
|
||
|
version_filter = result.filters[2]
|
||
|
self.assertTrue(isinstance(version_filter, CellsColumnLimitFilter))
|
||
|
# Relies on the fact that RowFilter instances can
|
||
|
# only have one value set.
|
||
|
self.assertEqual(version_filter.num_cells, versions)
|
||
|
|
||
|
def test_with_timestamp(self):
|
||
|
from gcloud._helpers import _datetime_from_microseconds
|
||
|
from gcloud.bigtable.row_filters import TimestampRange
|
||
|
from gcloud.bigtable.row_filters import TimestampRangeFilter
|
||
|
|
||
|
timestamp = 1441928298571
|
||
|
result = self._column_helper(num_filters=3, timestamp=timestamp)
|
||
|
|
||
|
range_filter = result.filters[2]
|
||
|
self.assertTrue(isinstance(range_filter, TimestampRangeFilter))
|
||
|
# Relies on the fact that RowFilter instances can
|
||
|
# only have one value set.
|
||
|
time_range = range_filter.range_
|
||
|
self.assertTrue(isinstance(time_range, TimestampRange))
|
||
|
self.assertEqual(time_range.start, None)
|
||
|
ts_dt = _datetime_from_microseconds(1000 * timestamp)
|
||
|
self.assertEqual(time_range.end, ts_dt)
|
||
|
|
||
|
def test_with_all_options(self):
|
||
|
versions = 11
|
||
|
timestamp = 1441928298571
|
||
|
self._column_helper(num_filters=4, versions=versions,
|
||
|
timestamp=timestamp)
|
||
|
|
||
|
|
||
|
class Test__columns_filter_helper(unittest2.TestCase):
|
||
|
|
||
|
def _callFUT(self, *args, **kwargs):
|
||
|
from gcloud.bigtable.happybase.table import _columns_filter_helper
|
||
|
return _columns_filter_helper(*args, **kwargs)
|
||
|
|
||
|
def test_no_columns(self):
|
||
|
columns = []
|
||
|
with self.assertRaises(ValueError):
|
||
|
self._callFUT(columns)
|
||
|
|
||
|
def test_single_column(self):
|
||
|
from gcloud.bigtable.row_filters import FamilyNameRegexFilter
|
||
|
|
||
|
col_fam = 'cf1'
|
||
|
columns = [col_fam]
|
||
|
result = self._callFUT(columns)
|
||
|
expected_result = FamilyNameRegexFilter(col_fam)
|
||
|
self.assertEqual(result, expected_result)
|
||
|
|
||
|
def test_column_and_column_families(self):
|
||
|
from gcloud.bigtable.row_filters import ColumnQualifierRegexFilter
|
||
|
from gcloud.bigtable.row_filters import FamilyNameRegexFilter
|
||
|
from gcloud.bigtable.row_filters import RowFilterChain
|
||
|
from gcloud.bigtable.row_filters import RowFilterUnion
|
||
|
|
||
|
col_fam1 = 'cf1'
|
||
|
col_fam2 = 'cf2'
|
||
|
col_qual2 = 'qual2'
|
||
|
columns = [col_fam1, col_fam2 + ':' + col_qual2]
|
||
|
result = self._callFUT(columns)
|
||
|
|
||
|
self.assertTrue(isinstance(result, RowFilterUnion))
|
||
|
self.assertEqual(len(result.filters), 2)
|
||
|
filter1 = result.filters[0]
|
||
|
filter2 = result.filters[1]
|
||
|
|
||
|
self.assertTrue(isinstance(filter1, FamilyNameRegexFilter))
|
||
|
self.assertEqual(filter1.regex, col_fam1.encode('utf-8'))
|
||
|
|
||
|
self.assertTrue(isinstance(filter2, RowFilterChain))
|
||
|
filter2a, filter2b = filter2.filters
|
||
|
self.assertTrue(isinstance(filter2a, FamilyNameRegexFilter))
|
||
|
self.assertEqual(filter2a.regex, col_fam2.encode('utf-8'))
|
||
|
self.assertTrue(isinstance(filter2b, ColumnQualifierRegexFilter))
|
||
|
self.assertEqual(filter2b.regex, col_qual2.encode('utf-8'))
|
||
|
|
||
|
|
||
|
class Test__row_keys_filter_helper(unittest2.TestCase):
|
||
|
|
||
|
def _callFUT(self, *args, **kwargs):
|
||
|
from gcloud.bigtable.happybase.table import _row_keys_filter_helper
|
||
|
return _row_keys_filter_helper(*args, **kwargs)
|
||
|
|
||
|
def test_no_rows(self):
|
||
|
row_keys = []
|
||
|
with self.assertRaises(ValueError):
|
||
|
self._callFUT(row_keys)
|
||
|
|
||
|
def test_single_row(self):
|
||
|
from gcloud.bigtable.row_filters import RowKeyRegexFilter
|
||
|
|
||
|
row_key = b'row-key'
|
||
|
row_keys = [row_key]
|
||
|
result = self._callFUT(row_keys)
|
||
|
expected_result = RowKeyRegexFilter(row_key)
|
||
|
self.assertEqual(result, expected_result)
|
||
|
|
||
|
def test_many_rows(self):
|
||
|
from gcloud.bigtable.row_filters import RowFilterUnion
|
||
|
from gcloud.bigtable.row_filters import RowKeyRegexFilter
|
||
|
|
||
|
row_key1 = b'row-key1'
|
||
|
row_key2 = b'row-key2'
|
||
|
row_key3 = b'row-key3'
|
||
|
row_keys = [row_key1, row_key2, row_key3]
|
||
|
result = self._callFUT(row_keys)
|
||
|
|
||
|
filter1 = RowKeyRegexFilter(row_key1)
|
||
|
filter2 = RowKeyRegexFilter(row_key2)
|
||
|
filter3 = RowKeyRegexFilter(row_key3)
|
||
|
expected_result = RowFilterUnion(filters=[filter1, filter2, filter3])
|
||
|
self.assertEqual(result, expected_result)
|
||
|
|
||
|
|
||
|
class _Connection(object):
|
||
|
|
||
|
def __init__(self, instance):
|
||
|
self._instance = instance
|
||
|
|
||
|
|
||
|
class _MockLowLevelColumnFamily(object):
|
||
|
|
||
|
def __init__(self, column_family_id, gc_rule=None):
|
||
|
self.column_family_id = column_family_id
|
||
|
self.gc_rule = gc_rule
|
||
|
|
||
|
|
||
|
class _MockLowLevelTable(object):
|
||
|
|
||
|
def __init__(self, *args, **kwargs):
|
||
|
self.args = args
|
||
|
self.kwargs = kwargs
|
||
|
self.list_column_families_calls = 0
|
||
|
self.column_families = {}
|
||
|
self.row_values = {}
|
||
|
self.read_row_calls = []
|
||
|
self.read_row_result = None
|
||
|
self.read_rows_calls = []
|
||
|
self.read_rows_result = None
|
||
|
|
||
|
def list_column_families(self):
|
||
|
self.list_column_families_calls += 1
|
||
|
return self.column_families
|
||
|
|
||
|
def row(self, row_key, append=None):
|
||
|
result = self.row_values[row_key]
|
||
|
result._append = append
|
||
|
return result
|
||
|
|
||
|
def read_row(self, *args, **kwargs):
|
||
|
self.read_row_calls.append((args, kwargs))
|
||
|
return self.read_row_result
|
||
|
|
||
|
def read_rows(self, *args, **kwargs):
|
||
|
self.read_rows_calls.append((args, kwargs))
|
||
|
return self.read_rows_result
|
||
|
|
||
|
|
||
|
class _MockLowLevelRow(object):
|
||
|
|
||
|
COUNTER_DEFAULT = 0
|
||
|
|
||
|
def __init__(self, row_key, commit_result=None):
|
||
|
self.row_key = row_key
|
||
|
self._append = False
|
||
|
self.counts = {}
|
||
|
self.commit_result = commit_result
|
||
|
|
||
|
def increment_cell_value(self, column_family_id, column, int_value):
|
||
|
count = self.counts.setdefault((column_family_id, column),
|
||
|
self.COUNTER_DEFAULT)
|
||
|
self.counts[(column_family_id, column)] = count + int_value
|
||
|
|
||
|
def commit(self):
|
||
|
return self.commit_result
|
||
|
|
||
|
|
||
|
class _MockBatch(object):
|
||
|
|
||
|
def __init__(self, *args, **kwargs):
|
||
|
self.args = args
|
||
|
self.kwargs = kwargs
|
||
|
self.exit_vals = []
|
||
|
self.put_args = []
|
||
|
self.delete_args = []
|
||
|
|
||
|
def __enter__(self):
|
||
|
return self
|
||
|
|
||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||
|
self.exit_vals.append((exc_type, exc_value, traceback))
|
||
|
|
||
|
def put(self, *args):
|
||
|
self.put_args.append(args)
|
||
|
|
||
|
def delete(self, *args):
|
||
|
self.delete_args.append(args)
|
||
|
|
||
|
|
||
|
class _MockPartialRowsData(object):
|
||
|
|
||
|
def __init__(self, rows=None, iterations=0):
|
||
|
self.rows = rows or {}
|
||
|
self.consume_all_calls = 0
|
||
|
self.consume_next_calls = 0
|
||
|
self.iterations = iterations
|
||
|
|
||
|
def consume_all(self):
|
||
|
self.consume_all_calls += 1
|
||
|
|
||
|
def consume_next(self):
|
||
|
self.consume_next_calls += 1
|
||
|
if self.consume_next_calls > self.iterations:
|
||
|
raise StopIteration
|