From 0bcf4c64a68ef2f54e6509948cce5255c107dfa3 Mon Sep 17 00:00:00 2001 From: Renaud Lessard Larouche Date: Tue, 29 Oct 2019 16:16:30 -0400 Subject: [PATCH 1/2] Ensure mockgun string comparison are case insensitive --- shotgun_api3/lib/mockgun/mockgun.py | 7 ++ tests/test_mockgun.py | 105 ++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/shotgun_api3/lib/mockgun/mockgun.py b/shotgun_api3/lib/mockgun/mockgun.py index acabd8f83..e3ca18fc4 100644 --- a/shotgun_api3/lib/mockgun/mockgun.py +++ b/shotgun_api3/lib/mockgun/mockgun.py @@ -596,6 +596,13 @@ def _compare(self, field_type, lval, operator, rval): if operator == "is": return lval == rval elif field_type == "text": + # Shotgun string comparison is case insensitive + lval = lval.lower() + if isinstance(rval, list): + rval = [val.lower() for val in rval] + elif isinstance(rval, six.string_types): + rval = rval.lower() + if operator == "is": return lval == rval elif operator == "is_not": diff --git a/tests/test_mockgun.py b/tests/test_mockgun.py index ce8511352..5aaffcd02 100644 --- a/tests/test_mockgun.py +++ b/tests/test_mockgun.py @@ -202,6 +202,62 @@ def setUp(self): self._mockgun = Mockgun("https://test.shotgunstudio.com", login="user", password="1234") self._user = self._mockgun.create("HumanUser", {"login": "user"}) + def test_operator_is(self): + """ + Ensure is operator work. + """ + item = self._mockgun.find_one("HumanUser", [["login", "is", "user"]]) + self.assertTrue(item) + + def test_operator_is_case_sensitivity(self): + """ + Ensure is operator is case insensitive. + """ + item = self._mockgun.find_one("HumanUser", [["login", "is", "USER"]]) + self.assertTrue(item) + + def test_operator_is_not(self): + """ + Ensure the is_not operator works. + """ + item = self._mockgun.find_one("HumanUser", [["login", "is_not", "another_user"]]) + self.assertTrue(item) + + def test_operator_is_not_case_sensitivity(self): + """ + Ensure the is_not operator is case insensitive. + """ + item = self._mockgun.find_one("HumanUser", [["login", "is_not", "USER"]]) + self.assertFalse(item) + + def test_operator_in(self): + """ + Ensure the in operator works. + """ + item = self._mockgun.find_one("HumanUser", [["login", "in", ["user"]]]) + self.assertTrue(item) + + def test_operator_in_case_sensitivity(self): + """ + Ensure the in operator is case insensitive. + """ + item = self._mockgun.find_one("HumanUser", [["login", "in", ["USER"]]]) + self.assertTrue(item) + + def test_operator_not_in(self): + """ + Ensure the not_in operator works. + """ + item = self._mockgun.find_one("HumanUser", [["login", "not_in", ["foo"]]]) + self.assertTrue(item) + + def test_operator_not_in_case_sensitivity(self): + """ + Ensure not_in operator is case insensitive. + """ + item = self._mockgun.find_one("HumanUser", [["login", "not_in", ["USER"]]]) + self.assertFalse(item) + def test_operator_contains(self): """ Ensures contains operator works. @@ -209,6 +265,55 @@ def test_operator_contains(self): item = self._mockgun.find_one("HumanUser", [["login", "contains", "se"]]) self.assertTrue(item) + def test_operator_contains_case_sensitivity(self): + """ + Ensure contains operator is case insensitive. + """ + item = self._mockgun.find_one("HumanUser", [["login", "contains", "SE"]]) + self.assertTrue(item) + + def test_operator_not_contains(self): + """ + Ensure not_contains operator works. + """ + item = self._mockgun.find_one("HumanUser", [["login", "not_contains", "foo"]]) + self.assertTrue(item) + + def test_operator_not_contains_case_sensitivity(self): + """ + Ensure not_contains operator is case insensitive. + """ + item = self._mockgun.find_one("HumanUser", [["login", "not_contains", "USER"]]) + self.assertFalse(item) + + def test_operator_starts_with(self): + """ + Ensure starts_with operator works. + """ + item = self._mockgun.find_one("HumanUser", [["login", "starts_with", "us"]]) + self.assertTrue(item) + + def test_operator_starts_with_case_sensitivity(self): + """ + Ensure starts_with operator is case insensitive. + """ + item = self._mockgun.find_one("HumanUser", [["login", "starts_with", "US"]]) + self.assertTrue(item) + + def test_operator_ends_with(self): + """ + Ensure ends_with operator works. + """ + item = self._mockgun.find_one("HumanUser", [["login", "ends_with", "er"]]) + self.assertTrue(item) + + def test_operator_ends_with_case_sensitivity(self): + """ + Ensure starts_with operator is case insensitive. + """ + item = self._mockgun.find_one("HumanUser", [["login", "ends_with", "ER"]]) + self.assertTrue(item) + class TestMultiEntityFieldComparison(TestBaseWithExceptionTests): """ From d074de3eb176518cf772a8e352769f85f9afbade Mon Sep 17 00:00:00 2001 From: Renaud Lessard Larouche Date: Mon, 25 Nov 2019 19:43:49 -0500 Subject: [PATCH 2/2] Fix issues when comparing text fields with None --- shotgun_api3/lib/mockgun/mockgun.py | 19 +++- tests/test_mockgun.py | 140 +++++++++++++++++++--------- 2 files changed, 112 insertions(+), 47 deletions(-) diff --git a/shotgun_api3/lib/mockgun/mockgun.py b/shotgun_api3/lib/mockgun/mockgun.py index e3ca18fc4..21ea77450 100644 --- a/shotgun_api3/lib/mockgun/mockgun.py +++ b/shotgun_api3/lib/mockgun/mockgun.py @@ -596,12 +596,21 @@ def _compare(self, field_type, lval, operator, rval): if operator == "is": return lval == rval elif field_type == "text": + # Some operations expect a list but can deal with a single value + if operator in ("in", "not_in") and not isinstance(rval, list): + rval = [rval] + + # Some operation expect a string but can deal with None + elif operator in ("starts_with", "ends_with", "contains", "not_contains"): + lval = lval or '' + rval = rval or '' + # Shotgun string comparison is case insensitive - lval = lval.lower() + lval = lval.lower() if lval is not None else None if isinstance(rval, list): - rval = [val.lower() for val in rval] - elif isinstance(rval, six.string_types): - rval = rval.lower() + rval = [val.lower() if val is not None else None for val in rval] + else: + rval = rval.lower() if rval is not None else None if operator == "is": return lval == rval @@ -612,7 +621,7 @@ def _compare(self, field_type, lval, operator, rval): elif operator == "contains": return rval in lval elif operator == "not_contains": - return lval not in rval + return rval not in lval elif operator == "starts_with": return lval.startswith(rval) elif operator == "ends_with": diff --git a/tests/test_mockgun.py b/tests/test_mockgun.py index 5aaffcd02..1d2023dad 100644 --- a/tests/test_mockgun.py +++ b/tests/test_mockgun.py @@ -200,119 +200,175 @@ def setUp(self): Creates test data. """ self._mockgun = Mockgun("https://test.shotgunstudio.com", login="user", password="1234") - self._user = self._mockgun.create("HumanUser", {"login": "user"}) + self._user1 = self._mockgun.create("HumanUser", {"login": "user"}) + self._user2 = self._mockgun.create("HumanUser", {"login": None}) def test_operator_is(self): """ Ensure is operator work. """ - item = self._mockgun.find_one("HumanUser", [["login", "is", "user"]]) - self.assertTrue(item) + actual = self._mockgun.find("HumanUser", [["login", "is", "user"]]) + expected = [{"type": "HumanUser", "id": self._user1["id"]}] + self.assertEqual(expected, actual) + + def test_operator_is_none(self): + """ + Ensure is operator work when used with None. + """ + actual = self._mockgun.find("HumanUser", [["login", "is", None]]) + expected = [{"type": "HumanUser", "id": self._user2["id"]}] + self.assertEqual(expected, actual) def test_operator_is_case_sensitivity(self): """ Ensure is operator is case insensitive. """ - item = self._mockgun.find_one("HumanUser", [["login", "is", "USER"]]) - self.assertTrue(item) + actual = self._mockgun.find("HumanUser", [["login", "is", "USER"]]) + expected = [{"type": "HumanUser", "id": self._user1["id"]}] + self.assertEqual(expected, actual) def test_operator_is_not(self): """ Ensure the is_not operator works. """ - item = self._mockgun.find_one("HumanUser", [["login", "is_not", "another_user"]]) - self.assertTrue(item) + actual = self._mockgun.find("HumanUser", [["login", "is_not", "user"]]) + expected = [{"type": "HumanUser", "id": self._user2["id"]}] + self.assertEqual(expected, actual) + + def test_operator_is_not_none(self): + """ + Ensure the is_not operator works when used with None. + """ + actual = self._mockgun.find("HumanUser", [["login", "is_not", None]]) + expected = [{"type": "HumanUser", "id": self._user1["id"]}] + self.assertEqual(expected, actual) def test_operator_is_not_case_sensitivity(self): """ Ensure the is_not operator is case insensitive. """ - item = self._mockgun.find_one("HumanUser", [["login", "is_not", "USER"]]) - self.assertFalse(item) + actual = self._mockgun.find("HumanUser", [["login", "is_not", "USER"]]) + expected = [{"type": "HumanUser", "id": self._user2["id"]}] + self.assertEqual(expected, actual) def test_operator_in(self): """ Ensure the in operator works. """ - item = self._mockgun.find_one("HumanUser", [["login", "in", ["user"]]]) - self.assertTrue(item) + actual = self._mockgun.find("HumanUser", [["login", "in", ["user"]]]) + expected = [{"type": "HumanUser", "id": self._user1["id"]}] + self.assertEqual(expected, actual) + + def test_operator_in_none(self): + """ + Ensure the in operator works with a list containing None. + """ + actual = self._mockgun.find("HumanUser", [["login", "in", [None]]]) + expected = [{"type": "HumanUser", "id": self._user2["id"]}] + self.assertEqual(expected, actual) def test_operator_in_case_sensitivity(self): """ Ensure the in operator is case insensitive. """ - item = self._mockgun.find_one("HumanUser", [["login", "in", ["USER"]]]) - self.assertTrue(item) + actual = self._mockgun.find("HumanUser", [["login", "in", ["USER"]]]) + expected = [{"type": "HumanUser", "id": self._user1["id"]}] + self.assertEqual(expected, actual) def test_operator_not_in(self): """ Ensure the not_in operator works. """ - item = self._mockgun.find_one("HumanUser", [["login", "not_in", ["foo"]]]) - self.assertTrue(item) + actual = self._mockgun.find("HumanUser", [["login", "not_in", ["foo"]]]) + expected = [ + {"type": "HumanUser", "id": self._user1["id"]}, + {"type": "HumanUser", "id": self._user2["id"]} + ] + self.assertEqual(expected, actual) + + def test_operator_not_in_none(self): + """ + Ensure the not_not operator works with a list containing None. + """ + actual = self._mockgun.find("HumanUser", [["login", "not_in", [None]]]) + expected = [{"type": "HumanUser", "id": self._user1["id"]}] + self.assertEqual(expected, actual) def test_operator_not_in_case_sensitivity(self): """ - Ensure not_in operator is case insensitive. + Ensure the not_in operator is case insensitive. """ - item = self._mockgun.find_one("HumanUser", [["login", "not_in", ["USER"]]]) - self.assertFalse(item) + actual = self._mockgun.find("HumanUser", [["login", "not_in", ["USER"]]]) + expected = [{"type": "HumanUser", "id": self._user2["id"]}] + self.assertEqual(expected, actual) def test_operator_contains(self): """ - Ensures contains operator works. + Ensures the contains operator works. """ - item = self._mockgun.find_one("HumanUser", [["login", "contains", "se"]]) - self.assertTrue(item) + actual = self._mockgun.find("HumanUser", [["login", "contains", "se"]]) + expected = [{"type": "HumanUser", "id": self._user1["id"]}] + self.assertEqual(expected, actual) def test_operator_contains_case_sensitivity(self): """ - Ensure contains operator is case insensitive. + Ensure the contains operator is case insensitive. """ - item = self._mockgun.find_one("HumanUser", [["login", "contains", "SE"]]) - self.assertTrue(item) + actual = self._mockgun.find("HumanUser", [["login", "contains", "SE"]]) + expected = [{"type": "HumanUser", "id": self._user1["id"]}] + self.assertEqual(expected, actual) def test_operator_not_contains(self): """ - Ensure not_contains operator works. + Ensure the not_contains operator works. """ - item = self._mockgun.find_one("HumanUser", [["login", "not_contains", "foo"]]) - self.assertTrue(item) + actual = self._mockgun.find("HumanUser", [["login", "not_contains", "user"]]) + expected = [ + {"type": "HumanUser", "id": self._user2["id"]} + ] + self.assertEqual(expected, actual) def test_operator_not_contains_case_sensitivity(self): """ - Ensure not_contains operator is case insensitive. + Ensure the not_contains operator is case insensitive. """ - item = self._mockgun.find_one("HumanUser", [["login", "not_contains", "USER"]]) - self.assertFalse(item) + actual = self._mockgun.find("HumanUser", [["login", "not_contains", "USER"]]) + expected = [ + {"type": "HumanUser", "id": self._user2["id"]} + ] + self.assertEqual(expected, actual) def test_operator_starts_with(self): """ - Ensure starts_with operator works. + Ensure the starts_with operator works. """ - item = self._mockgun.find_one("HumanUser", [["login", "starts_with", "us"]]) - self.assertTrue(item) + actual = self._mockgun.find("HumanUser", [["login", "starts_with", "us"]]) + expected = [{"type": "HumanUser", "id": self._user1["id"]}] + self.assertEqual(expected, actual) def test_operator_starts_with_case_sensitivity(self): """ - Ensure starts_with operator is case insensitive. + Ensure the starts_with operator is case insensitive. """ - item = self._mockgun.find_one("HumanUser", [["login", "starts_with", "US"]]) - self.assertTrue(item) + actual = self._mockgun.find("HumanUser", [["login", "starts_with", "US"]]) + expected = [{"type": "HumanUser", "id": self._user1["id"]}] + self.assertEqual(expected, actual) def test_operator_ends_with(self): """ - Ensure ends_with operator works. + Ensure the ends_with operator works. """ - item = self._mockgun.find_one("HumanUser", [["login", "ends_with", "er"]]) - self.assertTrue(item) + actual = self._mockgun.find("HumanUser", [["login", "ends_with", "er"]]) + expected = [{"type": "HumanUser", "id": self._user1["id"]}] + self.assertEqual(expected, actual) def test_operator_ends_with_case_sensitivity(self): """ - Ensure starts_with operator is case insensitive. + Ensure the starts_with operator is case insensitive. """ - item = self._mockgun.find_one("HumanUser", [["login", "ends_with", "ER"]]) - self.assertTrue(item) + actual = self._mockgun.find("HumanUser", [["login", "ends_with", "ER"]]) + expected = [{"type": "HumanUser", "id": self._user1["id"]}] + self.assertEqual(expected, actual) class TestMultiEntityFieldComparison(TestBaseWithExceptionTests):