Пример #1
0
def test_not_visited(graph_access: GraphAccess) -> None:
    graph_access.node("1")
    graph_access.node("3")
    not_visited = list(graph_access.not_visited_nodes())
    assert len(not_visited) == 2
    assert not_visited[0]["hash"] == "0546994698f57ed4a7a4463b0353397ebe4acc7196b9e9ca1dd7bc0b72245be6"
    assert not_visited[1]["hash"] == "0fead7dd5aff45049878e4c1b108b78c5fab594ac503eaba2199b71725241fce"
Пример #2
0
def test_reassign_root(person_model: Model) -> None:
    max_m = {"id": "max", "kind": "Person", "name": "Max"}
    builder = GraphBuilder(person_model)
    builder.add_from_json({"id": "should_be_root", "reported": {"kind": "graph_root"}})
    builder.add_from_json({"id": "2", "reported": max_m})
    builder.add_from_json({"id": "3", "reported": max_m})
    builder.add_from_json({"from": "should_be_root", "to": "2"})
    builder.add_from_json({"from": "should_be_root", "to": "3"})
    builder.check_complete()
    access = GraphAccess(builder.graph)
    assert access.root() == "root"
    assert set(access.successors("root", EdgeType.default)) == {"2", "3"}
Пример #3
0
def test_predecessors() -> None:
    graph = GraphAccess(multi_cloud_graph("account"))
    child = "child_parent_region_account_cloud_gcp_2_europe_1_0"
    parent = "parent_region_account_cloud_gcp_2_europe_1"
    region = "region_account_cloud_gcp_2_europe"

    # default: region -> parent -> child
    assert list(graph.predecessors(child, EdgeType.default)) == [parent]
    assert list(graph.predecessors(parent, EdgeType.default)) == [region]
    assert child in list(graph.successors(parent, EdgeType.default))
    assert parent in list(graph.successors(region, EdgeType.default))

    # delete: child -> parent -> region
    assert list(graph.successors(child, EdgeType.delete)) == [parent]
    assert list(graph.successors(parent, EdgeType.delete)) == [region]
    assert parent in list(graph.successors(child, EdgeType.delete))
    assert region in list(graph.successors(parent, EdgeType.delete))
Пример #4
0
def test_content_hash() -> None:
    # the order of properties should not matter for the content hash
    g = MultiDiGraph()
    g.add_node("1", reported={"a": {"a": 1, "c": 2, "b": 3}, "c": 2, "b": 3, "d": "foo", "z": True, "kind": "a"})
    g.add_node("2", reported={"z": True, "c": 2, "b": 3, "a": {"b": 3, "c": 2, "a": 1}, "d": "foo", "kind": "a"})

    access = GraphAccess(g)
    sha1 = node(access, "1")["hash"]  # type: ignore
    sha2 = node(access, "2")["hash"]  # type: ignore
    assert sha1 == sha2
Пример #5
0
def test_ancestor_of() -> None:
    nid1 = "child_parent_region_account_cloud_gcp_1_europe_1_0"
    acc1 = "account_cloud_gcp_1"
    acc2 = "account_cloud_gcp_2"
    g = multi_cloud_graph("account")

    graph = GraphAccess(g)
    assert graph.ancestor_of(nid1, EdgeType.default, "root") is not None
    assert graph.ancestor_of(nid1, EdgeType.delete, "root") is None
    assert graph.ancestor_of(nid1, EdgeType.default, "foo") is None
    assert graph.ancestor_of(nid1, EdgeType.default, "foo") is None
    assert graph.ancestor_of(nid1, EdgeType.default, "account")["id"] == acc1  # type: ignore

    # add another "shorter" edge from acc2 -> nid1, so it is shorter that from acc1 -> nid1
    key = GraphAccess.edge_key(acc2, nid1, EdgeType.default)
    g.add_edge(acc2, nid1, key, edge_type=EdgeType.default)
    assert graph.ancestor_of(nid1, EdgeType.default, "account")["id"] == acc2  # type: ignore
