966 lines
34 KiB
Python
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)
|
|
|
|
|