Skip to content
Permalink
Browse files
Merge remote-tracking branch 'riptano/master' into reusable-cluster
Conflicts:
	upgrade_tests/upgrade_base.py
  • Loading branch information
thobbs committed Jul 1, 2016
2 parents b3f4779 + 2d9275c commit 1ea88ebf026343ba336d4342bddbd914ef630216
Showing 42 changed files with 1,535 additions and 1,653 deletions.
@@ -6,7 +6,7 @@ We plan to move to Python 3 in the near future. Where possible, new code should
- use the `/` on numbers in a Python 3-compatible way. In particular, if you want floor division (which is the behavior of `/` in Python 2), use `//` instead. If you want the result of integer division to be a `float` (e.g. `1 / 2 == 0.5`), add `from __future__ import division` to the top of the imports and use `/`. For more infomration, see [the official Python 3 porting docs](https://docs.python.org/3/howto/pyporting.html#division).
- use `absolute_import`, `division`, and `unicode_literals` in new test files.

Contributions will be evaluated by PEP8. We now strictly enforce compliance, via a linter run with Travis CI against all new pull requests. We do not enforce the default limits on line length, but have established a maximum length of 200 chars as a sanity check. You can conform to PEP8 by running `autopep8` which can be installed via `pip`.
Contributions will be evaluated by PEP8. We now strictly enforce compliance, via a linter run with Travis CI against all new pull requests. We do not enforce the default limits on line length, but have established a maximum length of 200 chars as a sanity check. You can conform to PEP8 by running `autopep8` which can be installed via `pip`.
`pip install autopep8 && autopep8 --in-place -a --ignore E501`

Another way to make sure that your code will pass compliance checks is to run flake8 from a commit hook:
@@ -71,6 +71,14 @@ Feel free to submit test plans without the implemented tests. If you are submitt

In some cases, we organize our test files by putting them in directories. If you do so, please export a module from that directory by placing an `__init__.py` file in the directory with the test files. This makes the modules visible to our test infrastructure scripts that divide tests into buckets for CI.

##Assertions

- When possible, you should use the assert functions from `assertions.py`.
https://github.com/riptano/cassandra-dtest/blob/master/assertions.py
- When none of these are applicable, use python's built in unittest assertions
https://docs.python.org/2/library/unittest.html#assert-methods.
- Naked assert statements should never be used, e.g. `assert True`

## Summary: Review Checklist

- Correctness
@@ -1,85 +1,199 @@
import re
from nose.tools import assert_regexp_matches, assert_equal

from cassandra import (ConsistencyLevel, InvalidRequest, ReadFailure,
from cassandra import (InvalidRequest, ReadFailure,
ReadTimeout, Unauthorized, Unavailable, WriteFailure,
WriteTimeout)
from cassandra.query import SimpleStatement

from tools import rows_to_list
import tools

"""
The assertion methods in this file are used to structure, execute, and test different queries and scenarios. Use these anytime you are trying
to check the content of a table, the row count of a table, if a query should raise an exception, etc. These methods handle error messaging
well, and will help discovering and treating bugs.
def assert_unavailable(fun, *args):
An example:
Imagine some table, test:
id | name
1 | John Doe
2 | Jane Doe
We could assert the row count is 2 by using:
assert_row_count(session, 'test', 2)
After inserting [3, 'Alex Smith'], we can ensure the table is correct by:
assert_all(session, "SELECT * FROM test", [[1, 'John Doe'], [2, 'Jane Doe'], [3, 'Alex Smith']])
or we could check the insert was successful:
assert_one(session, "SELECT * FROM test WHERE id = 3", [3, 'Alex Smith'])
We could remove all rows in test, and assert this was sucessful with:
assert_none(session, "SELECT * FROM test")
Perhaps we want to assert invalid queries will throw an exception:
assert_invalid(session, "SELECT FROM test")
or, maybe after shutting down all the nodes, we want to assert an Unavailable exception is raised:
assert_unavailable(session.execute, "SELECT * FROM test")
OR
assert_exception(session, "SELECT * FROM test", expected=Unavailable)
"""


def _assert_exception(fun, *args, **kwargs):
matching = kwargs.pop('matching', None)
expected = kwargs['expected']
try:
if len(args) == 0:
fun(None)
else:
fun(*args)
except (Unavailable, WriteTimeout, WriteFailure, ReadTimeout, ReadFailure) as e:
pass
except expected as e:
if matching is not None:
assert_regexp_matches(str(e), matching)
except Exception as e:
assert False, "Expecting unavailable exception, got: " + str(e)
raise e
else:
assert False, "Expecting unavailable exception but no exception was raised"
assert False, "Expecting query to raise an exception, but nothing was raised."


def assert_exception(session, query, matching=None, expected=None):
if expected is None:
assert False, "Expected exception should not be None. Your test code is wrong, please set `expected`."

_assert_exception(session.execute, query, matching=matching, expected=expected)


def assert_unavailable(fun, *args):
"""
Attempt to execute a function, and assert Unavailable, WriteTimeout, WriteFailure, ReadTimeout, or ReadFailure exception is raised.
@param fun Function to be executed
@param *args Arguments to be passed to the function
Examples:
assert_unavailable(session2.execute, "SELECT * FROM ttl_table;")
assert_unavailable(lambda c: debug(c.execute(statement)), session)
"""
_assert_exception(fun, *args, expected=(Unavailable, WriteTimeout, WriteFailure, ReadTimeout, ReadFailure))


def assert_invalid(session, query, matching=None, expected=InvalidRequest):
try:
res = session.execute(query)
assert False, "Expecting query to be invalid: got %s" % res
except AssertionError as e:
raise e
except expected as e:
msg = str(e)
if matching is not None:
assert re.search(matching, msg), "Error message does not contain " + matching + " (error = " + msg + ")"
"""
Attempt to issue a query and assert that the query is invalid.
@param session Session to use
@param query Invalid query to run
@param matching Optional error message string contained within expected exception
@param expected Exception expected to be raised by the invalid query
Examples:
assert_invalid(session, 'DROP USER nonexistent', "nonexistent doesn't exist")
"""
assert_exception(session, query, matching=matching, expected=expected)


def assert_unauthorized(session, query, message):
"""
Attempt to issue a query, and assert Unauthorized is raised.
@param message Expected error message
@param session Session to use
@param query Unauthorized query to run
@param message Expected error message
Examples:
assert_unauthorized(session, "ALTER USER cassandra NOSUPERUSER", "You aren't allowed to alter your own superuser status")
assert_unauthorized(cathy, "ALTER TABLE ks.cf ADD val int", "User cathy has no ALTER permission on <table ks.cf> or any of its parents")
"""
assert_invalid(session, query, message, Unauthorized)
assert_exception(session, query, matching=message, expected=Unauthorized)


def assert_one(session, query, expected, cl=ConsistencyLevel.ONE):
def assert_one(session, query, expected, cl=None):
"""
Assert query returns one row.
@param session Session to use
@param query Query to run
@param expected Expected results from query
@param cl Optional Consistency Level setting. Default ONE
Examples:
assert_one(session, "LIST USERS", ['cassandra', True])
assert_one(session, query, [0, 0])
"""
simple_query = SimpleStatement(query, consistency_level=cl)
res = session.execute(simple_query)
list_res = rows_to_list(res)
assert list_res == [expected], "Expected %s from %s, but got %s" % ([expected], query, list_res)
list_res = tools.rows_to_list(res)
assert list_res == [expected], "Expected {} from {}, but got {}".format([expected], query, list_res)


def assert_none(session, query, cl=None):
"""
Assert query returns nothing
@param session Session to use
@param query Query to run
@param cl Optional Consistency Level setting. Default ONE
def assert_none(session, query, cl=ConsistencyLevel.ONE):
Examples:
assert_none(self.session1, "SELECT * FROM test where key=2;")
assert_none(cursor, "SELECT * FROM test WHERE k=2", cl=ConsistencyLevel.SERIAL)
"""
simple_query = SimpleStatement(query, consistency_level=cl)
res = session.execute(simple_query)
list_res = rows_to_list(res)
assert list_res == [], "Expected nothing from %s, but got %s" % (query, list_res)
list_res = tools.rows_to_list(res)
assert list_res == [], "Expected nothing from {}, but got {}".format(query, list_res)


def assert_all(session, query, expected, cl=ConsistencyLevel.ONE, ignore_order=False):
def assert_all(session, query, expected, cl=None, ignore_order=False):
"""
Assert query returns all expected items optionally in the correct order
@param session Session in use
@param query Query to run
@param expected Expected results from query
@param cl Optional Consistency Level setting. Default ONE
@param ignore_order Optional boolean flag determining whether response is ordered
Examples:
assert_all(session, "LIST USERS", [['aleksey', False], ['cassandra', True]])
assert_all(self.session1, "SELECT * FROM ttl_table;", [[1, 42, 1, 1]])
"""
simple_query = SimpleStatement(query, consistency_level=cl)
res = session.execute(simple_query)
list_res = rows_to_list(res)
list_res = tools.rows_to_list(res)
if ignore_order:
expected = sorted(expected)
list_res = sorted(list_res)
assert list_res == expected, "Expected %s from %s, but got %s" % (expected, query, list_res)
assert list_res == expected, "Expected {} from {}, but got {}".format(expected, query, list_res)


def assert_almost_equal(*args, **kwargs):
"""
Assert variable number of arguments all fall within a margin of error.
@params *args variable number of numerical arguments to check
@params error Optional margin of error. Default 0.16
@params error_message Optional error message to print. Default ''
Examples:
assert_almost_equal(sizes[2], init_size)
assert_almost_equal(ttl_session1, ttl_session2[0][0], error=0.005)
"""
error = kwargs['error'] if 'error' in kwargs else 0.16
vmax = max(args)
vmin = min(args)
error_message = '' if 'error_message' not in kwargs else kwargs['error_message']
assert vmin > vmax * (1.0 - error) or vmin == vmax, "values not within %.2f%% of the max: %s (%s)" % (error * 100, args, error_message)
assert vmin > vmax * (1.0 - error) or vmin == vmax, "values not within {.2f}% of the max: {} ({})".format(error * 100, args, error_message)


def assert_row_count(session, table_name, expected):
""" Function to validate the row count expected in table_name """
def assert_row_count(session, table_name, expected, where=None):
"""
Assert the number of rows in a table matches expected.
@params session Session to use
@param table_name Name of the table to query
@param expected Number of rows expected to be in table
query = "SELECT count(*) FROM {};".format(table_name)
Examples:
assert_row_count(self.session1, 'ttl_table', 1)
"""
if where is not None:
query = "SELECT count(*) FROM {} WHERE {};".format(table_name, where)
else:
query = "SELECT count(*) FROM {};".format(table_name)
res = session.execute(query)
count = res[0][0]
assert count == expected, "Expected a row count of {} in table '{}', but got {}".format(
@@ -89,6 +203,16 @@ def assert_row_count(session, table_name, expected):

def assert_crc_check_chance_equal(session, table, expected, ks="ks", view=False):
"""
Assert crc_check_chance equals expected for a given table or view
@param session Session to use
@param table Name of the table or view to check
@param ks Optional Name of the keyspace
@param view Optional Boolean flag indicating if the table is a view
Examples:
assert_crc_check_chance_equal(session, "compression_opts_table", 0.25)
assert_crc_check_chance_equal(session, "t_by_v", 0.5, view=True)
driver still doesn't support top-level crc_check_chance property,
so let's fetch directly from system_schema
"""
@@ -102,3 +226,17 @@ def assert_crc_check_chance_equal(session, table, expected, ks="ks", view=False)
"SELECT crc_check_chance from system_schema.tables WHERE keyspace_name = 'ks' AND "
"table_name = '{table}';".format(table=table),
[expected])


def assert_length_equal(object_with_length, expected_length):
"""
Assert an object has a specific length.
@param object_with_length The object whose length will be checked
@param expected_length The expected length of the object
Examples:
assert_length_equal(res, nb_counter)
"""
assert_equal(len(object_with_length), expected_length,
"Expected {} to have length {}, but instead is of length {}".format(object_with_length,
expected_length, len(object_with_length)))
@@ -82,11 +82,11 @@ def login_test(self):
try:
self.get_session(user='cassandra', password='badpassword')
except NoHostAvailable as e:
assert isinstance(e.errors.values()[0], AuthenticationFailed)
self.assertIsInstance(e.errors.values()[0], AuthenticationFailed)
try:
self.get_session(user='doesntexist', password='doesntmatter')
except NoHostAvailable as e:
assert isinstance(e.errors.values()[0], AuthenticationFailed)
self.assertIsInstance(e.errors.values()[0], AuthenticationFailed)

# from 2.2 role creation is granted by CREATE_ROLE permissions, not superuser status
@since('1.2', max_version='2.1.x')
@@ -5,7 +5,7 @@
from cassandra import ConsistencyLevel, Timeout, Unavailable
from cassandra.query import SimpleStatement

from assertions import assert_invalid, assert_one, assert_unavailable
from assertions import assert_invalid, assert_one, assert_unavailable, assert_all
from dtest import CASSANDRA_DIR, Tester, debug
from tools import since

@@ -34,8 +34,7 @@ def counter_batch_accepts_counter_mutations_test(self):
UPDATE clicks SET total = total + 1 WHERE userid = 2 and url = 'http://baz.com'
APPLY BATCH
""")
rows = session.execute("SELECT total FROM clicks")
assert [list(rows[0]), list(rows[1]), list(rows[2])] == [[1], [1], [1]], rows
assert_all(session, "SELECT total FROM clicks", [[1], [1], [1]])

def counter_batch_rejects_regular_mutations_test(self):
""" Test that counter batch rejects non-counter mutations """
@@ -60,9 +59,7 @@ def logged_batch_accepts_regular_mutations_test(self):
INSERT INTO users (id, firstname, lastname) VALUES (1, 'Will', 'Turner')
APPLY BATCH
""")
rows = session.execute("SELECT * FROM users")
res = sorted(rows)
assert [list(res[0]), list(res[1])] == [[0, u'Jack', u'Sparrow'], [1, u'Will', u'Turner']], res
assert_all(session, "SELECT * FROM users", [[1, u'Will', u'Turner'], [0, u'Jack', u'Sparrow']])

@since('3.0')
def logged_batch_gcgs_below_threshold_single_table_test(self):
@@ -151,9 +148,7 @@ def unlogged_batch_accepts_regular_mutations_test(self):
INSERT INTO users (id, firstname, lastname) VALUES (2, 'Elizabeth', 'Swann')
APPLY BATCH
""")
rows = session.execute("SELECT * FROM users")
res = sorted(rows)
assert [list(res[0]), list(res[1])] == [[0, u'Jack', u'Sparrow'], [2, u'Elizabeth', u'Swann']], res
assert_all(session, "SELECT * FROM users", [[0, u'Jack', u'Sparrow'], [2, u'Elizabeth', u'Swann']])

def unlogged_batch_rejects_counter_mutations_test(self):
""" Test that unlogged batch rejects counter mutations """
@@ -191,7 +186,7 @@ def logged_batch_doesnt_throw_uae_test(self):
APPLY BATCH
""", consistency_level=ConsistencyLevel.ANY)
session.execute(query)
assert True
assert_all(session, "SELECT * FROM users", [[1, u'Will', u'Turner'], [0, u'Jack', u'Sparrow']])