Пример #6
0
def test_sub_graphs_from_graph_account() -> None:
    graph = multi_cloud_graph("account")
    merges, parent, graph_it = GraphAccess.merge_graphs(graph)
    graphs = list(graph_it)
    assert len(graphs) == 6
    for root, succ in graphs:
        assert len(parent.nodes) == 9
        assert succ.root().startswith("account")
        assert len(list(succ.not_visited_nodes())) == 78
        assert len(succ.nodes) == 79
        # make sure there is no node from another subgraph
        for node_id in succ.not_visited_nodes():
            assert succ.root() in node_id["id"]
        assert len(list(succ.not_visited_edges(EdgeType.default))) == 78
        assert len(list(succ.not_visited_edges(EdgeType.delete))) == 78
Пример #7
0
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)
Пример #8
0
def test_replace_nodes(person_model: Model) -> None:
    builder = GraphBuilder(person_model)
    meta = {"metadata": {"replace": True}}
    builder.add_from_json({"id": "root", "reported": {"kind": "graph_root"}})
    builder.add_from_json({"id": "cloud", "reported": {"id": "cloud", "kind": "cloud"}, **meta})
    # also mark account and region as replace node -> the flags should be ignored!
    builder.add_from_json({"id": "account", "reported": {"id": "account", "kind": "account"}, **meta})
    builder.add_from_json({"id": "region", "reported": {"id": "region", "kind": "region"}, **meta})
    builder.add_from_json({"from": "root", "to": "cloud"})
    builder.add_from_json({"from": "cloud", "to": "account"})
    builder.add_from_json({"from": "account", "to": "region"})
    roots, _, gen = GraphAccess.merge_graphs(builder.graph)
    assert roots == ["cloud"]
    cloud, access = list(gen)[0]
    assert cloud == "cloud"
    assert set(access.nodes) == {"cloud", "account", "region"}
Пример #9
0
def test_access_node() -> None:
    g = MultiDiGraph()
    g.add_node("1", reported=to_json(FooTuple(a="1")))
    access: GraphAccess = GraphAccess(g)
    elem: Json = node(access, "1")  # type: ignore
    assert elem["hash"] == "153c1a5c002f6213a95383f33b63aa18b8ed6939f57418fb0f27312576f0cea4"
    assert elem["reported"] == {
        "a": "1",
        "b": 0,
        "c": [],
        "d": "foo",
        "e": {"a": 12, "b": 32},
        "f": "2021-03-29",
        "g": 1.234567,
        "kind": "foo",
    }
    assert access.node("2") is None
Пример #10
0
def graph_access() -> GraphAccess:
    g = MultiDiGraph()

    def add_edge(from_node: str, to_node: str, edge_type: str) -> None:
        key = GraphAccess.edge_key(from_node, to_node, edge_type)
        g.add_edge(from_node, to_node, key, edge_type=edge_type)

    g.add_node("1", reported=to_json(FooTuple("1")), desired={"name": "a"}, metadata={"version": 1}, kinds=["foo"])
    g.add_node("2", reported=to_json(FooTuple("2")), desired={"name": "b"}, metadata={"version": 2}, kinds=["foo"])
    g.add_node("3", reported=to_json(FooTuple("3")), desired={"name": "c"}, metadata={"version": 3}, kinds=["foo"])
    g.add_node("4", reported=to_json(FooTuple("4")), desired={"name": "d"}, metadata={"version": 4}, kinds=["foo"])
    add_edge("1", "2", edge_type=EdgeType.default)
    add_edge("1", "3", edge_type=EdgeType.default)
    add_edge("2", "3", edge_type=EdgeType.default)
    add_edge("2", "4", edge_type=EdgeType.default)
    add_edge("3", "4", edge_type=EdgeType.default)
    add_edge("1", "2", edge_type=EdgeType.delete)
    add_edge("1", "3", edge_type=EdgeType.delete)
    add_edge("1", "4", edge_type=EdgeType.delete)
    return GraphAccess(g)
