import pytest
from silk import GraphStore
def _hierarchy_store():
return GraphStore("test", {
"node_types": {
"thing": {
"properties": {
"name": {"value_type": "string", "required": True}
}
},
"entity": {
"parent_type": "thing",
"properties": {
"status": {"value_type": "string"}
}
},
"server": {
"parent_type": "entity",
"properties": {
"ip": {"value_type": "string"}
}
},
"event": {
"parent_type": "thing",
"properties": {}
},
},
"edge_types": {
"RELATES_TO": {
"source_types": ["thing"],
"target_types": ["entity"],
"properties": {}
}
}
})
def test_child_inherits_parent_required():
store = _hierarchy_store()
with pytest.raises(ValueError, match="requires property"):
store.add_node("s1", "server", "S1")
def test_child_with_inherited_property():
store = _hierarchy_store()
store.add_node("s1", "server", "S1", {"name": "web-01"})
assert store.get_node("s1")["properties"]["name"] == "web-01"
def test_child_can_use_own_and_inherited():
store = _hierarchy_store()
store.add_node("s1", "server", "S1", {
"name": "web-01", "status": "active", "ip": "10.0.0.1"
})
node = store.get_node("s1")
assert node["properties"]["name"] == "web-01"
assert node["properties"]["status"] == "active"
assert node["properties"]["ip"] == "10.0.0.1"
def test_query_parent_returns_children():
store = _hierarchy_store()
store.add_node("t1", "thing", "T", {"name": "root"})
store.add_node("e1", "entity", "E", {"name": "ent"})
store.add_node("s1", "server", "S", {"name": "srv"})
store.add_node("ev1", "event", "Ev", {"name": "evt"})
things = store.query_nodes_by_type("thing")
ids = {n["node_id"] for n in things}
assert ids == {"t1", "e1", "s1", "ev1"}
def test_query_mid_level_returns_descendants():
store = _hierarchy_store()
store.add_node("e1", "entity", "E", {"name": "ent"})
store.add_node("s1", "server", "S", {"name": "srv"})
store.add_node("ev1", "event", "Ev", {"name": "evt"})
entities = store.query_nodes_by_type("entity")
ids = {n["node_id"] for n in entities}
assert ids == {"e1", "s1"}
def test_query_leaf_returns_only_self():
store = _hierarchy_store()
store.add_node("s1", "server", "S", {"name": "srv"})
store.add_node("e1", "entity", "E", {"name": "ent"})
servers = store.query_nodes_by_type("server")
assert len(servers) == 1
assert servers[0]["node_id"] == "s1"
def test_edge_accepts_descendant_as_source():
store = _hierarchy_store()
store.add_node("s1", "server", "S", {"name": "srv"})
store.add_node("e1", "entity", "E", {"name": "ent"})
store.add_edge("edge1", "RELATES_TO", "s1", "e1")
assert store.get_edge("edge1") is not None
def test_edge_accepts_descendant_as_target():
store = _hierarchy_store()
store.add_node("t1", "thing", "T", {"name": "root"})
store.add_node("s1", "server", "S", {"name": "srv"})
store.add_edge("edge1", "RELATES_TO", "t1", "s1")
assert store.get_edge("edge1") is not None
def test_edge_rejects_wrong_branch():
store = _hierarchy_store()
store.add_node("t1", "thing", "T", {"name": "root"})
store.add_node("ev1", "event", "Ev", {"name": "evt"})
with pytest.raises(ValueError, match="cannot have target type"):
store.add_edge("edge1", "RELATES_TO", "t1", "ev1")
def test_no_parent_type_works_as_before():
store = GraphStore("test", {
"node_types": {
"widget": {"properties": {"color": {"value_type": "string"}}}
},
"edge_types": {}
})
store.add_node("w1", "widget", "W", {"color": "red"})
assert store.get_node("w1") is not None
def test_parent_type_survives_sync():
a = _hierarchy_store()
a.add_node("s1", "server", "S", {"name": "srv"})
a.add_node("e1", "entity", "E", {"name": "ent"})
snap = a.snapshot()
b = GraphStore.from_snapshot("inst-b", snap)
things = b.query_nodes_by_type("thing")
ids = {n["node_id"] for n in things}
assert "s1" in ids
assert "e1" in ids
def test_dangling_parent_rejected():
with pytest.raises(ValueError):
GraphStore("test", {
"node_types": {
"orphan": {
"parent_type": "ghost",
"properties": {}
}
},
"edge_types": {}
})