def acknowledged_by_batchlog_not_set_when_batchlog_write_fails_test(self):
""" Test that acknowledged_by_batchlog is False if batchlog can't be written """
@@ -226,9 +221,8 @@ def batch_uses_proper_timestamp_test(self):
INSERT INTO users (id, firstname, lastname) VALUES (1, 'Will', 'Turner')
APPLY BATCH
""")
rows = session.execute("SELECT id, writetime(firstname), writetime(lastname) FROM users")
res = sorted(rows)
assert [list(res[0]), list(res[1])] == [[0, 1111111111111111, 1111111111111111], [1, 1111111111111111, 1111111111111111]], res
query = "SELECT id, writetime(firstname), writetime(lastname) FROM users"
assert_all(session, query, [[1, 1111111111111111, 1111111111111111], [0, 1111111111111111, 1111111111111111]])

def only_one_timestamp_is_valid_test(self):
""" Test that TIMESTAMP must not be used in the statements within the batch. """
@@ -249,9 +243,9 @@ def each_statement_in_batch_uses_proper_timestamp_test(self):
INSERT INTO users (id, firstname, lastname) VALUES (1, 'Will', 'Turner') USING TIMESTAMP 1111111111111112
APPLY BATCH
""")
rows = session.execute("SELECT id, writetime(firstname), writetime(lastname) FROM users")
res = sorted(rows)
assert [list(res[0]), list(res[1])] == [[0, 1111111111111111, 1111111111111111], [1, 1111111111111112, 1111111111111112]], res

query = "SELECT id, writetime(firstname), writetime(lastname) FROM users"
assert_all(session, query, [[1, 1111111111111112, 1111111111111112], [0, 1111111111111111, 1111111111111111]])

def multi_table_batch_for_10554_test(self):
""" Test a batch on 2 tables having different columns, restarting the node afterwards, to reproduce CASSANDRA-10554 """

0 comments on commit 1ea88eb

Please sign in to comment.