@@ -278,6 +278,162 @@ def test_table_factory_with_prefix(self):
278278 def test_table_factory_with_ignored_prefix (self ):
279279 self ._table_factory_prefix_helper (use_prefix = False )
280280
281+ def test_create_table (self ):
282+ import operator
283+ from gcloud ._testing import _Monkey
284+ from gcloud .bigtable .happybase import connection as MUT
285+
286+ cluster = _Cluster () # Avoid implicit environ check.
287+ connection = self ._makeOne (autoconnect = False , cluster = cluster )
288+ mock_gc_rule = object ()
289+ called_options = []
290+
291+ def mock_parse_family_option (option ):
292+ called_options .append (option )
293+ return mock_gc_rule
294+
295+ name = 'table-name'
296+ col_fam1 = 'cf1'
297+ col_fam_option1 = object ()
298+ col_fam2 = u'cf2'
299+ col_fam_option2 = object ()
300+ col_fam3 = b'cf3'
301+ col_fam_option3 = object ()
302+ families = {
303+ col_fam1 : col_fam_option1 ,
304+ # A trailing colon is also allowed.
305+ col_fam2 + ':' : col_fam_option2 ,
306+ col_fam3 + b':' : col_fam_option3 ,
307+ }
308+
309+ tables_created = []
310+
311+ def make_table (* args , ** kwargs ):
312+ result = _MockLowLevelTable (* args , ** kwargs )
313+ tables_created .append (result )
314+ return result
315+
316+ with _Monkey (MUT , _LowLevelTable = make_table ,
317+ _parse_family_option = mock_parse_family_option ):
318+ connection .create_table (name , families )
319+
320+ # Just one table would have been created.
321+ table_instance , = tables_created
322+ self .assertEqual (table_instance .args , (name , cluster ))
323+ self .assertEqual (table_instance .kwargs , {})
324+ self .assertEqual (table_instance .create_calls , 1 )
325+
326+ # Check if our mock was called twice, but we don't know the order.
327+ self .assertEqual (
328+ set (called_options ),
329+ set ([col_fam_option1 , col_fam_option2 , col_fam_option3 ]))
330+
331+ # We expect three column family instances created, but don't know the
332+ # order due to non-deterministic dict.items().
333+ col_fam_created = table_instance .col_fam_created
334+ self .assertEqual (len (col_fam_created ), 3 )
335+ col_fam_created .sort (key = operator .attrgetter ('column_family_id' ))
336+ self .assertEqual (col_fam_created [0 ].column_family_id , col_fam1 )
337+ self .assertEqual (col_fam_created [0 ].gc_rule , mock_gc_rule )
338+ self .assertEqual (col_fam_created [0 ].create_calls , 1 )
339+ self .assertEqual (col_fam_created [1 ].column_family_id , col_fam2 )
340+ self .assertEqual (col_fam_created [1 ].gc_rule , mock_gc_rule )
341+ self .assertEqual (col_fam_created [1 ].create_calls , 1 )
342+ self .assertEqual (col_fam_created [2 ].column_family_id ,
343+ col_fam3 .decode ('utf-8' ))
344+ self .assertEqual (col_fam_created [2 ].gc_rule , mock_gc_rule )
345+ self .assertEqual (col_fam_created [2 ].create_calls , 1 )
346+
347+ def test_create_table_bad_type (self ):
348+ cluster = _Cluster () # Avoid implicit environ check.
349+ connection = self ._makeOne (autoconnect = False , cluster = cluster )
350+
351+ name = 'table-name'
352+ families = None
353+ with self .assertRaises (TypeError ):
354+ connection .create_table (name , families )
355+
356+ def test_create_table_bad_value (self ):
357+ cluster = _Cluster () # Avoid implicit environ check.
358+ connection = self ._makeOne (autoconnect = False , cluster = cluster )
359+
360+ name = 'table-name'
361+ families = {}
362+ with self .assertRaises (ValueError ):
363+ connection .create_table (name , families )
364+
365+
366+ class Test__parse_family_option (unittest2 .TestCase ):
367+
368+ def _callFUT (self , option ):
369+ from gcloud .bigtable .happybase .connection import _parse_family_option
370+ return _parse_family_option (option )
371+
372+ def test_dictionary_no_keys (self ):
373+ option = {}
374+ result = self ._callFUT (option )
375+ self .assertEqual (result , None )
376+
377+ def test_null (self ):
378+ option = None
379+ result = self ._callFUT (option )
380+ self .assertEqual (result , None )
381+
382+ def test_dictionary_bad_key (self ):
383+ option = {'badkey' : None }
384+ with self .assertRaises (ValueError ):
385+ self ._callFUT (option )
386+
387+ def test_dictionary_versions_key (self ):
388+ from gcloud .bigtable .column_family import MaxVersionsGCRule
389+
390+ versions = 42
391+ option = {'max_versions' : versions }
392+ result = self ._callFUT (option )
393+
394+ gc_rule = MaxVersionsGCRule (versions )
395+ self .assertEqual (result , gc_rule )
396+
397+ def test_dictionary_ttl_key (self ):
398+ import datetime
399+ from gcloud .bigtable .column_family import MaxAgeGCRule
400+
401+ time_to_live = 24 * 60 * 60
402+ max_age = datetime .timedelta (days = 1 )
403+ option = {'time_to_live' : time_to_live }
404+ result = self ._callFUT (option )
405+
406+ gc_rule = MaxAgeGCRule (max_age )
407+ self .assertEqual (result , gc_rule )
408+
409+ def test_dictionary_both_keys (self ):
410+ import datetime
411+ from gcloud .bigtable .column_family import GCRuleIntersection
412+ from gcloud .bigtable .column_family import MaxAgeGCRule
413+ from gcloud .bigtable .column_family import MaxVersionsGCRule
414+
415+ versions = 42
416+ time_to_live = 24 * 60 * 60
417+ option = {
418+ 'max_versions' : versions ,
419+ 'time_to_live' : time_to_live ,
420+ }
421+ result = self ._callFUT (option )
422+
423+ max_age = datetime .timedelta (days = 1 )
424+ # NOTE: This relies on the order of the rules in the method we are
425+ # calling matching this order here.
426+ gc_rule1 = MaxAgeGCRule (max_age )
427+ gc_rule2 = MaxVersionsGCRule (versions )
428+ gc_rule = GCRuleIntersection (rules = [gc_rule1 , gc_rule2 ])
429+ self .assertEqual (result , gc_rule )
430+
431+ def test_non_dictionary (self ):
432+ option = object ()
433+ self .assertFalse (isinstance (option , dict ))
434+ result = self ._callFUT (option )
435+ self .assertEqual (result , option )
436+
281437
282438class _Client (object ):
283439
@@ -316,3 +472,31 @@ def copy(self):
316472 return result
317473 else :
318474 return self
475+
476+
477+ class _MockLowLevelColumnFamily (object ):
478+
479+ def __init__ (self , column_family_id , gc_rule = None ):
480+ self .column_family_id = column_family_id
481+ self .gc_rule = gc_rule
482+ self .create_calls = 0
483+
484+ def create (self ):
485+ self .create_calls += 1
486+
487+
488+ class _MockLowLevelTable (object ):
489+
490+ def __init__ (self , * args , ** kwargs ):
491+ self .args = args
492+ self .kwargs = kwargs
493+ self .create_calls = 0
494+ self .col_fam_created = []
495+
496+ def create (self ):
497+ self .create_calls += 1
498+
499+ def column_family (self , column_family_id , gc_rule = None ):
500+ result = _MockLowLevelColumnFamily (column_family_id , gc_rule = gc_rule )
501+ self .col_fam_created .append (result )
502+ return result
0 commit comments