Files
deb-python-cassandra-driver/tests/integration/cqlengine/columns/test_container_columns.py
2016-03-04 11:17:29 -06:00

966 lines
34 KiB
Python

# Copyright 2013-2016 DataStax, Inc.
#
# 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.
from datetime import datetime, timedelta
import json
import logging
import six
import sys
import traceback
from uuid import uuid4
from cassandra import WriteTimeout
import cassandra.cqlengine.columns as columns
from cassandra.cqlengine.functions import get_total_seconds
from cassandra.cqlengine.models import Model, ValidationError
from cassandra.cqlengine.management import sync_table, drop_table
from tests.integration.cqlengine import is_prepend_reversed
from tests.integration.cqlengine.base import BaseCassEngTestCase
from tests.integration import greaterthancass20, CASSANDRA_VERSION
log = logging.getLogger(__name__)
class TestSetModel(Model):
partition = columns.UUID(primary_key=True, default=uuid4)
int_set = columns.Set(columns.Integer, required=False)
text_set = columns.Set(columns.Text, required=False)
class JsonTestColumn(columns.Column):
db_type = 'text'
def to_python(self, value):
if value is None:
return
if isinstance(value, six.string_types):
return json.loads(value)
else:
return value
def to_database(self, value):
if value is None:
return
return json.dumps(value)
class TestSetColumn(BaseCassEngTestCase):
@classmethod
def setUpClass(cls):
drop_table(TestSetModel)
sync_table(TestSetModel)
@classmethod
def tearDownClass(cls):
drop_table(TestSetModel)
def test_add_none_fails(self):
self.assertRaises(ValidationError, TestSetModel.create, **{'int_set': set([None])})
def test_empty_set_initial(self):
"""
tests that sets are set() by default, should never be none
:return:
"""
m = TestSetModel.create()
m.int_set.add(5)
m.save()
def test_deleting_last_item_should_succeed(self):
m = TestSetModel.create()
m.int_set.add(5)
m.save()
m.int_set.remove(5)
m.save()
m = TestSetModel.get(partition=m.partition)
self.assertTrue(5 not in m.int_set)
def test_blind_deleting_last_item_should_succeed(self):
m = TestSetModel.create()
m.int_set.add(5)
m.save()
TestSetModel.objects(partition=m.partition).update(int_set=set())
m = TestSetModel.get(partition=m.partition)
self.assertTrue(5 not in m.int_set)
def test_empty_set_retrieval(self):
m = TestSetModel.create()
m2 = TestSetModel.get(partition=m.partition)
m2.int_set.add(3)
def test_io_success(self):
""" Tests that a basic usage works as expected """
m1 = TestSetModel.create(int_set=set((1, 2)), text_set=set(('kai', 'andreas')))
m2 = TestSetModel.get(partition=m1.partition)
self.assertIsInstance(m2.int_set, set)
self.assertIsInstance(m2.text_set, set)
self.assertIn(1, m2.int_set)
self.assertIn(2, m2.int_set)
self.assertIn('kai', m2.text_set)
self.assertIn('andreas', m2.text_set)
def test_type_validation(self):
"""
Tests that attempting to use the wrong types will raise an exception
"""
self.assertRaises(ValidationError, TestSetModel.create, **{'int_set': set(('string', True)), 'text_set': set((1, 3.0))})
def test_element_count_validation(self):
"""
Tests that big collections are detected and raise an exception.
"""
while True:
try:
TestSetModel.create(text_set=set(str(uuid4()) for i in range(65535)))
break
except WriteTimeout:
ex_type, ex, tb = sys.exc_info()
log.warn("{0}: {1} Backtrace: {2}".format(ex_type.__name__, ex, traceback.extract_tb(tb)))
del tb
self.assertRaises(ValidationError, TestSetModel.create, **{'text_set': set(str(uuid4()) for i in range(65536))})
def test_partial_updates(self):
""" Tests that partial udpates work as expected """
m1 = TestSetModel.create(int_set=set((1, 2, 3, 4)))
m1.int_set.add(5)
m1.int_set.remove(1)
self.assertEqual(m1.int_set, set((2, 3, 4, 5)))
m1.save()
m2 = TestSetModel.get(partition=m1.partition)
self.assertEqual(m2.int_set, set((2, 3, 4, 5)))
def test_instantiation_with_column_class(self):
"""
Tests that columns instantiated with a column class work properly
and that the class is instantiated in the constructor
"""
column = columns.Set(columns.Text)
self.assertIsInstance(column.value_col, columns.Text)
def test_instantiation_with_column_instance(self):
"""
Tests that columns instantiated with a column instance work properly
"""
column = columns.Set(columns.Text(min_length=100))
self.assertIsInstance(column.value_col, columns.Text)
def test_to_python(self):
""" Tests that to_python of value column is called """
column = columns.Set(JsonTestColumn)
val = set((1, 2, 3))
db_val = column.to_database(val)
self.assertEqual(db_val, set(json.dumps(v) for v in val))
py_val = column.to_python(db_val)
self.assertEqual(py_val, val)
def test_default_empty_container_saving(self):
""" tests that the default empty container is not saved if it hasn't been updated """
pkey = uuid4()
# create a row with set data
TestSetModel.create(partition=pkey, int_set=set((3, 4)))
# create another with no set data
TestSetModel.create(partition=pkey)
m = TestSetModel.get(partition=pkey)
self.assertEqual(m.int_set, set((3, 4)))
class TestListModel(Model):
partition = columns.UUID(primary_key=True, default=uuid4)
int_list = columns.List(columns.Integer, required=False)
text_list = columns.List(columns.Text, required=False)
class TestListColumn(BaseCassEngTestCase):
@classmethod
def setUpClass(cls):
drop_table(TestListModel)
sync_table(TestListModel)
@classmethod
def tearDownClass(cls):
drop_table(TestListModel)
def test_initial(self):
tmp = TestListModel.create()
tmp.int_list.append(1)
def test_initial_retrieve(self):
tmp = TestListModel.create()
tmp2 = TestListModel.get(partition=tmp.partition)
tmp2.int_list.append(1)
def test_io_success(self):
""" Tests that a basic usage works as expected """
m1 = TestListModel.create(int_list=[1, 2], text_list=['kai', 'andreas'])
m2 = TestListModel.get(partition=m1.partition)
self.assertIsInstance(m2.int_list, list)
self.assertIsInstance(m2.text_list, list)
self.assertEqual(len(m2.int_list), 2)
self.assertEqual(len(m2.text_list), 2)
self.assertEqual(m2.int_list[0], 1)
self.assertEqual(m2.int_list[1], 2)
self.assertEqual(m2.text_list[0], 'kai')
self.assertEqual(m2.text_list[1], 'andreas')
def test_type_validation(self):
"""
Tests that attempting to use the wrong types will raise an exception
"""
self.assertRaises(ValidationError, TestListModel.create, **{'int_list': ['string', True], 'text_list': [1, 3.0]})
def test_element_count_validation(self):
"""
Tests that big collections are detected and raise an exception.
"""
while True:
try:
TestListModel.create(text_list=[str(uuid4()) for i in range(65535)])
break
except WriteTimeout:
ex_type, ex, tb = sys.exc_info()
log.warn("{0}: {1} Backtrace: {2}".format(ex_type.__name__, ex, traceback.extract_tb(tb)))
del tb
self.assertRaises(ValidationError, TestListModel.create, **{'text_list': [str(uuid4()) for _ in range(65536)]})
def test_partial_updates(self):
""" Tests that partial udpates work as expected """
full = list(range(10))
initial = full[3:7]
m1 = TestListModel.create(int_list=initial)
m1.int_list = full
m1.save()
if is_prepend_reversed():
expected = full[2::-1] + full[3:]
else:
expected = full
m2 = TestListModel.get(partition=m1.partition)
self.assertEqual(list(m2.int_list), expected)
def test_instantiation_with_column_class(self):
"""
Tests that columns instantiated with a column class work properly
and that the class is instantiated in the constructor
"""
column = columns.List(columns.Text)
self.assertIsInstance(column.value_col, columns.Text)
def test_instantiation_with_column_instance(self):
"""
Tests that columns instantiated with a column instance work properly
"""
column = columns.List(columns.Text(min_length=100))
self.assertIsInstance(column.value_col, columns.Text)
def test_to_python(self):
""" Tests that to_python of value column is called """
column = columns.List(JsonTestColumn)
val = [1, 2, 3]
db_val = column.to_database(val)
self.assertEqual(db_val, [json.dumps(v) for v in val])
py_val = column.to_python(db_val)
self.assertEqual(py_val, val)
def test_default_empty_container_saving(self):
""" tests that the default empty container is not saved if it hasn't been updated """
pkey = uuid4()
# create a row with list data
TestListModel.create(partition=pkey, int_list=[1, 2, 3, 4])
# create another with no list data
TestListModel.create(partition=pkey)
m = TestListModel.get(partition=pkey)
self.assertEqual(m.int_list, [1, 2, 3, 4])
def test_remove_entry_works(self):
pkey = uuid4()
tmp = TestListModel.create(partition=pkey, int_list=[1, 2])
tmp.int_list.pop()
tmp.update()
tmp = TestListModel.get(partition=pkey)
self.assertEqual(tmp.int_list, [1])
def test_update_from_non_empty_to_empty(self):
pkey = uuid4()
tmp = TestListModel.create(partition=pkey, int_list=[1, 2])
tmp.int_list = []
tmp.update()
tmp = TestListModel.get(partition=pkey)
self.assertEqual(tmp.int_list, [])
def test_insert_none(self):
pkey = uuid4()
self.assertRaises(ValidationError, TestListModel.create, **{'partition': pkey, 'int_list': [None]})
def test_blind_list_updates_from_none(self):
""" Tests that updates from None work as expected """
m = TestListModel.create(int_list=None)
expected = [1, 2]
m.int_list = expected
m.save()
m2 = TestListModel.get(partition=m.partition)
self.assertEqual(m2.int_list, expected)
TestListModel.objects(partition=m.partition).update(int_list=[])
m3 = TestListModel.get(partition=m.partition)
self.assertEqual(m3.int_list, [])
class TestMapModel(Model):
partition = columns.UUID(primary_key=True, default=uuid4)
int_map = columns.Map(columns.Integer, columns.UUID, required=False)
text_map = columns.Map(columns.Text, columns.DateTime, required=False)
class TestMapColumn(BaseCassEngTestCase):
@classmethod
def setUpClass(cls):
drop_table(TestMapModel)
sync_table(TestMapModel)
@classmethod
def tearDownClass(cls):
drop_table(TestMapModel)
def test_empty_default(self):
tmp = TestMapModel.create()
tmp.int_map['blah'] = 1
def test_add_none_as_map_key(self):
self.assertRaises(ValidationError, TestMapModel.create, **{'int_map': {None: uuid4()}})
def test_empty_retrieve(self):
tmp = TestMapModel.create()
tmp2 = TestMapModel.get(partition=tmp.partition)
tmp2.int_map['blah'] = 1
def test_remove_last_entry_works(self):
tmp = TestMapModel.create()
tmp.text_map["blah"] = datetime.now()
tmp.save()
del tmp.text_map["blah"]
tmp.save()
tmp = TestMapModel.get(partition=tmp.partition)
self.assertTrue("blah" not in tmp.int_map)
def test_io_success(self):
""" Tests that a basic usage works as expected """
k1 = uuid4()
k2 = uuid4()
now = datetime.now()
then = now + timedelta(days=1)
m1 = TestMapModel.create(int_map={1: k1, 2: k2},
text_map={'now': now, 'then': then})
m2 = TestMapModel.get(partition=m1.partition)
self.assertTrue(isinstance(m2.int_map, dict))
self.assertTrue(isinstance(m2.text_map, dict))
self.assertTrue(1 in m2.int_map)
self.assertTrue(2 in m2.int_map)
self.assertEqual(m2.int_map[1], k1)
self.assertEqual(m2.int_map[2], k2)
self.assertAlmostEqual(get_total_seconds(now - m2.text_map['now']), 0, 2)
self.assertAlmostEqual(get_total_seconds(then - m2.text_map['then']), 0, 2)
def test_type_validation(self):
"""
Tests that attempting to use the wrong types will raise an exception
"""
self.assertRaises(ValidationError, TestMapModel.create, **{'int_map': {'key': 2, uuid4(): 'val'}, 'text_map': {2: 5}})
def test_element_count_validation(self):
"""
Tests that big collections are detected and raise an exception.
"""
while True:
try:
TestMapModel.create(text_map=dict((str(uuid4()), i) for i in range(65535)))
break
except WriteTimeout:
ex_type, ex, tb = sys.exc_info()
log.warn("{0}: {1} Backtrace: {2}".format(ex_type.__name__, ex, traceback.extract_tb(tb)))
del tb
self.assertRaises(ValidationError, TestMapModel.create, **{'text_map': dict((str(uuid4()), i) for i in range(65536))})
def test_partial_updates(self):
""" Tests that partial udpates work as expected """
now = datetime.now()
# derez it a bit
now = datetime(*now.timetuple()[:-3])
early = now - timedelta(minutes=30)
earlier = early - timedelta(minutes=30)
later = now + timedelta(minutes=30)
initial = {'now': now, 'early': earlier}
final = {'later': later, 'early': early}
m1 = TestMapModel.create(text_map=initial)
m1.text_map = final
m1.save()
m2 = TestMapModel.get(partition=m1.partition)
self.assertEqual(m2.text_map, final)
def test_updates_from_none(self):
""" Tests that updates from None work as expected """
m = TestMapModel.create(int_map=None)
expected = {1: uuid4()}
m.int_map = expected
m.save()
m2 = TestMapModel.get(partition=m.partition)
self.assertEqual(m2.int_map, expected)
m2.int_map = None
m2.save()
m3 = TestMapModel.get(partition=m.partition)
self.assertNotEqual(m3.int_map, expected)
def test_blind_updates_from_none(self):
""" Tests that updates from None work as expected """
m = TestMapModel.create(int_map=None)
expected = {1: uuid4()}
m.int_map = expected
m.save()
m2 = TestMapModel.get(partition=m.partition)
self.assertEqual(m2.int_map, expected)
TestMapModel.objects(partition=m.partition).update(int_map={})
m3 = TestMapModel.get(partition=m.partition)
self.assertNotEqual(m3.int_map, expected)
def test_updates_to_none(self):
""" Tests that setting the field to None works as expected """
m = TestMapModel.create(int_map={1: uuid4()})
m.int_map = None
m.save()
m2 = TestMapModel.get(partition=m.partition)
self.assertEqual(m2.int_map, {})
def test_instantiation_with_column_class(self):
"""
Tests that columns instantiated with a column class work properly
and that the class is instantiated in the constructor
"""
column = columns.Map(columns.Text, columns.Integer)
self.assertIsInstance(column.key_col, columns.Text)
self.assertIsInstance(column.value_col, columns.Integer)
def test_instantiation_with_column_instance(self):
"""
Tests that columns instantiated with a column instance work properly
"""
column = columns.Map(columns.Text(min_length=100), columns.Integer())
self.assertIsInstance(column.key_col, columns.Text)
self.assertIsInstance(column.value_col, columns.Integer)
def test_to_python(self):
""" Tests that to_python of value column is called """
column = columns.Map(JsonTestColumn, JsonTestColumn)
val = {1: 2, 3: 4, 5: 6}
db_val = column.to_database(val)
self.assertEqual(db_val, dict((json.dumps(k), json.dumps(v)) for k, v in val.items()))
py_val = column.to_python(db_val)
self.assertEqual(py_val, val)
def test_default_empty_container_saving(self):
""" tests that the default empty container is not saved if it hasn't been updated """
pkey = uuid4()
tmap = {1: uuid4(), 2: uuid4()}
# create a row with set data
TestMapModel.create(partition=pkey, int_map=tmap)
# create another with no set data
TestMapModel.create(partition=pkey)
m = TestMapModel.get(partition=pkey)
self.assertEqual(m.int_map, tmap)
class TestCamelMapModel(Model):
partition = columns.UUID(primary_key=True, default=uuid4)
camelMap = columns.Map(columns.Text, columns.Integer, required=False)
class TestCamelMapColumn(BaseCassEngTestCase):
@classmethod
def setUpClass(cls):
drop_table(TestCamelMapModel)
sync_table(TestCamelMapModel)
@classmethod
def tearDownClass(cls):
drop_table(TestCamelMapModel)
def test_camelcase_column(self):
TestCamelMapModel.create(camelMap={'blah': 1})
class TestTupleModel(Model):
partition = columns.UUID(primary_key=True, default=uuid4)
int_tuple = columns.Tuple(columns.Integer, columns.Integer, columns.Integer, required=False)
text_tuple = columns.Tuple(columns.Text, columns.Text, required=False)
mixed_tuple = columns.Tuple(columns.Text, columns.Integer, columns.Text, required=False)
@greaterthancass20
class TestTupleColumn(BaseCassEngTestCase):
@classmethod
def setUpClass(cls):
# Skip annotations don't seem to skip class level teradown and setup methods
if(CASSANDRA_VERSION >= '2.1'):
drop_table(TestTupleModel)
sync_table(TestTupleModel)
@classmethod
def tearDownClass(cls):
drop_table(TestTupleModel)
def test_initial(self):
"""
Tests creation and insertion of tuple types with models
@since 3.1
@jira_ticket PYTHON-306
@expected_result Model is successfully crated
@test_category object_mapper
"""
tmp = TestTupleModel.create()
tmp.int_tuple = (1, 2, 3)
def test_initial_retrieve(self):
"""
Tests creation and insertion of tuple types with models,
and their retrieval.
@since 3.1
@jira_ticket PYTHON-306
@expected_result Model is successfully crated
@test_category object_mapper
"""
tmp = TestTupleModel.create()
tmp2 = tmp.get(partition=tmp.partition)
tmp2.int_tuple = (1, 2, 3)
def test_io_success(self):
"""
Tests creation and insertion of various types with models,
and their retrieval.
@since 3.1
@jira_ticket PYTHON-306
@expected_result Model is successfully created and fetched correctly
@test_category object_mapper
"""
m1 = TestTupleModel.create(int_tuple=(1, 2, 3, 5, 6), text_tuple=('kai', 'andreas'), mixed_tuple=('first', 2, 'Third'))
m2 = TestTupleModel.get(partition=m1.partition)
self.assertIsInstance(m2.int_tuple, tuple)
self.assertIsInstance(m2.text_tuple, tuple)
self.assertIsInstance(m2.mixed_tuple, tuple)
self.assertEqual((1, 2, 3), m2.int_tuple)
self.assertEqual(('kai', 'andreas'), m2.text_tuple)
self.assertEqual(('first', 2, 'Third'), m2.mixed_tuple)
def test_type_validation(self):
"""
Tests to make sure tuple type validation occurs
@since 3.1
@jira_ticket PYTHON-306
@expected_result validation errors are raised
@test_category object_mapper
"""
self.assertRaises(ValidationError, TestTupleModel.create, **{'int_tuple': ('string', True), 'text_tuple': ('test', 'test'), 'mixed_tuple': ('one', 2, 'three')})
self.assertRaises(ValidationError, TestTupleModel.create, **{'int_tuple': ('string', 'string'), 'text_tuple': (1, 3.0), 'mixed_tuple': ('one', 2, 'three')})
self.assertRaises(ValidationError, TestTupleModel.create, **{'int_tuple': ('string', 'string'), 'text_tuple': ('test', 'test'), 'mixed_tuple': (1, "two", 3)})
def test_instantiation_with_column_class(self):
"""
Tests that columns instantiated with a column class work properly
and that the class is instantiated in the constructor
@since 3.1
@jira_ticket PYTHON-306
@expected_result types are instantiated correctly
@test_category object_mapper
"""
mixed_tuple = columns.Tuple(columns.Text, columns.Integer, columns.Text, required=False)
self.assertIsInstance(mixed_tuple.types[0], columns.Text)
self.assertIsInstance(mixed_tuple.types[1], columns.Integer)
self.assertIsInstance(mixed_tuple.types[2], columns.Text)
self.assertEqual(len(mixed_tuple.types), 3)
def test_default_empty_container_saving(self):
"""
Tests that the default empty container is not saved if it hasn't been updated
@since 3.1
@jira_ticket PYTHON-306
@expected_result empty tuple is not upserted
@test_category object_mapper
"""
pkey = uuid4()
# create a row with tuple data
TestTupleModel.create(partition=pkey, int_tuple=(1, 2, 3))
# create another with no tuple data
TestTupleModel.create(partition=pkey)
m = TestTupleModel.get(partition=pkey)
self.assertEqual(m.int_tuple, (1, 2, 3))
def test_updates(self):
"""
Tests that updates can be preformed on tuple containers
@since 3.1
@jira_ticket PYTHON-306
@expected_result tuple is replaced
@test_category object_mapper
"""
initial = (1, 2)
replacement = (1, 2, 3)
m1 = TestTupleModel.create(int_tuple=initial)
m1.int_tuple = replacement
m1.save()
m2 = TestTupleModel.get(partition=m1.partition)
self.assertEqual(tuple(m2.int_tuple), replacement)
def test_update_from_non_empty_to_empty(self):
"""
Tests that explcit none updates are processed correctly on tuples
@since 3.1
@jira_ticket PYTHON-306
@expected_result tuple is replaced with none
@test_category object_mapper
"""
pkey = uuid4()
tmp = TestTupleModel.create(partition=pkey, int_tuple=(1, 2, 3))
tmp.int_tuple = (None)
tmp.update()
tmp = TestTupleModel.get(partition=pkey)
self.assertEqual(tmp.int_tuple, (None))
def test_insert_none(self):
"""
Tests that Tuples can be created as none
@since 3.1
@jira_ticket PYTHON-306
@expected_result tuple is created as none
@test_category object_mapper
"""
pkey = uuid4()
tmp = TestTupleModel.create(partition=pkey, int_tuple=(None))
self.assertEqual((None), tmp.int_tuple)
def test_blind_tuple_updates_from_none(self):
"""
Tests that Tuples can be updated from none
@since 3.1
@jira_ticket PYTHON-306
@expected_result tuple is created as none, but upserted to contain values
@test_category object_mapper
"""
m = TestTupleModel.create(int_tuple=None)
expected = (1, 2, 3)
m.int_tuple = expected
m.save()
m2 = TestTupleModel.get(partition=m.partition)
self.assertEqual(m2.int_tuple, expected)
TestTupleModel.objects(partition=m.partition).update(int_tuple=None)
m3 = TestTupleModel.get(partition=m.partition)
self.assertEqual(m3.int_tuple, None)
class TestNestedModel(Model):
partition = columns.UUID(primary_key=True, default=uuid4)
list_list = columns.List(columns.List(columns.Integer), required=False)
map_list = columns.Map(columns.Text, columns.List(columns.Text), required=False)
set_tuple = columns.Set(columns.Tuple(columns.Integer, columns.Integer), required=False)
@greaterthancass20
class TestNestedType(BaseCassEngTestCase):
@classmethod
def setUpClass(cls):
# Skip annotations don't seem to skip class level teradown and setup methods
if(CASSANDRA_VERSION >= '2.1'):
drop_table(TestNestedModel)
sync_table(TestNestedModel)
@classmethod
def tearDownClass(cls):
drop_table(TestNestedModel)
def test_initial(self):
"""
Tests creation and insertion of nested collection types with models
@since 3.1
@jira_ticket PYTHON-478
@expected_result Model is successfully created
@test_category object_mapper
"""
tmp = TestNestedModel.create()
tmp.list_list = [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
def test_initial_retrieve(self):
"""
Tests creation and insertion of nested collection types with models,
and their retrieval.
@since 3.1
@jira_ticket PYTHON-478
@expected_result Model is successfully crated
@test_category object_mapper
"""
tmp = TestNestedModel.create()
tmp2 = tmp.get(partition=tmp.partition)
tmp2.list_list = [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
tmp2.map_list = {'key1': ["text1", "text2", "text3"], "key2": ["text1", "text2", "text3"], "key3": ["text1", "text2", "text3"]}
tmp2.set_tuple = set(((1, 2), (3, 5), (4, 5)))
def test_io_success(self):
"""
Tests creation and insertion of various nested collection types with models,
and their retrieval.
@since 3.1
@jira_ticket PYTHON-378
@expected_result Model is successfully created and fetched correctly
@test_category object_mapper
"""
list_list_master = [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
map_list_master = {'key1': ["text1", "text2", "text3"], "key2": ["text1", "text2", "text3"], "key3": ["text1", "text2", "text3"]}
set_tuple_master = set(((1, 2), (3, 5), (4, 5)))
m1 = TestNestedModel.create(list_list=list_list_master, map_list=map_list_master, set_tuple=set_tuple_master)
m2 = TestNestedModel.get(partition=m1.partition)
self.assertIsInstance(m2.list_list, list)
self.assertIsInstance(m2.list_list[0], list)
self.assertIsInstance(m2.map_list, dict)
self.assertIsInstance(m2.map_list.get("key2"), list)
self.assertEqual(list_list_master, m2.list_list)
self.assertEqual(map_list_master, m2.map_list)
self.assertEqual(set_tuple_master, m2.set_tuple)
self.assertIsInstance(m2.set_tuple.pop(), tuple)
def test_type_validation(self):
"""
Tests to make sure nested collection type validation occurs
@since 3.1
@jira_ticket PYTHON-478
@expected_result validation errors are raised
@test_category object_mapper
"""
list_list_bad_list_context = [['text', "text", "text"], ["text", "text", "text"], ["text", "text", "text"]]
list_list_no_list = ['text', "text", "text"]
map_list_bad_value = {'key1': [1, 2, 3], "key2": [1, 2, 3], "key3": [1, 2, 3]}
map_list_bad_key = {1: ["text1", "text2", "text3"], 2: ["text1", "text2", "text3"], 3: ["text1", "text2", "text3"]}
set_tuple_bad_tuple_value = set((("text", "text"), ("text", "text"), ("text", "text")))
set_tuple_not_set = ['This', 'is', 'not', 'a', 'set']
self.assertRaises(ValidationError, TestNestedModel.create, **{'list_list': list_list_bad_list_context})
self.assertRaises(ValidationError, TestNestedModel.create, **{'list_list': list_list_no_list})
self.assertRaises(ValidationError, TestNestedModel.create, **{'map_list': map_list_bad_value})
self.assertRaises(ValidationError, TestNestedModel.create, **{'map_list': map_list_bad_key})
self.assertRaises(ValidationError, TestNestedModel.create, **{'set_tuple': set_tuple_bad_tuple_value})
self.assertRaises(ValidationError, TestNestedModel.create, **{'set_tuple': set_tuple_not_set})
def test_instantiation_with_column_class(self):
"""
Tests that columns instantiated with a column class work properly
and that the class is instantiated in the constructor
@since 3.1
@jira_ticket PYTHON-478
@expected_result types are instantiated correctly
@test_category object_mapper
"""
list_list = columns.List(columns.List(columns.Integer), required=False)
map_list = columns.Map(columns.Text, columns.List(columns.Text), required=False)
set_tuple = columns.Set(columns.Tuple(columns.Integer, columns.Integer), required=False)
self.assertIsInstance(list_list, columns.List)
self.assertIsInstance(list_list.types[0], columns.List)
self.assertIsInstance(map_list.types[0], columns.Text)
self.assertIsInstance(map_list.types[1], columns.List)
self.assertIsInstance(set_tuple.types[0], columns.Tuple)
def test_default_empty_container_saving(self):
"""
Tests that the default empty nested collection container is not saved if it hasn't been updated
@since 3.1
@jira_ticket PYTHON-478
@expected_result empty nested collection is not upserted
@test_category object_mapper
"""
pkey = uuid4()
# create a row with tuple data
list_list_master = [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
map_list_master = {'key1': ["text1", "text2", "text3"], "key2": ["text1", "text2", "text3"], "key3": ["text1", "text2", "text3"]}
set_tuple_master = set(((1, 2), (3, 5), (4, 5)))
TestNestedModel.create(partition=pkey, list_list=list_list_master, map_list=map_list_master, set_tuple=set_tuple_master)
# create another with no tuple data
TestNestedModel.create(partition=pkey)
m = TestNestedModel.get(partition=pkey)
self.assertEqual(m.list_list, list_list_master)
self.assertEqual(m.map_list, map_list_master)
self.assertEqual(m.set_tuple, set_tuple_master)
def test_updates(self):
"""
Tests that updates can be preformed on nested collections
@since 3.1
@jira_ticket PYTHON-478
@expected_result nested collection is replaced
@test_category object_mapper
"""
list_list_initial = [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
list_list_replacement = [[1, 2, 3], [3, 4, 5]]
set_tuple_initial = set(((1, 2), (3, 5), (4, 5)))
map_list_initial = {'key1': ["text1", "text2", "text3"], "key2": ["text1", "text2", "text3"], "key3": ["text1", "text2", "text3"]}
map_list_replacement = {'key1': ["text1", "text2", "text3"], "key3": ["text1", "text2", "text3"]}
set_tuple_replacement = set(((7, 7), (7, 7), (4, 5)))
m1 = TestNestedModel.create(list_list=list_list_initial, map_list=map_list_initial, set_tuple=set_tuple_initial)
m1.list_list = list_list_replacement
m1.map_list = map_list_replacement
m1.set_tuple = set_tuple_replacement
m1.save()
m2 = TestNestedModel.get(partition=m1.partition)
self.assertEqual(m2.list_list, list_list_replacement)
self.assertEqual(m2.map_list, map_list_replacement)
self.assertEqual(m2.set_tuple, set_tuple_replacement)
def test_update_from_non_empty_to_empty(self):
"""
Tests that explcit none updates are processed correctly on tuples
@since 3.1
@jira_ticket PYTHON-478
@expected_result nested collection is replaced with none
@test_category object_mapper
"""
list_list_initial = [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
map_list_initial = {'key1': ["text1", "text2", "text3"], "key2": ["text1", "text2", "text3"], "key3": ["text1", "text2", "text3"]}
set_tuple_initial = set(((1, 2), (3, 5), (4, 5)))
tmp = TestNestedModel.create(list_list=list_list_initial, map_list=map_list_initial, set_tuple=set_tuple_initial)
tmp.list_list = []
tmp.map_list = {}
tmp.set_tuple = set()
tmp.update()
tmp = TestNestedModel.get(partition=tmp.partition)
self.assertEqual(tmp.list_list, [])
self.assertEqual(tmp.map_list, {})
self.assertEqual(tmp.set_tuple, set())
def test_insert_none(self):
"""
Tests that Tuples can be created as none
@since 3.1
@jira_ticket PYTHON-478
@expected_result nested collection is created as none
@test_category object_mapper
"""
pkey = uuid4()
tmp = TestNestedModel.create(partition=pkey, list_list=(None), map_list=(None), set_tuple=(None))
self.assertEqual([], tmp.list_list)
self.assertEqual({}, tmp.map_list)
self.assertEqual(set(), tmp.set_tuple)