From e9ab4c5b271bac3215a25aa7adea84b6942839e1 Mon Sep 17 00:00:00 2001 From: Kefu Chai Date: Tue, 20 Jun 2023 11:35:49 +0800 Subject: [PATCH] tools/assertions: fail if exception does not match before this change, we fail the test if repr(e) does *not* match with the given `matching` regular expression. despite that the tests pass as expected, this is what we intend to complete with this verification. let's explain this with a Python REPL session: ``` >>> import re >>> ex = Exception('foobarhello') >>> matching = 'foobar' >>> regex = re.compile(matching) >>> actual = repr(e) >>> actual "Exception('foobarhello')" >>> regex.match(actual) is None True >>> regex.match("c'est la vie") is None True ``` `repr()` returns a canonical string representation of an object. that's why it adds "Exception()" around the error message. and, it fails to match with the the "matching" string. as "Exception('foobarhello')" does not match at the beginning of this string. but we actually expect the unmatch! in other words, two negatives make a positive. to correct this, in this change: * use str() to stringify the exception, so Python does not include its type in the output string * do not check if the return match object is None, but check if it is *not* None. so the test can fail if the message string in exception does not match. * include the message string and regular expression in the raised AssertionError. * instead of using 're.match()', use 're.search()' for matching the regular expression with the message extracted from exception, as sometimes, we just pass a part of message in hope to match with the returned message, even if the part is not sitting at the beginning of the message, for instance, the full message looks like: ``` Cannot execute this query as it might involve data filtering and thus may have unpredictable performance. If you want to execute this query despite the performance unpredictability, use ALLOW FILTERING' ```` but we just use "use ALLOW FILTERING". so instead of changing all the tests, update the assert helper. * do not compile the re. otherwise we only match it for a single time, and then drop the compiled re on the floor. Signed-off-by: Kefu Chai --- tools/assertions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/assertions.py b/tools/assertions.py index 78fd4444d3..ab50649939 100644 --- a/tools/assertions.py +++ b/tools/assertions.py @@ -55,8 +55,8 @@ def _assert_exception(fun, *args, **kwargs): fun(*args) except expected as e: if matching is not None: - regex = re.compile(matching) - assert regex.match(repr(e)) is None + msg = str(e) + assert re.search(matching, msg), f"Raised exception '{msg}' failed to match with '{matching}'" except Exception as e: raise e else: