Conflicts: cassandra/__init__.py cassandra/cluster.py cassandra/connection.py cassandra/metadata.py cassandra/protocol.py tests/integration/__init__.py tests/integration/standard/test_metadata.py tests/unit/test_control_connection.py
367 lines
15 KiB
Python
367 lines
15 KiB
Python
# Copyright 2013-2015 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
|
|
|
|
from mock import Mock
|
|
import os
|
|
import random
|
|
import six
|
|
|
|
import cassandra
|
|
from cassandra.cqltypes import IntegerType, AsciiType, TupleType
|
|
from cassandra.metadata import (Murmur3Token, MD5Token,
|
|
BytesToken, ReplicationStrategy,
|
|
NetworkTopologyStrategy, SimpleStrategy,
|
|
LocalStrategy, NoMurmur3, protect_name,
|
|
protect_names, protect_value, is_valid_name,
|
|
UserType, KeyspaceMetadata, get_schema_parser,
|
|
_UnknownStrategy)
|
|
from cassandra.policies import SimpleConvictionPolicy
|
|
from cassandra.pool import Host
|
|
|
|
|
|
class StrategiesTest(unittest.TestCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
"Hook method for setting up class fixture before running tests in the class."
|
|
if not hasattr(cls, 'assertItemsEqual'):
|
|
cls.assertItemsEqual = cls.assertCountEqual
|
|
|
|
def test_replication_strategy(self):
|
|
"""
|
|
Basic code coverage testing that ensures different ReplicationStrategies
|
|
can be initiated using parameters correctly.
|
|
"""
|
|
|
|
rs = ReplicationStrategy()
|
|
|
|
self.assertEqual(rs.create('OldNetworkTopologyStrategy', None), _UnknownStrategy('OldNetworkTopologyStrategy', None))
|
|
fake_options_map = {'options': 'map'}
|
|
uks = rs.create('OldNetworkTopologyStrategy', fake_options_map)
|
|
self.assertEqual(uks, _UnknownStrategy('OldNetworkTopologyStrategy', fake_options_map))
|
|
self.assertEqual(uks.make_token_replica_map({}, []), {})
|
|
|
|
fake_options_map = {'dc1': '3'}
|
|
self.assertIsInstance(rs.create('NetworkTopologyStrategy', fake_options_map), NetworkTopologyStrategy)
|
|
self.assertEqual(rs.create('NetworkTopologyStrategy', fake_options_map).dc_replication_factors,
|
|
NetworkTopologyStrategy(fake_options_map).dc_replication_factors)
|
|
|
|
fake_options_map = {'options': 'map'}
|
|
self.assertIsNone(rs.create('SimpleStrategy', fake_options_map))
|
|
|
|
fake_options_map = {'options': 'map'}
|
|
self.assertIsInstance(rs.create('LocalStrategy', fake_options_map), LocalStrategy)
|
|
|
|
fake_options_map = {'options': 'map', 'replication_factor': 3}
|
|
self.assertIsInstance(rs.create('SimpleStrategy', fake_options_map), SimpleStrategy)
|
|
self.assertEqual(rs.create('SimpleStrategy', fake_options_map).replication_factor,
|
|
SimpleStrategy(fake_options_map).replication_factor)
|
|
|
|
self.assertEqual(rs.create('xxxxxxxx', fake_options_map), _UnknownStrategy('xxxxxxxx', fake_options_map))
|
|
|
|
self.assertRaises(NotImplementedError, rs.make_token_replica_map, None, None)
|
|
self.assertRaises(NotImplementedError, rs.export_for_schema)
|
|
|
|
def test_nts_make_token_replica_map(self):
|
|
token_to_host_owner = {}
|
|
|
|
dc1_1 = Host('dc1.1', SimpleConvictionPolicy)
|
|
dc1_2 = Host('dc1.2', SimpleConvictionPolicy)
|
|
dc1_3 = Host('dc1.3', SimpleConvictionPolicy)
|
|
for host in (dc1_1, dc1_2, dc1_3):
|
|
host.set_location_info('dc1', 'rack1')
|
|
token_to_host_owner[MD5Token(0)] = dc1_1
|
|
token_to_host_owner[MD5Token(100)] = dc1_2
|
|
token_to_host_owner[MD5Token(200)] = dc1_3
|
|
|
|
dc2_1 = Host('dc2.1', SimpleConvictionPolicy)
|
|
dc2_2 = Host('dc2.2', SimpleConvictionPolicy)
|
|
dc2_1.set_location_info('dc2', 'rack1')
|
|
dc2_2.set_location_info('dc2', 'rack1')
|
|
token_to_host_owner[MD5Token(1)] = dc2_1
|
|
token_to_host_owner[MD5Token(101)] = dc2_2
|
|
|
|
dc3_1 = Host('dc3.1', SimpleConvictionPolicy)
|
|
dc3_1.set_location_info('dc3', 'rack3')
|
|
token_to_host_owner[MD5Token(2)] = dc3_1
|
|
|
|
ring = [MD5Token(0),
|
|
MD5Token(1),
|
|
MD5Token(2),
|
|
MD5Token(100),
|
|
MD5Token(101),
|
|
MD5Token(200)]
|
|
|
|
nts = NetworkTopologyStrategy({'dc1': 2, 'dc2': 2, 'dc3': 1})
|
|
replica_map = nts.make_token_replica_map(token_to_host_owner, ring)
|
|
|
|
self.assertItemsEqual(replica_map[MD5Token(0)], (dc1_1, dc1_2, dc2_1, dc2_2, dc3_1))
|
|
|
|
def test_nts_make_token_replica_map_empty_dc(self):
|
|
host = Host('1', SimpleConvictionPolicy)
|
|
host.set_location_info('dc1', 'rack1')
|
|
token_to_host_owner = {MD5Token(0): host}
|
|
ring = [MD5Token(0)]
|
|
nts = NetworkTopologyStrategy({'dc1': 1, 'dc2': 0})
|
|
|
|
replica_map = nts.make_token_replica_map(token_to_host_owner, ring)
|
|
self.assertEqual(set(replica_map[MD5Token(0)]), set([host]))
|
|
|
|
def test_nts_export_for_schema(self):
|
|
strategy = NetworkTopologyStrategy({'dc1': '1', 'dc2': '2'})
|
|
self.assertEqual("{'class': 'NetworkTopologyStrategy', 'dc1': '1', 'dc2': '2'}",
|
|
strategy.export_for_schema())
|
|
|
|
def test_simple_strategy_make_token_replica_map(self):
|
|
host1 = Host('1', SimpleConvictionPolicy)
|
|
host2 = Host('2', SimpleConvictionPolicy)
|
|
host3 = Host('3', SimpleConvictionPolicy)
|
|
token_to_host_owner = {
|
|
MD5Token(0): host1,
|
|
MD5Token(100): host2,
|
|
MD5Token(200): host3
|
|
}
|
|
ring = [MD5Token(0), MD5Token(100), MD5Token(200)]
|
|
|
|
rf1_replicas = SimpleStrategy({'replication_factor': '1'}).make_token_replica_map(token_to_host_owner, ring)
|
|
self.assertItemsEqual(rf1_replicas[MD5Token(0)], [host1])
|
|
self.assertItemsEqual(rf1_replicas[MD5Token(100)], [host2])
|
|
self.assertItemsEqual(rf1_replicas[MD5Token(200)], [host3])
|
|
|
|
rf2_replicas = SimpleStrategy({'replication_factor': '2'}).make_token_replica_map(token_to_host_owner, ring)
|
|
self.assertItemsEqual(rf2_replicas[MD5Token(0)], [host1, host2])
|
|
self.assertItemsEqual(rf2_replicas[MD5Token(100)], [host2, host3])
|
|
self.assertItemsEqual(rf2_replicas[MD5Token(200)], [host3, host1])
|
|
|
|
rf3_replicas = SimpleStrategy({'replication_factor': '3'}).make_token_replica_map(token_to_host_owner, ring)
|
|
self.assertItemsEqual(rf3_replicas[MD5Token(0)], [host1, host2, host3])
|
|
self.assertItemsEqual(rf3_replicas[MD5Token(100)], [host2, host3, host1])
|
|
self.assertItemsEqual(rf3_replicas[MD5Token(200)], [host3, host1, host2])
|
|
|
|
def test_ss_equals(self):
|
|
self.assertNotEqual(SimpleStrategy({'replication_factor': '1'}), NetworkTopologyStrategy({'dc1': 2}))
|
|
|
|
|
|
class NameEscapingTest(unittest.TestCase):
|
|
|
|
def test_protect_name(self):
|
|
"""
|
|
Test cassandra.metadata.protect_name output
|
|
"""
|
|
self.assertEqual(protect_name('tests'), 'tests')
|
|
self.assertEqual(protect_name('test\'s'), '"test\'s"')
|
|
self.assertEqual(protect_name('test\'s'), "\"test's\"")
|
|
self.assertEqual(protect_name('tests ?!@#$%^&*()'), '"tests ?!@#$%^&*()"')
|
|
self.assertEqual(protect_name('1'), '"1"')
|
|
self.assertEqual(protect_name('1test'), '"1test"')
|
|
|
|
def test_protect_names(self):
|
|
"""
|
|
Test cassandra.metadata.protect_names output
|
|
"""
|
|
self.assertEqual(protect_names(['tests']), ['tests'])
|
|
self.assertEqual(protect_names(
|
|
[
|
|
'tests',
|
|
'test\'s',
|
|
'tests ?!@#$%^&*()',
|
|
'1'
|
|
]),
|
|
[
|
|
'tests',
|
|
"\"test's\"",
|
|
'"tests ?!@#$%^&*()"',
|
|
'"1"'
|
|
])
|
|
|
|
def test_protect_value(self):
|
|
"""
|
|
Test cassandra.metadata.protect_value output
|
|
"""
|
|
self.assertEqual(protect_value(True), "true")
|
|
self.assertEqual(protect_value(False), "false")
|
|
self.assertEqual(protect_value(3.14), '3.14')
|
|
self.assertEqual(protect_value(3), '3')
|
|
self.assertEqual(protect_value('test'), "'test'")
|
|
self.assertEqual(protect_value('test\'s'), "'test''s'")
|
|
self.assertEqual(protect_value(None), 'NULL')
|
|
|
|
def test_is_valid_name(self):
|
|
"""
|
|
Test cassandra.metadata.is_valid_name output
|
|
"""
|
|
self.assertEqual(is_valid_name(None), False)
|
|
self.assertEqual(is_valid_name('test'), True)
|
|
self.assertEqual(is_valid_name('Test'), False)
|
|
self.assertEqual(is_valid_name('t_____1'), True)
|
|
self.assertEqual(is_valid_name('test1'), True)
|
|
self.assertEqual(is_valid_name('1test1'), False)
|
|
|
|
invalid_keywords = cassandra.metadata.cql_keywords - cassandra.metadata.cql_keywords_unreserved
|
|
for keyword in invalid_keywords:
|
|
self.assertEqual(is_valid_name(keyword), False)
|
|
|
|
|
|
class Murmur3TokensTest(unittest.TestCase):
|
|
|
|
def test_murmur3_init(self):
|
|
murmur3_token = Murmur3Token(cassandra.metadata.MIN_LONG - 1)
|
|
self.assertEqual(str(murmur3_token), '<Murmur3Token: -9223372036854775809>')
|
|
|
|
def test_python_vs_c(self):
|
|
from cassandra.murmur3 import _murmur3 as mm3_python
|
|
try:
|
|
from cassandra.cmurmur3 import murmur3 as mm3_c
|
|
|
|
iterations = 100
|
|
for _ in range(iterations):
|
|
for len in range(0, 32): # zero to one block plus full range of tail lengths
|
|
key = os.urandom(len)
|
|
self.assertEqual(mm3_python(key), mm3_c(key))
|
|
|
|
except ImportError:
|
|
raise unittest.SkipTest('The cmurmur3 extension is not available')
|
|
|
|
def test_murmur3_python(self):
|
|
from cassandra.murmur3 import _murmur3
|
|
self._verify_hash(_murmur3)
|
|
|
|
def test_murmur3_c(self):
|
|
try:
|
|
from cassandra.cmurmur3 import murmur3
|
|
self._verify_hash(murmur3)
|
|
except ImportError:
|
|
raise unittest.SkipTest('The cmurmur3 extension is not available')
|
|
|
|
def _verify_hash(self, fn):
|
|
self.assertEqual(fn(six.b('123')), -7468325962851647638)
|
|
self.assertEqual(fn(b'\x00\xff\x10\xfa\x99' * 10), 5837342703291459765)
|
|
self.assertEqual(fn(b'\xfe' * 8), -8927430733708461935)
|
|
self.assertEqual(fn(b'\x10' * 8), 1446172840243228796)
|
|
self.assertEqual(fn(six.b(str(cassandra.metadata.MAX_LONG))), 7162290910810015547)
|
|
|
|
|
|
class MD5TokensTest(unittest.TestCase):
|
|
|
|
def test_md5_tokens(self):
|
|
md5_token = MD5Token(cassandra.metadata.MIN_LONG - 1)
|
|
self.assertEqual(md5_token.hash_fn('123'), 42767516990368493138776584305024125808)
|
|
self.assertEqual(md5_token.hash_fn(str(cassandra.metadata.MAX_LONG)), 28528976619278518853815276204542453639)
|
|
self.assertEqual(str(md5_token), '<MD5Token: %s>' % -9223372036854775809)
|
|
|
|
|
|
class BytesTokensTest(unittest.TestCase):
|
|
|
|
def test_bytes_tokens(self):
|
|
bytes_token = BytesToken(str(cassandra.metadata.MIN_LONG - 1))
|
|
self.assertEqual(bytes_token.hash_fn('123'), '123')
|
|
self.assertEqual(bytes_token.hash_fn(123), 123)
|
|
self.assertEqual(bytes_token.hash_fn(str(cassandra.metadata.MAX_LONG)), str(cassandra.metadata.MAX_LONG))
|
|
self.assertEqual(str(bytes_token), "<BytesToken: -9223372036854775809>")
|
|
|
|
try:
|
|
bytes_token = BytesToken(cassandra.metadata.MIN_LONG - 1)
|
|
self.fail('Tokens for ByteOrderedPartitioner should be only strings')
|
|
except TypeError:
|
|
pass
|
|
|
|
|
|
class KeyspaceMetadataTest(unittest.TestCase):
|
|
|
|
def test_export_as_string_user_types(self):
|
|
keyspace_name = 'test'
|
|
keyspace = KeyspaceMetadata(keyspace_name, True, 'SimpleStrategy', dict(replication_factor=3))
|
|
keyspace.user_types['a'] = UserType(keyspace_name, 'a', ['one', 'two'],
|
|
[self.mock_user_type('UserType', 'c'),
|
|
self.mock_user_type('IntType', 'int')])
|
|
keyspace.user_types['b'] = UserType(keyspace_name, 'b', ['one', 'two', 'three'],
|
|
[self.mock_user_type('UserType', 'd'),
|
|
self.mock_user_type('IntType', 'int'),
|
|
self.mock_user_type('UserType', 'a')])
|
|
keyspace.user_types['c'] = UserType(keyspace_name, 'c', ['one'],
|
|
[self.mock_user_type('IntType', 'int')])
|
|
keyspace.user_types['d'] = UserType(keyspace_name, 'd', ['one'],
|
|
[self.mock_user_type('UserType', 'c')])
|
|
|
|
self.assertEqual("""CREATE KEYSPACE test WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '3'} AND durable_writes = true;
|
|
|
|
CREATE TYPE test.c (
|
|
one int
|
|
);
|
|
|
|
CREATE TYPE test.a (
|
|
one c,
|
|
two int
|
|
);
|
|
|
|
CREATE TYPE test.d (
|
|
one c
|
|
);
|
|
|
|
CREATE TYPE test.b (
|
|
one d,
|
|
two int,
|
|
three a
|
|
);""", keyspace.export_as_string())
|
|
|
|
def mock_user_type(self, cassname, typename):
|
|
return Mock(**{'cassname': cassname, 'typename': typename, 'cql_parameterized_type.return_value': typename})
|
|
|
|
|
|
class UserTypesTest(unittest.TestCase):
|
|
|
|
def test_as_cql_query(self):
|
|
field_types = [IntegerType, AsciiType, TupleType.apply_parameters([IntegerType, AsciiType])]
|
|
udt = UserType("ks1", "mytype", ["a", "b", "c"], field_types)
|
|
self.assertEqual("CREATE TYPE ks1.mytype (a varint, b ascii, c frozen<tuple<varint, ascii>>);", udt.as_cql_query(formatted=False))
|
|
|
|
self.assertEqual("""CREATE TYPE ks1.mytype (
|
|
a varint,
|
|
b ascii,
|
|
c frozen<tuple<varint, ascii>>
|
|
);""", udt.as_cql_query(formatted=True))
|
|
|
|
def test_as_cql_query_name_escaping(self):
|
|
udt = UserType("MyKeyspace", "MyType", ["AbA", "keyspace"], [AsciiType, AsciiType])
|
|
self.assertEqual('CREATE TYPE "MyKeyspace"."MyType" ("AbA" ascii, "keyspace" ascii);', udt.as_cql_query(formatted=False))
|
|
|
|
|
|
class IndexTest(unittest.TestCase):
|
|
|
|
def test_build_index_as_cql(self):
|
|
column_meta = Mock()
|
|
column_meta.name = 'column_name_here'
|
|
column_meta.table.name = 'table_name_here'
|
|
column_meta.table.keyspace.name = 'keyspace_name_here'
|
|
column_meta.table.columns = {column_meta.name: column_meta}
|
|
connection = Mock()
|
|
connection.server_version = '2.1.0'
|
|
parser = get_schema_parser(connection, 0.1)
|
|
|
|
row = {'index_name': 'index_name_here', 'index_type': 'index_type_here'}
|
|
index_meta = parser._build_index_metadata(column_meta, row)
|
|
self.assertEqual(index_meta.as_cql_query(),
|
|
'CREATE INDEX index_name_here ON keyspace_name_here.table_name_here (column_name_here)')
|
|
|
|
row['index_options'] = '{ "class_name": "class_name_here" }'
|
|
row['index_type'] = 'CUSTOM'
|
|
index_meta = parser._build_index_metadata(column_meta, row)
|
|
self.assertEqual(index_meta.as_cql_query(),
|
|
"CREATE CUSTOM INDEX index_name_here ON keyspace_name_here.table_name_here (column_name_here) USING 'class_name_here'")
|