Пример #11
0
def test_resolve_graph_data() -> None:
    g = multi_cloud_graph("account")
    graph = GraphAccess(g)
    graph.resolve()

    # ancestor data should be stored in metadata
    n1 = AccessJson(graph.node("child_parent_region_account_cloud_gcp_1_europe_1_0"))  # type: ignore
    assert n1.refs.region_id == "region_account_cloud_gcp_1_europe"
    assert n1.ancestors.account.reported.id == "id_account_cloud_gcp_1"
    assert n1.ancestors.account.reported.name == "name_account_cloud_gcp_1"
    assert n1.ancestors.region.reported.id == "id_region_account_cloud_gcp_1_europe"
    assert n1.ancestors.region.reported.name == "name_region_account_cloud_gcp_1_europe"
    # make sure there is no summary
    assert n1.descendant_summary == AccessNone(None)

    r1 = AccessJson(graph.node("region_account_cloud_gcp_1_europe"))  # type: ignore
    assert r1.metadata.descendant_summary == {"child": 9}
    assert r1.metadata.descendant_count == 9
    r2 = AccessJson(graph.node("account_cloud_gcp_1"))  # type: ignore
    assert r2.metadata.descendant_summary == {"child": 54, "region": 6}
    assert r2.metadata.descendant_count == 60
    r3 = AccessJson(graph.node("cloud_gcp"))  # type: ignore
    assert r3.metadata.descendant_summary == {"child": 162, "region": 18, "account": 3}
    assert r3.metadata.descendant_count == 183
Пример #12
0
def test_root(graph_access: GraphAccess) -> None:
    assert graph_access.root() == "1"
Пример #13
0
 def add_edge(from_node: str, to_node: str, edge_type: str) -> None:
     key = GraphAccess.edge_key(from_node, to_node, edge_type)
     g.add_edge(from_node, to_node, key, edge_type=edge_type)
Пример #14
0
def test_desired(graph_access: GraphAccess) -> None:
    desired = {a["id"]: a["desired"] for a in graph_access.not_visited_nodes()}
    assert desired == {"1": {"name": "a"}, "2": {"name": "b"}, "3": {"name": "c"}, "4": {"name": "d"}}
Пример #15
0
def test_acyclic() -> None:
    assert GraphAccess(cyclic_multi_graph(acyclic=False)).is_acyclic_per_edge_type() is False
    assert GraphAccess(cyclic_multi_graph(acyclic=True)).is_acyclic_per_edge_type() is True
Пример #16
0
def test_metadata(graph_access: GraphAccess) -> None:
    desired = {a["id"]: a["metadata"] for a in graph_access.not_visited_nodes()}
    assert desired == {"1": {"version": 1}, "2": {"version": 2}, "3": {"version": 3}, "4": {"version": 4}}
Пример #17
0
def node(access: GraphAccess, node_id: str) -> Optional[Json]:
    res = access.node(node_id)
    if res:
        return res
    else:
        raise AttributeError(f"Expected {node_id} to be defined!")
Пример #18
0
 def add_edge(from_node: str,
              to_node: str,
              edge_type: str = EdgeType.default) -> None:
     key = GraphAccess.edge_key(from_node, to_node, edge_type)
     graph.add_edge(from_node, to_node, key, edge_type=edge_type)
Пример #19
0
def test_edges(graph_access: GraphAccess) -> None:
    assert graph_access.has_edge("1", "2", EdgeType.default)
    assert not graph_access.has_edge("1", "9", EdgeType.default)
    assert graph_access.has_edge("2", "3", EdgeType.default)
    assert list(graph_access.not_visited_edges(EdgeType.default)) == [("1", "3"), ("2", "4"), ("3", "4")]
    assert list(graph_access.not_visited_edges(EdgeType.delete)) == [("1", "2"), ("1", "3"), ("1", "4")]