def test_combine() -> None: query1 = Query.by(P("test") == True).traverse_out().combine( Query.by("foo")).combine(Query.by("bla")) assert str(query1) == 'test == true -default-> (is("foo") and is("bla"))' query2 = (Query.by(P("test") == True).traverse_out().combine( Query.by("foo").traverse_out()).combine( Query.by("bla").traverse_out())) assert str( query2 ) == 'test == true -default-> is("foo") -default-> is("bla") -default->' query3 = (Query.by(P("test") == True).traverse_out().filter( "boo").traverse_out().filter("bar").combine(Query.by("foo")).combine( Query.by("bla"))) assert str( query3 ) == 'test == true -default-> is("boo") -default-> ((is("bar") and is("foo")) and is("bla"))' query4 = Query.by("a").with_limit(10).combine(Query.by("b").with_limit(2)) assert query4.current_part.limit == Limit(0, 2) # minimum is taken with pytest.raises(AttributeError): # can not combine 2 aggregations parse_query("aggregate(sum(1)): is(a)").combine( parse_query("aggregate(sum(1)): is(a)")) with pytest.raises(AttributeError): # can not combine 2 with statements parse_query("is(foo) with(empty, -default->)").combine( parse_query("is(bla) with(empty, -default->)"))
def test_not() -> None: assert_round_trip(not_term, (P.with_id("foo") | P.of_kind("bla")).not_term()) assert_round_trip(not_term, P.of_kind("bla").not_term()) assert_round_trip( not_term, term_parser.parse( "not(is(a) or not is(b) and not a>1 or not b<2 or not(a>1))"))
def test_not() -> None: assert_round_trip(not_term, (P.with_id("foo") | P.of_kind("bla")).not_term()) assert_round_trip(not_term, P.of_kind("bla").not_term()) assert_round_trip( not_term, term_parser.parse( "not(is(a) or not is(b) and not a>1 or not b<2 or not(a>1))")) # make sure not only negates the simple term, not the combined term assert term_parser.parse("not a==b and b==c") == CombinedTerm( NotTerm(P("a") == "b"), "and", P("b") == "c")
def test_filter_term() -> None: assert_round_trip(term_parser, P("mem") < 23) assert_round_trip(term_parser, P.with_id("foo")) assert_round_trip(term_parser, P.of_kind("foo")) assert_round_trip(term_parser, P.of_kind("foo") | P.of_kind("bla")) assert_round_trip( term_parser, ((P.of_kind("foo") | P.of_kind("bla")) & (P("a") > 23)) & (P("b").is_in([1, 2, 3])) & (P("c") == { "a": 123 }), )
def test_function() -> None: assert_round_trip(function_term, P.function("in_subnet").on("foo.bla.bar", 1, "2", True)) assert_round_trip(function_term, P.function("in_subnet").on("foo.bla.bar", "in_subnet")) assert_round_trip( function_term, P.function("in_subnet").on("foo.bla.bar", "in_subnet", "1000")) assert_round_trip(function_term, P.function("has_key").on("foo.bla.bar", "a", "b", "c")) assert_round_trip(function_term, P.function("has_key").on("foo.bla.bar", "a"))
async def test_query_list(filled_graph_db: ArangoGraphDB, foo_model: Model) -> None: blas = Query.by("foo", P("identifier") == "9").traverse_out().filter( "bla", P("f") == 23) async with await filled_graph_db.search_list( QueryModel(blas.on_section("reported"), foo_model)) as gen: result = [from_js(x["reported"], Bla) async for x in gen] assert len(result) == 10 foos_or_blas = parse_query("is([foo, bla])") async with await filled_graph_db.search_list( QueryModel(foos_or_blas.on_section("reported"), foo_model)) as gen: result = [x async for x in gen] assert len(result) == 111 # 113 minus 1 graph_root, minus one cloud
async def load_graph(db: GraphDB, model: Model, base_id: str = "sub_root") -> MultiDiGraph: blas = Query.by("foo", P("identifier") == base_id).traverse_out( 0, Navigation.Max) return await db.search_graph(QueryModel(blas.on_section("reported"), model))
def test_simplify() -> None: # some_criteria | all => all assert str((IsTerm(["test"]) | AllTerm())) == "all" # some_criteria & all => some_criteria assert str((IsTerm(["test"]) & AllTerm())) == 'is("test")' # also works in nested setup q = Query.by(AllTerm() & ((P("test") == True) & (IsTerm(["test"]) | AllTerm()))) assert (str(q)) == "test == true"
def test_query() -> None: query = (Query.by( "ec2", P("cpu") > 4, (P("mem") < 23) | (P("mem") < 59)).merge_with( "cloud", Navigation(1, Navigation.Max, direction=Direction.inbound), Query.mk_term("cloud")).traverse_out().filter( P("some.int.value") < 1, P("some.other") == 23).traverse_out().filter( P("active") == 12, P.function("in_subnet").on( "ip", "1.2.3.4/96")).filter_with( WithClause(WithClauseFilter( "==", 0), Navigation())).group_by([ AggregateVariable( AggregateVariableName("foo")) ], [AggregateFunction("sum", "cpu")]).add_sort( Sort("test", "asc")).with_limit(10)) assert str(query) == ( 'aggregate(foo: sum(cpu)):((is("ec2") and cpu > 4) and (mem < 23 or mem < 59)) ' '{cloud: all <-default[1:]- is("cloud")} -default-> ' "(some.int.value < 1 and some.other == 23) -default-> " '(active == 12 and in_subnet(ip, "1.2.3.4/96")) ' "with(empty, -default->) sort test asc limit 10") assert_round_trip(query_parser, query)
def test_part() -> None: assert_round_trip(part_parser, Part(P.of_kind("test"))) assert_round_trip( part_parser, Part(P.of_kind("test"), navigation=Navigation(1, 10, [EdgeType.delete]))) assert_round_trip( part_parser, Part(P.of_kind("test"), "red", navigation=Navigation(1, 10, [EdgeType.delete]))) with_clause = WithClause(WithClauseFilter("==", 0), Navigation(maybe_edge_types=[EdgeType.delete])) assert_round_trip( part_parser, Part(P.of_kind("test"), "green", with_clause, navigation=Navigation(1, 10, [EdgeType.delete])))
def test_simple_query() -> None: a = (Query.by("ec2", P("cpu") > 4, (P("mem") < 23) | (P("mem") < 59)).traverse_out().filter( P("some.int.value") < 1, P("some.other") == 23).traverse_out().filter( P("active") == 12, P.function("in_subnet").on("ip", "1.2.3.4/32"))) assert (str(a) == '((is("ec2") and cpu > 4) and (mem < 23 or mem < 59)) -default-> ' "(some.int.value < 1 and some.other == 23) -default-> " '(active == 12 and in_subnet(ip, "1.2.3.4/32"))')
async def test_query_graph(filled_graph_db: ArangoGraphDB, foo_model: Model) -> None: graph = await load_graph(filled_graph_db, foo_model) assert len(graph.edges) == 110 assert len(graph.nodes.values()) == 111 # filter data and tag result, and then traverse to the end of the graph in both directions around_me = Query.by( "foo", P("identifier") == "9").tag("red").traverse_inout(start=0) graph = await filled_graph_db.search_graph( QueryModel(around_me.on_section("reported"), foo_model)) assert len({x for x in graph.nodes}) == 12 assert GraphAccess.root_id(graph) == "sub_root" assert list(graph.successors("sub_root"))[0] == "9" assert set(graph.successors("9")) == {f"9_{x}" for x in range(0, 10)} for from_node, to_node, data in graph.edges.data(True): assert from_node == "9" or to_node == "9" assert data == {"edge_type": "default"} for node_id, node in graph.nodes.data(True): if node_id == "9": assert node["metadata"]["query_tag"] == "red" else: assert "tag" not in node["metadata"] async def assert_result(query: str, nodes: int, edges: int) -> None: q = parse_query(query) graph = await filled_graph_db.search_graph(QueryModel(q, foo_model)) assert len(graph.nodes) == nodes assert len(graph.edges) == edges await assert_result( "is(foo) and reported.identifier==9 <-delete[0:]default->", 11, 20) await assert_result( "is(foo) and reported.identifier==9 <-default[0:]delete->", 4, 3) await assert_result("is(foo) and reported.identifier==9 <-default[0:]->", 14, 13) await assert_result("is(foo) and reported.identifier==9 <-delete[0:]->", 11, 10) await assert_result("is(foo) and reported.identifier==9 -default[0:]->", 11, 10) await assert_result("is(foo) and reported.identifier==9 <-delete[0:]-", 11, 10) await assert_result("is(foo) and reported.identifier==9 <-default[0:]-", 4, 3) await assert_result("is(foo) and reported.identifier==9 -delete[0:]->", 1, 0)
def test_parse_predicate_array() -> None: assert_round_trip(predicate_term, P.array("mem").for_any() < 23) assert_round_trip(predicate_term, P.array("mem").for_all() >= 23) assert_round_trip(predicate_term, P.array("mem").for_none().matches("foo.*")) assert_round_trip(predicate_term, P.array("num").for_any().is_in([1, 2, 5])) assert_round_trip(predicate_term, P.array("num").for_all().is_in([1, 2, 5])) assert_round_trip(predicate_term, P.array("num").for_none().is_in([1, 2, 5]))
def test_with_clause() -> None: predicate_term.parse("foo == bla") wc: WithClause = with_clause_parser.parse( "with(empty, -delete-> foo == bla and test > 23 with(any, -delete->))") assert wc.with_filter == WithClauseFilter("==", 0) assert wc.navigation == Navigation(maybe_edge_types=["delete"]) assert str(wc.term) == '(foo == "bla" and test > 23)' assert str(wc.with_clause) == "with(any, -delete->)" term = Query.mk_term("foo", P("test") == 23) clause_filter = WithClauseFilter(">", 23) nav = Navigation() def edge(wc: WithClause) -> WithClause: wcr = replace(wc, with_clause=edge( wc.with_clause)) if wc.with_clause else wc return replace(wcr, navigation=replace(wcr.navigation, maybe_edge_types=[EdgeType.default])) assert_round_trip( with_clause_parser, WithClause(clause_filter, nav, term, WithClause(clause_filter, nav)), edge) assert_round_trip(with_clause_parser, WithClause(clause_filter, nav), edge)
def test_query() -> None: query = (Query.by( "ec2", P("cpu") > 4, (P("mem") < 23) | (P("mem") < 59), preamble={ "merge_with_ancestors": "cloud" }).traverse_out().filter( P("some.int.value") < 1, P("some.other") == 23).traverse_out().filter( P("active") == 12, P.function("in_subnet").on("ip", "1.2.3.4/96")).filter_with( WithClause(WithClauseFilter( "==", 0), Navigation())).group_by( [AggregateVariable(AggregateVariableName("foo"))], [AggregateFunction("sum", "cpu")]).add_sort( "test", "asc").with_limit(10)) assert ( str(query) == 'aggregate(foo: sum(cpu))(merge_with_ancestors="cloud"):' + '((is("ec2") and cpu > 4) and (mem < 23 or mem < 59)) -default-> ' + "(some.int.value < 1 and some.other == 23) -default-> " + '(active == 12 and in_subnet(ip, "1.2.3.4/96")) ' + "with(empty, -default->) sort test asc limit 10") assert_round_trip(query_parser, query)
def test_marshal_query() -> None: q = Query.by("ec2", P("foo") > 23, P("test") >= "bummer", P("das") < "set") again = parse_query(str(q)) assert str(q) == str(again)
def test_kind() -> None: assert_round_trip(is_term, P.of_kind("foo"))
def test_combined() -> None: assert_round_trip(combined_term, P.of_kind("foo") | P.of_kind("bla"))
def simple_reference() -> None: # only kind Query.by("ec2") # equality Query.by(P.of_kind("ec2") & (P("simple") == "hallo")) Query.by(P.of_kind("ec2") & (P("simple") != "hallo")) # regex Query.by(P.of_kind("ec2") & P("simple").matches("^some.regex[a-d]+$")) Query.by(P.of_kind("ec2") & P("simple").not_matches("^some.regex[a-d]+$")) # comparator Query.by(P.of_kind("ec2") & (P("num") > 23)) Query.by(P.of_kind("ec2") & (P("num") >= 23)) Query.by(P.of_kind("ec2") & (P("num") == 23)) Query.by(P.of_kind("ec2") & (P("num") <= 23)) Query.by(P.of_kind("ec2") & (P("num") < 23)) # in set Query.by(P.of_kind("ec2") & P("num").is_in([1, 2, 5])) Query.by(P.of_kind("ec2") & P("num").is_not_in([1, 2, 5])) # array: all above operators are available Query.by(P.of_kind("ec2") & (P.array("some.array").for_all() > 12.23)) Query.by( P.of_kind("ec2") & (P.array("some.array").for_any().is_in([1, 2, 3]))) Query.by(P.of_kind("ec2") & (P.array("some.array").for_none() == 5)) # call a function Query.by(P.function("in_subnet").on("ip", "1.2.3.4/16")) # refine with multiple predicates (all predicates have to match) Query.by( P.of_kind("ec2") & P("a").ge(1), P("b") == 2, P("c").matches("aaa"))
def test_parse_predicate() -> None: assert_round_trip(predicate_term, P("mem") < 23) assert_round_trip(predicate_term, P("simple") == "hallo") assert_round_trip(predicate_term, P("simple") != "hallo") assert_round_trip(predicate_term, P("simple").matches("^some.regex[a-d]+$")) assert_round_trip(predicate_term, P("simple").not_matches("^some.regex[a-d]+$")) assert_round_trip(predicate_term, P("num") > 23) assert_round_trip(predicate_term, P("num") >= 23) assert_round_trip(predicate_term, P("num") == 23) assert_round_trip(predicate_term, P("num") <= 23) assert_round_trip(predicate_term, P("num") < 23) assert_round_trip(predicate_term, P("num").is_in([1, 2, 5])) assert_round_trip(predicate_term, P("num").is_not_in([1, 2, 5]))