def test_path_is_boolean(self): """ Check expected result from a query asking for objects tagged with a boolean value. """ boolean_tag = logic.create_tag( user=self.site_admin_user, name="bool-tag", description="A tag for annotating boolean data.", type_of="b", namespace=self.test_namespace, private=False, ) annotation1 = boolean_tag.annotate(self.admin_user, "test_object1", True) annotation2 = boolean_tag.annotate(self.admin_user, "test_object2", False) annotation1.save() annotation2.save() # Check False lexer = query.QueryLexer() tokens1 = list(lexer.tokenize("test_namespace/bool-tag is False")) parser = query.QueryParser(self.admin_user, lexer.tag_paths) result = parser.parse((x for x in tokens1)) self.assertEqual(len(result), 1) self.assertIn("test_object2", result) # Check True tokens = list(self.lexer.tokenize("test_namespace/bool-tag is true")) parser = query.QueryParser(self.admin_user, self.lexer.tag_paths) result = parser.parse((x for x in tokens)) self.assertEqual(len(result), 1) self.assertIn("test_object1", result)
def test_path_ge_scalar(self): """ Greater than or equal to (>=) used with a scalar value returns the expected result. """ float_tag = logic.create_tag( user=self.site_admin_user, name="float-tag", description="A tag for annotating floating point data.", type_of="f", namespace=self.test_namespace, private=False, ) annotation1 = float_tag.annotate(self.admin_user, "test_object1", 0.0) annotation2 = float_tag.annotate(self.admin_user, "test_object2", 1.23) annotation3 = float_tag.annotate(self.admin_user, "test_object3", -1.23) annotation1.save() annotation2.save() annotation3.save() tokens = list(self.lexer.tokenize("test_namespace/float-tag >= 0.0")) parser = query.QueryParser(self.admin_user, self.lexer.tag_paths) result = parser.parse((x for x in tokens)) self.assertEqual(len(result), 2) self.assertIn("test_object1", result) self.assertIn("test_object2", result)
def test_path_is_mime(self): """ Check expected result from a query asking for objects tagged with a binary blob of a certain MIME type (e.g. image/png). """ binary_tag = logic.create_tag( user=self.site_admin_user, name="bin-tag", description="A tag for annotating binary data.", type_of="a", namespace=self.test_namespace, private=False, ) val = uploadedfile.InMemoryUploadedFile( file=BytesIO(b"hello"), field_name="", name="file.txt", content_type="text/text", size=5, charset="utf-8", ) annotation = binary_tag.annotate(self.admin_user, "test_object1", val) annotation.save() tokens = list( self.lexer.tokenize("test_namespace/bin-tag is mime:text/text")) parser = query.QueryParser(self.admin_user, self.lexer.tag_paths) result = parser.parse((x for x in tokens)) self.assertEqual(len(result), 1) self.assertIn("test_object1", result)
def test_evaluate_query(self): """ Given a tag path, an indication of the type of object to which such a query could be applied, a Q object (defining an appropriate query) and an optional Q object defining what to exclude: * Check the tag is of the appropriate type to which the queries could be applied (raise a ValueError if not), * Return a set containing the object_ids of matching objects annotated by the tag that match the queries. """ val = self.public_tag.annotate(self.admin_user, "test_object", "a test value") val.save() list(self.lexer.tokenize('test_namespace/public_tag matches "hello"')) parser = query.QueryParser(self.admin_user, self.lexer.tag_paths) result = parser._evaluate_query( "test_namespace/public_tag", { "string", "url", }, "MATCHES", Q(value__contains="test"), ) self.assertEqual(len(result), 1) self.assertIn("test_object", result)
def test_init_missing_tag(self): """ Ensure that the tagpaths are checked for read permission with the referenced user. In this case the tag does not exist. """ list(self.lexer.tokenize('test_namespace/missing_tag matches "hello"')) with self.assertRaises(ValueError) as ex: query.QueryParser(self.normal_user, self.lexer.tag_paths) msg = ex.exception.args[0] self.assertIn("test_namespace/missing_tag", msg)
def test_init_readable_tag(self): """ Ensure that the tagpaths are checked for read permission with the referenced user. In this case, the tag is readable by the referenced user. """ list(self.lexer.tokenize('test_namespace/public_tag matches "hello"')) parser = query.QueryParser(self.admin_user, self.lexer.tag_paths) self.assertEqual(len(parser.tags), 1) self.assertIn(self.public_tag.path, parser.tags) self.assertEqual(parser.tags[self.public_tag.path], self.public_tag)
def test_syntax_error(self): """ A problem query results in a syntax error indicating where the error is. """ tokens = list(self.lexer.tokenize("test_namespace/public_tag and 100")) parser = query.QueryParser(self.admin_user, self.lexer.tag_paths) with self.assertRaises(SyntaxError) as ex: parser.parse((x for x in tokens)) msg = ex.exception.args[0] self.assertEquals( 'Cannot parse AND (with value "and") on line 1, character 26.', msg)
def test_path_iis_string(self): """ Check expected results from a query asking for objects tagged with a string/pointer that case insensitively matches the search term. """ pointer_tag = logic.create_tag( user=self.site_admin_user, name="pointer-tag", description="A tag for pointing at things via a URL.", type_of="p", namespace=self.test_namespace, private=False, ) val1 = "https://ntoll.org/" val2 = "Hello" annotation1 = pointer_tag.annotate(self.admin_user, "test_object1", val1) annotation2 = self.public_tag.annotate(self.admin_user, "test_object2", val2) annotation1.save() annotation2.save() # Check pointer match. lexer = query.QueryLexer() tokens1 = list( lexer.tokenize( 'test_namespace/pointer-tag iis "https://NTOLL.ORG/"')) parser = query.QueryParser(self.admin_user, lexer.tag_paths) result = parser.parse((x for x in tokens1)) self.assertEqual(len(result), 1) self.assertIn("test_object1", result) # Check string match. tokens2 = list( self.lexer.tokenize('test_namespace/public_tag iis "helLO"')) parser = query.QueryParser(self.admin_user, self.lexer.tag_paths) result = parser.parse((x for x in tokens2)) self.assertEqual(len(result), 1) self.assertIn("test_object2", result)
def test_has_path(self): """ Check expected result from a query for the presence of a tag on an object. """ val1 = self.public_tag.annotate(self.admin_user, "test_object1", "a test value") val2 = self.user_tag.annotate(self.admin_user, "test_object2", True) val3 = self.public_tag.annotate(self.admin_user, "test_object3", "another test value") val1.save() val2.save() val3.save() tokens = list(self.lexer.tokenize("has test_namespace/public_tag")) parser = query.QueryParser(self.admin_user, self.lexer.tag_paths) result = parser.parse((x for x in tokens)) self.assertEqual(len(result), 2) self.assertIn("test_object1", result) self.assertIn("test_object3", result)
def test_and_expr(self): """ A logical AND operator returns the expected results. """ val1 = self.public_tag.annotate(self.admin_user, "test_object1", "val1") val2 = self.user_tag.annotate(self.admin_user, "test_object1", True) val3 = self.public_tag.annotate(self.admin_user, "test_object2", "val1") val1.save() val2.save() val3.save() tokens = list( self.lexer.tokenize('test_namespace/public_tag is "val1" ' "and test_namespace/user_tag is true")) parser = query.QueryParser(self.admin_user, self.lexer.tag_paths) result = parser.parse((x for x in tokens)) self.assertEqual(len(result), 1) self.assertIn("test_object1", result)
def test_parenthesis_expr(self): """ Parenthesis define scope to produce expected results. """ val1 = self.public_tag.annotate(self.admin_user, "test_object1", "val1") val2 = self.user_tag.annotate(self.admin_user, "test_object1", True) val3 = self.reader_tag.annotate(self.admin_user, "test_object2", 42) val1.save() val2.save() val3.save() tokens = list( self.lexer.tokenize("has test_namespace/public_tag and " "(test_namespace/reader_tag = 42 or " "test_namespace/user_tag is true)")) parser = query.QueryParser(self.admin_user, self.lexer.tag_paths) result = parser.parse((x for x in tokens)) self.assertEqual(len(result), 1) self.assertIn("test_object1", result)
def test_path_lt_scalar(self): """ Less than (<) used with a scalar value returns the expected result. """ int_tag = logic.create_tag( user=self.site_admin_user, name="int-tag", description="A tag for annotating integer data.", type_of="i", namespace=self.test_namespace, private=False, ) annotation1 = int_tag.annotate(self.admin_user, "test_object1", 0) annotation2 = int_tag.annotate(self.admin_user, "test_object2", 100) annotation1.save() annotation2.save() tokens = list(self.lexer.tokenize("test_namespace/int-tag < 100")) parser = query.QueryParser(self.admin_user, self.lexer.tag_paths) result = parser.parse((x for x in tokens)) self.assertEqual(len(result), 1) self.assertIn("test_object1", result)
def test_evaluate_query_wrong_type_of_operator(self): """ If the query uses an operator that can't work with the type of the referenced tag, an informative ValueError exception is raised. """ val = self.public_tag.annotate(self.admin_user, "test_object", "a test value") val.save() list(self.lexer.tokenize('test_namespace/public_tag matches "hello"')) parser = query.QueryParser(self.admin_user, self.lexer.tag_paths) with self.assertRaises(ValueError) as ex: parser._evaluate_query( "test_namespace/public_tag", {"int", "float", "datetime", "duration"}, "MATCHES", Q(value__contains="test"), ) msg = ex.exception.args[0] expected = ( 'Cannot use operator "MATCHES" on tag: test_namespace/public_tag ' "(string)") self.assertEqual(expected, msg)
def test_evaluate_query_unknown_tag(self): """ If the query is for an unknown tag, an informative ValueError exception is raised. """ val = self.public_tag.annotate(self.admin_user, "test_object", "a test value") val.save() list(self.lexer.tokenize('test_namespace/public_tag matches "hello"')) parser = query.QueryParser(self.admin_user, self.lexer.tag_paths) with self.assertRaises(ValueError) as ex: parser._evaluate_query( "test_namespace/unknown_tag", { "string", "url", }, "MATCHES", Q(value__contains="test"), ) msg = ex.exception.args[0] self.assertEqual("Unknown tag: test_namespace/unknown_tag", msg)
def test_path_le_scalar(self): """ Less than or equal to (>=) used with a scalar value returns the expected result. """ datetime_tag = logic.create_tag( user=self.site_admin_user, name="dt-tag", description="A tag for annotating datetime data.", type_of="d", namespace=self.test_namespace, private=False, ) annotation1 = datetime_tag.annotate( self.admin_user, "test_object1", datetime(2020, 8, 19, tzinfo=timezone.utc), ) annotation2 = datetime_tag.annotate( self.admin_user, "test_object2", datetime(2019, 8, 19, tzinfo=timezone.utc), ) annotation3 = datetime_tag.annotate( self.admin_user, "test_object3", datetime(2021, 8, 19, tzinfo=timezone.utc), ) annotation1.save() annotation2.save() annotation3.save() tokens = list( self.lexer.tokenize("test_namespace/dt-tag <= 2020-08-19")) parser = query.QueryParser(self.admin_user, self.lexer.tag_paths) result = parser.parse((x for x in tokens)) self.assertEqual(len(result), 2) self.assertIn("test_object1", result) self.assertIn("test_object2", result)
def test_path_gt_scalar(self): """ Great than (>) used with a scalar value returns the expected result. """ dur_tag = logic.create_tag( user=self.site_admin_user, name="dur-tag", description="A tag for annotating duration data.", type_of="u", namespace=self.test_namespace, private=False, ) annotation1 = dur_tag.annotate(self.admin_user, "test_object1", timedelta(seconds=1024)) annotation2 = dur_tag.annotate(self.admin_user, "test_object2", timedelta(days=1024)) annotation1.save() annotation2.save() tokens = list(self.lexer.tokenize("test_namespace/dur-tag > 100d")) parser = query.QueryParser(self.admin_user, self.lexer.tag_paths) result = parser.parse((x for x in tokens)) self.assertEqual(len(result), 1) self.assertIn("test_object2", result)