# 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. try: import unittest2 as unittest except ImportError: import unittest # noqa import mock from uuid import uuid4 from cassandra.cqlengine import columns from cassandra.cqlengine.management import sync_table, drop_table from cassandra.cqlengine.models import Model from cassandra.cqlengine.query import BatchQuery, BatchType, LWTException, IfExistsWithCounterColumn from tests.integration.cqlengine.base import BaseCassEngTestCase from tests.integration import PROTOCOL_VERSION class TestIfExistsModel(Model): id = columns.UUID(primary_key=True, default=lambda: uuid4()) count = columns.Integer() text = columns.Text(required=False) class TestIfExistsModel2(Model): id = columns.Integer(primary_key=True) count = columns.Integer(primary_key=True, required=False) text = columns.Text(required=False) class TestIfExistsWithCounterModel(Model): id = columns.UUID(primary_key=True, default=lambda: uuid4()) likes = columns.Counter() class BaseIfExistsTest(BaseCassEngTestCase): @classmethod def setUpClass(cls): super(BaseIfExistsTest, cls).setUpClass() sync_table(TestIfExistsModel) sync_table(TestIfExistsModel2) @classmethod def tearDownClass(cls): super(BaseIfExistsTest, cls).tearDownClass() drop_table(TestIfExistsModel) drop_table(TestIfExistsModel2) class BaseIfExistsWithCounterTest(BaseCassEngTestCase): @classmethod def setUpClass(cls): super(BaseIfExistsWithCounterTest, cls).setUpClass() sync_table(TestIfExistsWithCounterModel) @classmethod def tearDownClass(cls): super(BaseIfExistsWithCounterTest, cls).tearDownClass() drop_table(TestIfExistsWithCounterModel) class IfExistsUpdateTests(BaseIfExistsTest): @unittest.skipUnless(PROTOCOL_VERSION >= 2, "only runs against the cql3 protocol v2.0") def test_update_if_exists(self): """ Tests that update with if_exists work as expected @since 3.1 @jira_ticket PYTHON-432 @expected_result updates to be applied when primary key exists, otherwise LWT exception to be thrown @test_category object_mapper """ id = uuid4() m = TestIfExistsModel.create(id=id, count=8, text='123456789') m.text = 'changed' m.if_exists().update() m = TestIfExistsModel.get(id=id) self.assertEqual(m.text, 'changed') # save() m.text = 'changed_again' m.if_exists().save() m = TestIfExistsModel.get(id=id) self.assertEqual(m.text, 'changed_again') m = TestIfExistsModel(id=uuid4(), count=44) # do not exists with self.assertRaises(LWTException) as assertion: m.if_exists().update() self.assertEqual(assertion.exception.existing, { '[applied]': False, }) # queryset update with self.assertRaises(LWTException) as assertion: TestIfExistsModel.objects(id=uuid4()).if_exists().update(count=8) self.assertEqual(assertion.exception.existing, { '[applied]': False, }) @unittest.skipUnless(PROTOCOL_VERSION >= 2, "only runs against the cql3 protocol v2.0") def test_batch_update_if_exists_success(self): """ Tests that batch update with if_exists work as expected @since 3.1 @jira_ticket PYTHON-432 @expected_result @test_category object_mapper """ id = uuid4() m = TestIfExistsModel.create(id=id, count=8, text='123456789') with BatchQuery() as b: m.text = '111111111' m.batch(b).if_exists().update() with self.assertRaises(LWTException) as assertion: with BatchQuery() as b: m = TestIfExistsModel(id=uuid4(), count=42) # Doesn't exist m.batch(b).if_exists().update() self.assertEqual(assertion.exception.existing, { '[applied]': False, }) q = TestIfExistsModel.objects(id=id) self.assertEqual(len(q), 1) tm = q.first() self.assertEqual(tm.count, 8) self.assertEqual(tm.text, '111111111') @unittest.skipUnless(PROTOCOL_VERSION >= 2, "only runs against the cql3 protocol v2.0") def test_batch_mixed_update_if_exists_success(self): """ Tests that batch update with with one bad query will still fail with LWTException @since 3.1 @jira_ticket PYTHON-432 @expected_result @test_category object_mapper """ m = TestIfExistsModel2.create(id=1, count=8, text='123456789') with self.assertRaises(LWTException) as assertion: with BatchQuery() as b: m.text = '111111112' m.batch(b).if_exists().update() # Does exist n = TestIfExistsModel2(id=1, count=10, text="Failure") # Doesn't exist n.batch(b).if_exists().update() self.assertEqual(assertion.exception.existing.get('[applied]'), False) @unittest.skipUnless(PROTOCOL_VERSION >= 2, "only runs against the cql3 protocol v2.0") def test_delete_if_exists(self): """ Tests that delete with if_exists work, and throw proper LWT exception when they are are not applied @since 3.1 @jira_ticket PYTHON-432 @expected_result Deletes will be preformed if they exist, otherwise throw LWT exception @test_category object_mapper """ id = uuid4() m = TestIfExistsModel.create(id=id, count=8, text='123456789') m.if_exists().delete() q = TestIfExistsModel.objects(id=id) self.assertEqual(len(q), 0) m = TestIfExistsModel(id=uuid4(), count=44) # do not exists with self.assertRaises(LWTException) as assertion: m.if_exists().delete() self.assertEqual(assertion.exception.existing, { '[applied]': False, }) # queryset delete with self.assertRaises(LWTException) as assertion: TestIfExistsModel.objects(id=uuid4()).if_exists().delete() self.assertEqual(assertion.exception.existing, { '[applied]': False, }) @unittest.skipUnless(PROTOCOL_VERSION >= 2, "only runs against the cql3 protocol v2.0") def test_batch_delete_if_exists_success(self): """ Tests that batch deletes with if_exists work, and throw proper LWTException when they are are not applied @since 3.1 @jira_ticket PYTHON-432 @expected_result Deletes will be preformed if they exist, otherwise throw LWTException @test_category object_mapper """ id = uuid4() m = TestIfExistsModel.create(id=id, count=8, text='123456789') with BatchQuery() as b: m.batch(b).if_exists().delete() q = TestIfExistsModel.objects(id=id) self.assertEqual(len(q), 0) with self.assertRaises(LWTException) as assertion: with BatchQuery() as b: m = TestIfExistsModel(id=uuid4(), count=42) # Doesn't exist m.batch(b).if_exists().delete() self.assertEqual(assertion.exception.existing, { '[applied]': False, }) @unittest.skipUnless(PROTOCOL_VERSION >= 2, "only runs against the cql3 protocol v2.0") def test_batch_delete_mixed(self): """ Tests that batch deletes with multiple queries and throw proper LWTException when they are are not all applicable @since 3.1 @jira_ticket PYTHON-432 @expected_result If one delete clause doesn't exist all should fail. @test_category object_mapper """ m = TestIfExistsModel2.create(id=3, count=8, text='123456789') with self.assertRaises(LWTException) as assertion: with BatchQuery() as b: m.batch(b).if_exists().delete() # Does exist n = TestIfExistsModel2(id=3, count=42, text='1111111') # Doesn't exist n.batch(b).if_exists().delete() self.assertEqual(assertion.exception.existing.get('[applied]'), False) q = TestIfExistsModel2.objects(id=3, count=8) self.assertEqual(len(q), 1) class IfExistsQueryTest(BaseIfExistsTest): def test_if_exists_included_on_queryset_update(self): with mock.patch.object(self.session, 'execute') as m: TestIfExistsModel.objects(id=uuid4()).if_exists().update(count=42) query = m.call_args[0][0].query_string self.assertIn("IF EXISTS", query) def test_if_exists_included_on_update(self): """ tests that if_exists on models update works as expected """ with mock.patch.object(self.session, 'execute') as m: TestIfExistsModel(id=uuid4()).if_exists().update(count=8) query = m.call_args[0][0].query_string self.assertIn("IF EXISTS", query) def test_if_exists_included_on_delete(self): """ tests that if_exists on models delete works as expected """ with mock.patch.object(self.session, 'execute') as m: TestIfExistsModel(id=uuid4()).if_exists().delete() query = m.call_args[0][0].query_string self.assertIn("IF EXISTS", query) class IfExistWithCounterTest(BaseIfExistsWithCounterTest): def test_instance_raise_exception(self): """ Tests if exists is used with a counter column model that exception are thrown @since 3.1 @jira_ticket PYTHON-432 @expected_result Deletes will be preformed if they exist, otherwise throw LWTException @test_category object_mapper """ id = uuid4() with self.assertRaises(IfExistsWithCounterColumn): TestIfExistsWithCounterModel.if_exists()