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_navigation() -> None: for edge_type in EdgeType.all: # the default edge type is not rendered, so we set it explicitly to make the mapping homogeneous for start, until in [(0, 0), (1, 1), (5, 5), (1, 10), (1, Navigation.Max), (10, Navigation.Max)]: assert_round_trip( navigation_parser, Navigation(start, until, [edge_type], Direction.any, [edge_type])) for direction in Direction.all: assert_round_trip( navigation_parser, Navigation(start, until, [edge_type], direction))
def test_merge_query_creation() -> None: inbound = Navigation(1, Navigation.Max, direction=Direction.inbound) for_foo = Query( [Part(IsTerm(["foo"])), Part(AllTerm(), navigation=inbound)]) merge_foo = [MergeQuery("ancestors.foo", for_foo)] # merge_foo is created automatically assert Part(AllTerm()).merge_queries_for(["ancestors.foo.reported.bla" ]) == merge_foo # merge_foo is already included and not added assert Part(MergeTerm(AllTerm(), merge_foo)).merge_queries_for( ["ancestors.foo.reported.bla"]) == merge_foo # neither ancestors/descendants with pytest.raises(Exception): Part(AllTerm()).merge_queries_for(["unknown.foo.reported.bla"]) # no path is given with pytest.raises(Exception): Part(AllTerm()).merge_queries_for(["ancestors.foo"]) # rewrite for ancestors/descendants also work with additional properties assert (str( Query.by("test").rewrite_for_ancestors_descendants([ "ancestors.kind.reported.prop", "test", "a" ])) == 'is("test") {ancestors.kind: all <-default[1:]- is("kind")}') assert ( str( Query.by("test").merge_with( "ancestors.cloud", NavigateUntilRoot, IsTerm(["cloud"])).rewrite_for_ancestors_descendants( ["ancestors.kind.reported.prop", "test", "a"])) == 'is("test") {ancestors.kind: all <-default[1:]- is("kind"), ancestors.cloud: all <-default[1:]- is("cloud")}' )
def navigation(ud: UD) -> Navigation: d = Drawer(ud) start = d.draw(integers(min_value=0, max_value=1)) length = d.draw(integers(min_value=0, max_value=100)) ed = d.draw(edge_type) direction = d.draw(edge_direction) return Navigation(start, length + start, [ed], direction)
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_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)
if edge_types and after_bracket_edge_types: raise AttributeError("Edge types can not be defined before and after the [start,until] definition.") return start, until, edge_types or after_bracket_edge_types @make_parser def two_directional_edge_definition_parser() -> Parser: edge_types = yield edge_type_parser maybe_range = yield range_parser.optional() outbound_edge_types = yield edge_type_parser start, until = maybe_range if maybe_range else (1, 1) return start, until, edge_types, outbound_edge_types out_p = lexeme(string("-") >> edge_definition_parser << string("->")).map( lambda nav: Navigation(nav[0], nav[1], nav[2], Direction.outbound) ) in_p = lexeme(string("<-") >> edge_definition_parser << string("-")).map( lambda nav: Navigation(nav[0], nav[1], nav[2], Direction.inbound) ) in_out_p = lexeme(string("<-") >> two_directional_edge_definition_parser << string("->")).map( lambda nav: Navigation(nav[0], nav[1], nav[2], Direction.any, nav[3]) ) navigation_parser = in_out_p | out_p | in_p tag_parser = lexeme(string("#") >> literal_p).optional() with_p = lexeme(string("with")) count_p = lexeme(string("count")) len_empty = lexeme(string("empty")).result(WithClauseFilter("==", 0)) len_any = lexeme(string("any")).result(WithClauseFilter(">", 0))
def test_navigation_default() -> None: assert str(Navigation(1, 1)) == "-default->" assert str(Navigation(1, Navigation.Max)) == "-default[1:]->"