@@ -311,6 +311,90 @@ def test_tables_with_prefix(self):
311311 result = connection .tables ()
312312 self .assertEqual (result , [unprefixed_table_name1 ])
313313
314+ def test_create_table (self ):
315+ import operator
316+ from gcloud ._testing import _Monkey
317+ from gcloud .bigtable .happybase import connection as MUT
318+
319+ cluster = _Cluster () # Avoid implicit environ check.
320+ connection = self ._makeOne (autoconnect = False , cluster = cluster )
321+ mock_gc_rule = object ()
322+ called_options = []
323+
324+ def mock_parse_family_option (option ):
325+ called_options .append (option )
326+ return mock_gc_rule
327+
328+ name = 'table-name'
329+ col_fam1 = 'cf1'
330+ col_fam_option1 = object ()
331+ col_fam2 = u'cf2'
332+ col_fam_option2 = object ()
333+ col_fam3 = b'cf3'
334+ col_fam_option3 = object ()
335+ families = {
336+ col_fam1 : col_fam_option1 ,
337+ # A trailing colon is also allowed.
338+ col_fam2 + ':' : col_fam_option2 ,
339+ col_fam3 + b':' : col_fam_option3 ,
340+ }
341+
342+ tables_created = []
343+
344+ def make_table (* args , ** kwargs ):
345+ result = _MockLowLevelTable (* args , ** kwargs )
346+ tables_created .append (result )
347+ return result
348+
349+ with _Monkey (MUT , _LowLevelTable = make_table ,
350+ _parse_family_option = mock_parse_family_option ):
351+ connection .create_table (name , families )
352+
353+ # Just one table would have been created.
354+ table_instance , = tables_created
355+ self .assertEqual (table_instance .args , (name , cluster ))
356+ self .assertEqual (table_instance .kwargs , {})
357+ self .assertEqual (table_instance .create_calls , 1 )
358+
359+ # Check if our mock was called twice, but we don't know the order.
360+ self .assertEqual (
361+ set (called_options ),
362+ set ([col_fam_option1 , col_fam_option2 , col_fam_option3 ]))
363+
364+ # We expect three column family instances created, but don't know the
365+ # order due to non-deterministic dict.items().
366+ col_fam_created = table_instance .col_fam_created
367+ self .assertEqual (len (col_fam_created ), 3 )
368+ col_fam_created .sort (key = operator .attrgetter ('column_family_id' ))
369+ self .assertEqual (col_fam_created [0 ].column_family_id , col_fam1 )
370+ self .assertEqual (col_fam_created [0 ].gc_rule , mock_gc_rule )
371+ self .assertEqual (col_fam_created [0 ].create_calls , 1 )
372+ self .assertEqual (col_fam_created [1 ].column_family_id , col_fam2 )
373+ self .assertEqual (col_fam_created [1 ].gc_rule , mock_gc_rule )
374+ self .assertEqual (col_fam_created [1 ].create_calls , 1 )
375+ self .assertEqual (col_fam_created [2 ].column_family_id ,
376+ col_fam3 .decode ('utf-8' ))
377+ self .assertEqual (col_fam_created [2 ].gc_rule , mock_gc_rule )
378+ self .assertEqual (col_fam_created [2 ].create_calls , 1 )
379+
380+ def test_create_table_bad_type (self ):
381+ cluster = _Cluster () # Avoid implicit environ check.
382+ connection = self ._makeOne (autoconnect = False , cluster = cluster )
383+
384+ name = 'table-name'
385+ families = None
386+ with self .assertRaises (TypeError ):
387+ connection .create_table (name , families )
388+
389+ def test_create_table_bad_value (self ):
390+ cluster = _Cluster () # Avoid implicit environ check.
391+ connection = self ._makeOne (autoconnect = False , cluster = cluster )
392+
393+ name = 'table-name'
394+ families = {}
395+ with self .assertRaises (ValueError ):
396+ connection .create_table (name , families )
397+
314398 def test_enable_table (self ):
315399 cluster = _Cluster () # Avoid implicit environ check.
316400 connection = self ._makeOne (autoconnect = False , cluster = cluster )
@@ -345,6 +429,78 @@ def test_compact_table(self):
345429 connection .compact_table (name , major = major )
346430
347431
432+ class Test__parse_family_option (unittest2 .TestCase ):
433+
434+ def _callFUT (self , option ):
435+ from gcloud .bigtable .happybase .connection import _parse_family_option
436+ return _parse_family_option (option )
437+
438+ def test_dictionary_no_keys (self ):
439+ option = {}
440+ result = self ._callFUT (option )
441+ self .assertEqual (result , None )
442+
443+ def test_null (self ):
444+ option = None
445+ result = self ._callFUT (option )
446+ self .assertEqual (result , None )
447+
448+ def test_dictionary_bad_key (self ):
449+ option = {'badkey' : None }
450+ with self .assertRaises (ValueError ):
451+ self ._callFUT (option )
452+
453+ def test_dictionary_versions_key (self ):
454+ from gcloud .bigtable .column_family import MaxVersionsGCRule
455+
456+ versions = 42
457+ option = {'max_versions' : versions }
458+ result = self ._callFUT (option )
459+
460+ gc_rule = MaxVersionsGCRule (versions )
461+ self .assertEqual (result , gc_rule )
462+
463+ def test_dictionary_ttl_key (self ):
464+ import datetime
465+ from gcloud .bigtable .column_family import MaxAgeGCRule
466+
467+ time_to_live = 24 * 60 * 60
468+ max_age = datetime .timedelta (days = 1 )
469+ option = {'time_to_live' : time_to_live }
470+ result = self ._callFUT (option )
471+
472+ gc_rule = MaxAgeGCRule (max_age )
473+ self .assertEqual (result , gc_rule )
474+
475+ def test_dictionary_both_keys (self ):
476+ import datetime
477+ from gcloud .bigtable .column_family import GCRuleIntersection
478+ from gcloud .bigtable .column_family import MaxAgeGCRule
479+ from gcloud .bigtable .column_family import MaxVersionsGCRule
480+
481+ versions = 42
482+ time_to_live = 24 * 60 * 60
483+ option = {
484+ 'max_versions' : versions ,
485+ 'time_to_live' : time_to_live ,
486+ }
487+ result = self ._callFUT (option )
488+
489+ max_age = datetime .timedelta (days = 1 )
490+ # NOTE: This relies on the order of the rules in the method we are
491+ # calling matching this order here.
492+ gc_rule1 = MaxAgeGCRule (max_age )
493+ gc_rule2 = MaxVersionsGCRule (versions )
494+ gc_rule = GCRuleIntersection (rules = [gc_rule1 , gc_rule2 ])
495+ self .assertEqual (result , gc_rule )
496+
497+ def test_non_dictionary (self ):
498+ option = object ()
499+ self .assertFalse (isinstance (option , dict ))
500+ result = self ._callFUT (option )
501+ self .assertEqual (result , option )
502+
503+
348504class _Client (object ):
349505
350506 def __init__ (self , * args , ** kwargs ):
@@ -385,3 +541,31 @@ def copy(self):
385541
386542 def list_tables (self ):
387543 return self .list_tables_result
544+
545+
546+ class _MockLowLevelColumnFamily (object ):
547+
548+ def __init__ (self , column_family_id , gc_rule = None ):
549+ self .column_family_id = column_family_id
550+ self .gc_rule = gc_rule
551+ self .create_calls = 0
552+
553+ def create (self ):
554+ self .create_calls += 1
555+
556+
557+ class _MockLowLevelTable (object ):
558+
559+ def __init__ (self , * args , ** kwargs ):
560+ self .args = args
561+ self .kwargs = kwargs
562+ self .create_calls = 0
563+ self .col_fam_created = []
564+
565+ def create (self ):
566+ self .create_calls += 1
567+
568+ def column_family (self , column_family_id , gc_rule = None ):
569+ result = _MockLowLevelColumnFamily (column_family_id , gc_rule = gc_rule )
570+ self .col_fam_created .append (result )
571+ return result
0 commit comments