From 47cb3e90154a46a34cc43b288ce0d727d15373bf Mon Sep 17 00:00:00 2001 From: GregBestland Date: Mon, 11 Apr 2016 16:49:00 -0500 Subject: [PATCH] Adding decorator for counting underlying query execution, updating some tests to add expected query counts --- tests/integration/cqlengine/__init__.py | 62 +++++++++++++++++++ .../cqlengine/query/test_batch_query.py | 12 ++++ .../cqlengine/query/test_datetime_queries.py | 15 +++-- .../integration/cqlengine/query/test_named.py | 12 +++- .../cqlengine/query/test_queryoperators.py | 3 + .../cqlengine/query/test_queryset.py | 55 +++++++++++++++- .../cqlengine/query/test_updates.py | 16 ++++- 7 files changed, 162 insertions(+), 13 deletions(-) diff --git a/tests/integration/cqlengine/__init__.py b/tests/integration/cqlengine/__init__.py index c54dd95a..381839b8 100644 --- a/tests/integration/cqlengine/__init__.py +++ b/tests/integration/cqlengine/__init__.py @@ -14,15 +14,23 @@ import os import warnings +try: + import unittest2 as unittest +except ImportError: + import unittest # noqa from cassandra import ConsistencyLevel from cassandra.cqlengine import connection from cassandra.cqlengine.management import create_keyspace_simple, CQLENG_ALLOW_SCHEMA_MANAGEMENT +import cassandra from tests.integration import get_server_versions, use_single_node, PROTOCOL_VERSION DEFAULT_KEYSPACE = 'cqlengine_test' +CQL_SKIP_EXECUTE = bool(os.getenv('CQL_SKIP_EXECUTE', False)) + + def setup_package(): warnings.simplefilter('always') # for testing warnings, make sure all are let through os.environ[CQLENG_ALLOW_SCHEMA_MANAGEMENT] = '1' @@ -44,3 +52,57 @@ def setup_connection(keyspace_name): consistency=ConsistencyLevel.ONE, protocol_version=PROTOCOL_VERSION, default_keyspace=keyspace_name) + + +class StatementCounter(object): + """ + Simple python object used to hold a count of the number of times + the wrapped method has been invoked + """ + def __init__(self, patched_func): + self.func = patched_func + self.counter = 0 + + def wrapped_execute(self, *args, **kwargs): + self.counter += 1 + return self.func(*args, **kwargs) + + def get_counter(self): + return self.counter + + +def execute_count(expected): + """ + A decorator used wrap cassandra.cqlengine.connection.execute. It counts the number of times this method is invoked + then compares it to the number expected. If they don't match it throws an assertion error. + This function can be disabled by running the test harness with the env variable CQL_SKIP_EXECUTE=1 set + """ + def innerCounter(fn): + def wrapped_function(*args, **kwargs): + # Create a counter monkey patch into cassandra.cqlengine.connection.execute + count = StatementCounter(cassandra.cqlengine.connection.execute) + original_function = cassandra.cqlengine.connection.execute + # Monkey patch in our StatementCounter wrapper + cassandra.cqlengine.connection.execute = count.wrapped_execute + # Invoked the underlying unit test + to_return = fn(*args, **kwargs) + # Get the count from our monkey patched counter + count.get_counter() + # DeMonkey Patch our code + cassandra.cqlengine.connection.execute = original_function + # Check to see if the count is what you expect + tc = unittest.TestCase("__init__") + tc.assertEquals(count.get_counter(), expected, "Expected number of cassandra.cqlengine.connection.execute calls doesn't match actual number invoked Expected: {0}, Invoked {1}".format(count.get_counter(), expected)) + return to_return + # Name of the wrapped function must match the original or unittest will error out. + wrapped_function.__name__ = fn.__name__ + wrapped_function.__doc__ = fn.__doc__ + # Escape hatch + if(CQL_SKIP_EXECUTE): + return fn + else: + return wrapped_function + + return innerCounter + + diff --git a/tests/integration/cqlengine/query/test_batch_query.py b/tests/integration/cqlengine/query/test_batch_query.py index 126ad21e..dafad04a 100644 --- a/tests/integration/cqlengine/query/test_batch_query.py +++ b/tests/integration/cqlengine/query/test_batch_query.py @@ -20,6 +20,7 @@ from cassandra.cqlengine.management import drop_table, sync_table from cassandra.cqlengine.models import Model from cassandra.cqlengine.query import BatchQuery, DMLQuery from tests.integration.cqlengine.base import BaseCassEngTestCase +from tests.integration.cqlengine import execute_count from cassandra.cluster import Session @@ -55,6 +56,7 @@ class BatchQueryTests(BaseCassEngTestCase): for obj in TestMultiKeyModel.filter(partition=self.pkey): obj.delete() + @execute_count(3) def test_insert_success_case(self): b = BatchQuery() @@ -67,6 +69,7 @@ class BatchQueryTests(BaseCassEngTestCase): TestMultiKeyModel.get(partition=self.pkey, cluster=2) + @execute_count(4) def test_update_success_case(self): inst = TestMultiKeyModel.create(partition=self.pkey, cluster=2, count=3, text='4') @@ -84,6 +87,7 @@ class BatchQueryTests(BaseCassEngTestCase): inst3 = TestMultiKeyModel.get(partition=self.pkey, cluster=2) self.assertEqual(inst3.count, 4) + @execute_count(4) def test_delete_success_case(self): inst = TestMultiKeyModel.create(partition=self.pkey, cluster=2, count=3, text='4') @@ -99,6 +103,7 @@ class BatchQueryTests(BaseCassEngTestCase): with self.assertRaises(TestMultiKeyModel.DoesNotExist): TestMultiKeyModel.get(partition=self.pkey, cluster=2) + @execute_count(11) def test_context_manager(self): with BatchQuery() as b: @@ -112,6 +117,7 @@ class BatchQueryTests(BaseCassEngTestCase): for i in range(5): TestMultiKeyModel.get(partition=self.pkey, cluster=i) + @execute_count(9) def test_bulk_delete_success_case(self): for i in range(1): @@ -127,6 +133,7 @@ class BatchQueryTests(BaseCassEngTestCase): for m in TestMultiKeyModel.all(): m.delete() + @execute_count(0) def test_none_success_case(self): """ Tests that passing None into the batch call clears any batch object """ b = BatchQuery() @@ -137,6 +144,7 @@ class BatchQueryTests(BaseCassEngTestCase): q = q.batch(None) self.assertIsNone(q._batch) + @execute_count(0) def test_dml_none_success_case(self): """ Tests that passing None into the batch call clears any batch object """ b = BatchQuery() @@ -147,6 +155,7 @@ class BatchQueryTests(BaseCassEngTestCase): q.batch(None) self.assertIsNone(q._batch) + @execute_count(3) def test_batch_execute_on_exception_succeeds(self): # makes sure if execute_on_exception == True we still apply the batch drop_table(BatchQueryLogModel) @@ -166,6 +175,7 @@ class BatchQueryTests(BaseCassEngTestCase): # should be 1 because the batch should execute self.assertEqual(1, len(obj)) + @execute_count(2) def test_batch_execute_on_exception_skips_if_not_specified(self): # makes sure if execute_on_exception == True we still apply the batch drop_table(BatchQueryLogModel) @@ -186,12 +196,14 @@ class BatchQueryTests(BaseCassEngTestCase): # should be 0 because the batch should not execute self.assertEqual(0, len(obj)) + @execute_count(1) def test_batch_execute_timeout(self): with mock.patch.object(Session, 'execute') as mock_execute: with BatchQuery(timeout=1) as b: BatchQueryLogModel.batch(b).create(k=2, v=2) self.assertEqual(mock_execute.call_args[-1]['timeout'], 1) + @execute_count(1) def test_batch_execute_no_timeout(self): with mock.patch.object(Session, 'execute') as mock_execute: with BatchQuery() as b: diff --git a/tests/integration/cqlengine/query/test_datetime_queries.py b/tests/integration/cqlengine/query/test_datetime_queries.py index 118d74dd..813dcad0 100644 --- a/tests/integration/cqlengine/query/test_datetime_queries.py +++ b/tests/integration/cqlengine/query/test_datetime_queries.py @@ -20,15 +20,17 @@ from tests.integration.cqlengine.base import BaseCassEngTestCase from cassandra.cqlengine.management import sync_table from cassandra.cqlengine.management import drop_table -from cassandra.cqlengine.models import Model, ModelException +from cassandra.cqlengine.models import Model from cassandra.cqlengine import columns -from cassandra.cqlengine import query +from tests.integration.cqlengine import execute_count + class DateTimeQueryTestModel(Model): - user = columns.Integer(primary_key=True) - day = columns.DateTime(primary_key=True) - data = columns.Text() + user = columns.Integer(primary_key=True) + day = columns.DateTime(primary_key=True) + data = columns.Text() + class TestDateTimeQueries(BaseCassEngTestCase): @@ -46,12 +48,12 @@ class TestDateTimeQueries(BaseCassEngTestCase): data=str(uuid4()) ) - @classmethod def tearDownClass(cls): super(TestDateTimeQueries, cls).tearDownClass() drop_table(DateTimeQueryTestModel) + @execute_count(1) def test_range_query(self): """ Tests that loading from a range of dates works properly """ start = datetime(*self.base_date.timetuple()[:3]) @@ -60,6 +62,7 @@ class TestDateTimeQueries(BaseCassEngTestCase): results = DateTimeQueryTestModel.filter(user=0, day__gte=start, day__lt=end) assert len(results) == 3 + @execute_count(3) def test_datetime_precision(self): """ Tests that millisecond resolution is preserved when saving datetime objects """ now = datetime.now() diff --git a/tests/integration/cqlengine/query/test_named.py b/tests/integration/cqlengine/query/test_named.py index 3b51d1d2..9cddbece 100644 --- a/tests/integration/cqlengine/query/test_named.py +++ b/tests/integration/cqlengine/query/test_named.py @@ -25,7 +25,7 @@ from cassandra.cqlengine.query import ResultObject from cassandra.concurrent import execute_concurrent_with_args from cassandra.cqlengine import models -from tests.integration.cqlengine import setup_connection +from tests.integration.cqlengine import setup_connection, execute_count from tests.integration.cqlengine.base import BaseCassEngTestCase from tests.integration.cqlengine.query.test_queryset import BaseQuerySetUsage @@ -134,6 +134,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): cls.keyspace = NamedKeyspace(ks) cls.table = cls.keyspace.table(tn) + @execute_count(2) def test_count(self): """ Tests that adding filtering statements affects the count query as expected """ assert self.table.objects.count() == 12 @@ -141,6 +142,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): q = self.table.objects(test_id=0) assert q.count() == 4 + @execute_count(2) def test_query_expression_count(self): """ Tests that adding query statements affects the count query as expected """ assert self.table.objects.count() == 12 @@ -148,6 +150,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): q = self.table.objects(self.table.column('test_id') == 0) assert q.count() == 4 + @execute_count(3) def test_iteration(self): """ Tests that iterating over a query set pulls back all of the expected results """ q = self.table.objects(test_id=0) @@ -181,6 +184,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): compare_set.remove(val) assert len(compare_set) == 0 + @execute_count(2) def test_multiple_iterations_work_properly(self): """ Tests that iterating over a query set more than once works """ # test with both the filtering method and the query method @@ -201,6 +205,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): compare_set.remove(val) assert len(compare_set) == 0 + @execute_count(2) def test_multiple_iterators_are_isolated(self): """ tests that the use of one iterator does not affect the behavior of another @@ -214,6 +219,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): assert next(iter1).attempt_id == attempt_id assert next(iter2).attempt_id == attempt_id + @execute_count(3) def test_get_success_case(self): """ Tests that the .get() method works on new and existing querysets @@ -235,6 +241,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): assert m.test_id == 0 assert m.attempt_id == 0 + @execute_count(3) def test_query_expression_get_success_case(self): """ Tests that the .get() method works on new and existing querysets @@ -256,6 +263,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): assert m.test_id == 0 assert m.attempt_id == 0 + @execute_count(1) def test_get_doesnotexist_exception(self): """ Tests that get calls that don't return a result raises a DoesNotExist error @@ -263,6 +271,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): with self.assertRaises(self.table.DoesNotExist): self.table.objects.get(test_id=100) + @execute_count(1) def test_get_multipleobjects_exception(self): """ Tests that get calls that return multiple results raise a MultipleObjectsReturned error @@ -286,6 +295,7 @@ class TestNamedWithMV(BasicSharedKeyspaceUnitTestCase): super(TestNamedWithMV, cls).tearDownClass() @greaterthanorequalcass30 + @execute_count(5) def test_named_table_with_mv(self): """ Test NamedTable access to materialized views diff --git a/tests/integration/cqlengine/query/test_queryoperators.py b/tests/integration/cqlengine/query/test_queryoperators.py index ae19f249..c2a2a742 100644 --- a/tests/integration/cqlengine/query/test_queryoperators.py +++ b/tests/integration/cqlengine/query/test_queryoperators.py @@ -24,6 +24,7 @@ from cassandra.cqlengine.operators import EqualsOperator from cassandra.cqlengine.statements import WhereClause from tests.integration.cqlengine import DEFAULT_KEYSPACE from tests.integration.cqlengine.base import BaseCassEngTestCase +from tests.integration.cqlengine import execute_count class TestQuerySetOperation(BaseCassEngTestCase): @@ -71,6 +72,7 @@ class TestTokenFunction(BaseCassEngTestCase): super(TestTokenFunction, self).tearDown() drop_table(TokenTestModel) + @execute_count(14) def test_token_function(self): """ Tests that token functions work properly """ assert TokenTestModel.objects().count() == 0 @@ -127,6 +129,7 @@ class TestTokenFunction(BaseCassEngTestCase): func = functions.Token('a') self.assertRaises(query.QueryException, TestModel.objects.filter, pk__token__gt=func) + @execute_count(7) def test_named_table_pk_token_function(self): """ Test to ensure that token function work with named tables. diff --git a/tests/integration/cqlengine/query/test_queryset.py b/tests/integration/cqlengine/query/test_queryset.py index 9cc8aa34..9d01cd56 100644 --- a/tests/integration/cqlengine/query/test_queryset.py +++ b/tests/integration/cqlengine/query/test_queryset.py @@ -40,9 +40,9 @@ from datetime import tzinfo from cassandra.cqlengine import statements from cassandra.cqlengine import operators from cassandra.util import uuid_from_time - from cassandra.cqlengine.connection import get_session from tests.integration import PROTOCOL_VERSION, CASSANDRA_VERSION, greaterthancass20, greaterthancass21 +from tests.integration.cqlengine import execute_count class TzOffset(tzinfo): @@ -105,6 +105,7 @@ class TestMultiClusteringModel(Model): class TestQuerySetOperation(BaseCassEngTestCase): + def test_query_filter_parsing(self): """ Tests the queryset filter method parses it's kwargs properly @@ -229,6 +230,7 @@ class TestQuerySetOperation(BaseCassEngTestCase): class BaseQuerySetUsage(BaseCassEngTestCase): + @classmethod def setUpClass(cls): super(BaseQuerySetUsage, cls).setUpClass() @@ -298,6 +300,8 @@ class BaseQuerySetUsage(BaseCassEngTestCase): class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): + + @execute_count(2) def test_count(self): """ Tests that adding filtering statements affects the count query as expected """ assert TestModel.objects.count() == 12 @@ -305,6 +309,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): q = TestModel.objects(test_id=0) assert q.count() == 4 + @execute_count(2) def test_query_expression_count(self): """ Tests that adding query statements affects the count query as expected """ assert TestModel.objects.count() == 12 @@ -312,6 +317,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): q = TestModel.objects(TestModel.test_id == 0) assert q.count() == 4 + @execute_count(3) def test_iteration(self): """ Tests that iterating over a query set pulls back all of the expected results """ q = TestModel.objects(test_id=0) @@ -345,6 +351,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): compare_set.remove(val) assert len(compare_set) == 0 + @execute_count(2) def test_multiple_iterations_work_properly(self): """ Tests that iterating over a query set more than once works """ # test with both the filtering method and the query method @@ -365,6 +372,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): compare_set.remove(val) assert len(compare_set) == 0 + @execute_count(2) def test_multiple_iterators_are_isolated(self): """ tests that the use of one iterator does not affect the behavior of another @@ -378,6 +386,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): assert next(iter1).attempt_id == attempt_id assert next(iter2).attempt_id == attempt_id + @execute_count(3) def test_get_success_case(self): """ Tests that the .get() method works on new and existing querysets @@ -399,6 +408,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): assert m.test_id == 0 assert m.attempt_id == 0 + @execute_count(3) def test_query_expression_get_success_case(self): """ Tests that the .get() method works on new and existing querysets @@ -420,6 +430,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): assert m.test_id == 0 assert m.attempt_id == 0 + @execute_count(1) def test_get_doesnotexist_exception(self): """ Tests that get calls that don't return a result raises a DoesNotExist error @@ -427,6 +438,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): with self.assertRaises(TestModel.DoesNotExist): TestModel.objects.get(test_id=100) + @execute_count(1) def test_get_multipleobjects_exception(self): """ Tests that get calls that return multiple results raise a MultipleObjectsReturned error @@ -438,7 +450,7 @@ class TestQuerySetCountSelectionAndIteration(BaseQuerySetUsage): """ """ - +@execute_count(4) def test_non_quality_filtering(): class NonEqualityFilteringModel(Model): @@ -463,28 +475,34 @@ def test_non_quality_filtering(): class TestQuerySetDistinct(BaseQuerySetUsage): + @execute_count(1) def test_distinct_without_parameter(self): q = TestModel.objects.distinct() self.assertEqual(len(q), 3) + @execute_count(1) def test_distinct_with_parameter(self): q = TestModel.objects.distinct(['test_id']) self.assertEqual(len(q), 3) + @execute_count(1) def test_distinct_with_filter(self): q = TestModel.objects.distinct(['test_id']).filter(test_id__in=[1, 2]) self.assertEqual(len(q), 2) + @execute_count(1) def test_distinct_with_non_partition(self): with self.assertRaises(InvalidRequest): q = TestModel.objects.distinct(['description']).filter(test_id__in=[1, 2]) len(q) + @execute_count(1) def test_zero_result(self): q = TestModel.objects.distinct(['test_id']).filter(test_id__in=[52]) self.assertEqual(len(q), 0) @greaterthancass21 + @execute_count(2) def test_distinct_with_explicit_count(self): q = TestModel.objects.distinct(['test_id']) self.assertEqual(q.count(), 3) @@ -494,7 +512,7 @@ class TestQuerySetDistinct(BaseQuerySetUsage): class TestQuerySetOrdering(BaseQuerySetUsage): - + @execute_count(2) def test_order_by_success_case(self): q = TestModel.objects(test_id=0).order_by('attempt_id') expected_order = [0, 1, 2, 3] @@ -523,6 +541,7 @@ class TestQuerySetOrdering(BaseQuerySetUsage): with self.assertRaises(query.QueryException): IndexedTestModel.objects(test_id=0).order_by('attempt_id') + @execute_count(8) def test_ordering_on_multiple_clustering_columns(self): TestMultiClusteringModel.create(one=1, two=1, three=4) TestMultiClusteringModel.create(one=1, two=1, three=2) @@ -541,23 +560,28 @@ class TestQuerySetOrdering(BaseQuerySetUsage): class TestQuerySetSlicing(BaseQuerySetUsage): + + @execute_count(1) def test_out_of_range_index_raises_error(self): q = TestModel.objects(test_id=0).order_by('attempt_id') with self.assertRaises(IndexError): q[10] + @execute_count(1) def test_array_indexing_works_properly(self): q = TestModel.objects(test_id=0).order_by('attempt_id') expected_order = [0, 1, 2, 3] for i in range(len(q)): assert q[i].attempt_id == expected_order[i] + @execute_count(1) def test_negative_indexing_works_properly(self): q = TestModel.objects(test_id=0).order_by('attempt_id') expected_order = [0, 1, 2, 3] assert q[-1].attempt_id == expected_order[-1] assert q[-2].attempt_id == expected_order[-2] + @execute_count(1) def test_slicing_works_properly(self): q = TestModel.objects(test_id=0).order_by('attempt_id') expected_order = [0, 1, 2, 3] @@ -568,6 +592,7 @@ class TestQuerySetSlicing(BaseQuerySetUsage): for model, expect in zip(q[0:3:2], expected_order[0:3:2]): self.assertEqual(model.attempt_id, expect) + @execute_count(1) def test_negative_slicing(self): q = TestModel.objects(test_id=0).order_by('attempt_id') expected_order = [0, 1, 2, 3] @@ -589,6 +614,7 @@ class TestQuerySetSlicing(BaseQuerySetUsage): class TestQuerySetValidation(BaseQuerySetUsage): + def test_primary_key_or_index_must_be_specified(self): """ Tests that queries that don't have an equals relation to a primary key or indexed field fail @@ -606,6 +632,7 @@ class TestQuerySetValidation(BaseQuerySetUsage): list([i for i in q]) @greaterthancass20 + @execute_count(7) def test_indexed_field_can_be_queried(self): """ Tests that queries on an indexed field will work without any primary key relations specified @@ -633,6 +660,8 @@ class TestQuerySetValidation(BaseQuerySetUsage): class TestQuerySetDelete(BaseQuerySetUsage): + + @execute_count(9) def test_delete(self): TestModel.objects.create(test_id=3, attempt_id=0, description='try9', expected_result=50, test_result=40) TestModel.objects.create(test_id=3, attempt_id=1, description='try10', expected_result=60, test_result=40) @@ -658,6 +687,7 @@ class TestQuerySetDelete(BaseQuerySetUsage): TestModel.objects(attempt_id=0).delete() @unittest.skipIf(CASSANDRA_VERSION < '3.0', "range deletion was introduce in C* 3.0, currently running {0}".format(CASSANDRA_VERSION)) + @execute_count(18) def test_range_deletion(self): """ Tests that range deletion work as expected @@ -697,6 +727,7 @@ class TestMinMaxTimeUUIDFunctions(BaseCassEngTestCase): super(TestMinMaxTimeUUIDFunctions, cls).tearDownClass() drop_table(TimeUUIDQueryModel) + @execute_count(7) def test_tzaware_datetime_support(self): """Test that using timezone aware datetime instances works with the MinTimeUUID/MaxTimeUUID functions. @@ -740,6 +771,7 @@ class TestMinMaxTimeUUIDFunctions(BaseCassEngTestCase): TimeUUIDQueryModel.partition == pk, TimeUUIDQueryModel.time >= functions.MinTimeUUID(midpoint_helsinki))] + @execute_count(8) def test_success_case(self): """ Test that the min and max time uuid functions work as expected """ pk = uuid4() @@ -790,11 +822,14 @@ class TestMinMaxTimeUUIDFunctions(BaseCassEngTestCase): class TestInOperator(BaseQuerySetUsage): + + @execute_count(1) def test_kwarg_success_case(self): """ Tests the in operator works with the kwarg query method """ q = TestModel.filter(test_id__in=[0, 1]) assert q.count() == 8 + @execute_count(1) def test_query_expression_success_case(self): """ Tests the in operator works with the query expression query method """ q = TestModel.filter(TestModel.test_id.in_([0, 1])) @@ -803,6 +838,8 @@ class TestInOperator(BaseQuerySetUsage): @greaterthancass20 class TestContainsOperator(BaseQuerySetUsage): + + @execute_count(6) def test_kwarg_success_case(self): """ Tests the CONTAINS operator works with the kwarg query method """ q = IndexedCollectionsTestModel.filter(test_list__contains=1) @@ -833,6 +870,7 @@ class TestContainsOperator(BaseQuerySetUsage): q = IndexedCollectionsTestModel.filter(test_map_no_index__contains=1) self.assertEqual(q.count(), 0) + @execute_count(6) def test_query_expression_success_case(self): """ Tests the CONTAINS operator works with the query expression query method """ q = IndexedCollectionsTestModel.filter(IndexedCollectionsTestModel.test_list.contains_(1)) @@ -865,6 +903,8 @@ class TestContainsOperator(BaseQuerySetUsage): class TestValuesList(BaseQuerySetUsage): + + @execute_count(2) def test_values_list(self): q = TestModel.objects.filter(test_id=0, attempt_id=1) item = q.values_list('test_id', 'attempt_id', 'description', 'expected_result', 'test_result').first() @@ -875,6 +915,7 @@ class TestValuesList(BaseQuerySetUsage): class TestObjectsProperty(BaseQuerySetUsage): + @execute_count(1) def test_objects_property_returns_fresh_queryset(self): assert TestModel.objects._result_cache is None len(TestModel.objects) # evaluate queryset @@ -882,6 +923,7 @@ class TestObjectsProperty(BaseQuerySetUsage): class PageQueryTests(BaseCassEngTestCase): + @execute_count(3) def test_paged_result_handling(self): if PROTOCOL_VERSION < 2: raise unittest.SkipTest("Paging requires native protocol 2+, currently using: {0}".format(PROTOCOL_VERSION)) @@ -989,6 +1031,7 @@ class TestModelQueryWithDBField(BaseCassEngTestCase): for model in cls.model_list: drop_table(model) + @execute_count(33) def test_basic_crud(self): """ Tests creation update and delete of object model queries that are using db_field mappings. @@ -1025,6 +1068,7 @@ class TestModelQueryWithDBField(BaseCassEngTestCase): i = model.objects(k0=i.k0, k1=i.k1).first() self.assertIsNone(i) + @execute_count(21) def test_slice(self): """ Tests slice queries for object models that are using db_field mapping @@ -1047,6 +1091,7 @@ class TestModelQueryWithDBField(BaseCassEngTestCase): self.assertEqual(model.objects(k0=i.k0, k1=i.k1, c0__lt=i.c0).count(), len(clustering_values[:-1])) self.assertEqual(model.objects(k0=i.k0, k1=i.k1, c0__gt=0).count(), len(clustering_values[1:])) + @execute_count(15) def test_order(self): """ Tests order by queries for object models that are using db_field mapping @@ -1066,6 +1111,7 @@ class TestModelQueryWithDBField(BaseCassEngTestCase): self.assertEqual(model.objects(k0=i.k0, k1=i.k1).order_by('c0').first().c0, clustering_values[0]) self.assertEqual(model.objects(k0=i.k0, k1=i.k1).order_by('-c0').first().c0, clustering_values[-1]) + @execute_count(15) def test_index(self): """ Tests queries using index fields for object models using db_field mapping @@ -1086,6 +1132,7 @@ class TestModelQueryWithDBField(BaseCassEngTestCase): self.assertEqual(model.objects(k0=i.k0, k1=i.k1).count(), len(clustering_values)) self.assertEqual(model.objects(k0=i.k0, k1=i.k1, v1=0).count(), 1) + @execute_count(1) def test_db_field_names_used(self): """ Tests to ensure that with generated cql update statements correctly utilize the db_field values. @@ -1145,6 +1192,7 @@ class TestModelQueryWithFetchSize(BaseCassEngTestCase): super(TestModelQueryWithFetchSize, cls).tearDownClass() drop_table(TestModelSmall) + @execute_count(9) def test_defaultFetchSize(self): with BatchQuery() as b: for i in range(5100): @@ -1202,6 +1250,7 @@ class TestModelQueryWithDifferedFeld(BaseCassEngTestCase): super(TestModelQueryWithDifferedFeld, cls).tearDownClass() drop_table(People) + @execute_count(8) def test_defaultFetchSize(self): # Populate Table People.objects.create(last_name="Smith", first_name="John", birthday=datetime.now()) diff --git a/tests/integration/cqlengine/query/test_updates.py b/tests/integration/cqlengine/query/test_updates.py index afa3a7f0..a3244d21 100644 --- a/tests/integration/cqlengine/query/test_updates.py +++ b/tests/integration/cqlengine/query/test_updates.py @@ -20,6 +20,7 @@ from cassandra.cqlengine.management import sync_table, drop_table from cassandra.cqlengine import columns from tests.integration.cqlengine import is_prepend_reversed from tests.integration.cqlengine.base import BaseCassEngTestCase +from tests.integration.cqlengine import execute_count class TestQueryUpdateModel(Model): @@ -45,6 +46,7 @@ class QueryUpdateTests(BaseCassEngTestCase): super(QueryUpdateTests, cls).tearDownClass() drop_table(TestQueryUpdateModel) + @execute_count(8) def test_update_values(self): """ tests calling udpate on a queryset """ partition = uuid4() @@ -65,6 +67,7 @@ class QueryUpdateTests(BaseCassEngTestCase): self.assertEqual(row.count, 6 if i == 3 else i) self.assertEqual(row.text, str(i)) + @execute_count(6) def test_update_values_validation(self): """ tests calling udpate on models with values passed in """ partition = uuid4() @@ -91,6 +94,7 @@ class QueryUpdateTests(BaseCassEngTestCase): with self.assertRaises(ValidationError): TestQueryUpdateModel.objects(partition=uuid4(), cluster=3).update(cluster=5000) + @execute_count(8) def test_null_update_deletes_column(self): """ setting a field to null in the update should issue a delete statement """ partition = uuid4() @@ -111,6 +115,7 @@ class QueryUpdateTests(BaseCassEngTestCase): self.assertEqual(row.count, i) self.assertEqual(row.text, None if i == 3 else str(i)) + @execute_count(9) def test_mixed_value_and_null_update(self): """ tests that updating a columns value, and removing another works properly """ partition = uuid4() @@ -131,9 +136,7 @@ class QueryUpdateTests(BaseCassEngTestCase): self.assertEqual(row.count, 6 if i == 3 else i) self.assertEqual(row.text, None if i == 3 else str(i)) - def test_counter_updates(self): - pass - + @execute_count(3) def test_set_add_updates(self): partition = uuid4() cluster = 1 @@ -144,6 +147,7 @@ class QueryUpdateTests(BaseCassEngTestCase): obj = TestQueryUpdateModel.objects.get(partition=partition, cluster=cluster) self.assertEqual(obj.text_set, set(("foo", "bar"))) + @execute_count(2) def test_set_add_updates_new_record(self): """ If the key doesn't exist yet, an update creates the record """ @@ -154,6 +158,7 @@ class QueryUpdateTests(BaseCassEngTestCase): obj = TestQueryUpdateModel.objects.get(partition=partition, cluster=cluster) self.assertEqual(obj.text_set, set(("bar",))) + @execute_count(3) def test_set_remove_updates(self): partition = uuid4() cluster = 1 @@ -165,6 +170,7 @@ class QueryUpdateTests(BaseCassEngTestCase): obj = TestQueryUpdateModel.objects.get(partition=partition, cluster=cluster) self.assertEqual(obj.text_set, set(("baz",))) + @execute_count(3) def test_set_remove_new_record(self): """ Removing something not in the set should silently do nothing """ @@ -178,6 +184,7 @@ class QueryUpdateTests(BaseCassEngTestCase): obj = TestQueryUpdateModel.objects.get(partition=partition, cluster=cluster) self.assertEqual(obj.text_set, set(("foo",))) + @execute_count(3) def test_list_append_updates(self): partition = uuid4() cluster = 1 @@ -189,6 +196,7 @@ class QueryUpdateTests(BaseCassEngTestCase): obj = TestQueryUpdateModel.objects.get(partition=partition, cluster=cluster) self.assertEqual(obj.text_list, ["foo", "bar"]) + @execute_count(3) def test_list_prepend_updates(self): """ Prepend two things since order is reversed by default by CQL """ partition = uuid4() @@ -204,6 +212,7 @@ class QueryUpdateTests(BaseCassEngTestCase): expected = (prepended[::-1] if is_prepend_reversed() else prepended) + original self.assertEqual(obj.text_list, expected) + @execute_count(3) def test_map_update_updates(self): """ Merge a dictionary into existing value """ partition = uuid4() @@ -217,6 +226,7 @@ class QueryUpdateTests(BaseCassEngTestCase): obj = TestQueryUpdateModel.objects.get(partition=partition, cluster=cluster) self.assertEqual(obj.text_map, {"foo": '1', "bar": '3', "baz": '4'}) + @execute_count(3) def test_map_update_none_deletes_key(self): """ The CQL behavior is if you set a key in a map to null it deletes that key from the map. Test that this works with __update.