import json
import pytest
from silk import GraphStore
ONTOLOGY = json.dumps(
{
"node_types": {
"entity": {"properties": {"status": {"value_type": "string", "required": False}}},
"signal": {"properties": {}},
"source": {"properties": {}},
"processor": {"properties": {}},
"queue": {"properties": {}},
"sink": {"properties": {}},
},
"edge_types": {
"DEPENDS_ON": {
"source_types": ["entity"],
"target_types": ["entity"],
"properties": {},
},
"FEEDS": {
"source_types": ["source"],
"target_types": ["processor"],
"properties": {},
},
"ROUTES": {
"source_types": ["processor", "queue", "sink"],
"target_types": ["queue", "sink", "source"],
"properties": {},
},
},
}
)
class TestGraphQueries:
def test_get_node(self):
store = GraphStore("inst-1", ONTOLOGY)
store.add_node("s1", "entity", "Server 1", {"status": "alive"})
node = store.get_node("s1")
assert node is not None
assert node["node_id"] == "s1"
assert node["node_type"] == "entity"
assert node["label"] == "Server 1"
assert node["properties"]["status"] == "alive"
def test_get_node_not_found(self):
store = GraphStore("inst-1", ONTOLOGY)
assert store.get_node("nonexistent") is None
def test_get_edge(self):
store = GraphStore("inst-1", ONTOLOGY)
store.add_node("a", "entity", "A")
store.add_node("b", "entity", "B")
store.add_edge("e1", "DEPENDS_ON", "a", "b")
edge = store.get_edge("e1")
assert edge is not None
assert edge["edge_type"] == "DEPENDS_ON"
assert edge["source_id"] == "a"
assert edge["target_id"] == "b"
def test_query_by_type(self):
store = GraphStore("inst-1", ONTOLOGY)
store.add_node("s1", "entity", "S1")
store.add_node("s2", "entity", "S2")
store.add_node("src1", "source", "Source 1")
entities = store.query_nodes_by_type("entity")
assert len(entities) == 2
sources = store.query_nodes_by_type("source")
assert len(sources) == 1
def test_query_by_property(self):
store = GraphStore("inst-1", ONTOLOGY)
store.add_node("s1", "entity", "S1", {"status": "alive"})
store.add_node("s2", "entity", "S2", {"status": "dead"})
alive = store.query_nodes_by_property("status", "alive")
assert len(alive) == 1
assert alive[0]["node_id"] == "s1"
def test_all_nodes_excludes_removed(self):
store = GraphStore("inst-1", ONTOLOGY)
store.add_node("s1", "entity", "S1")
store.add_node("s2", "entity", "S2")
assert len(store.all_nodes()) == 2
store.remove_node("s1")
assert len(store.all_nodes()) == 1
def test_all_edges_excludes_dangling(self):
store = GraphStore("inst-1", ONTOLOGY)
store.add_node("a", "entity", "A")
store.add_node("b", "entity", "B")
store.add_edge("e1", "DEPENDS_ON", "a", "b")
assert len(store.all_edges()) == 1
store.remove_node("b")
assert len(store.all_edges()) == 0
def test_neighbors(self):
store = GraphStore("inst-1", ONTOLOGY)
store.add_node("a", "entity", "A")
store.add_node("b", "entity", "B")
store.add_node("c", "entity", "C")
store.add_edge("ab", "DEPENDS_ON", "a", "b")
store.add_edge("ac", "DEPENDS_ON", "a", "c")
neighbors = store.neighbors("a")
assert set(neighbors) == {"b", "c"}
def test_outgoing_incoming_edges(self):
store = GraphStore("inst-1", ONTOLOGY)
store.add_node("a", "entity", "A")
store.add_node("b", "entity", "B")
store.add_edge("e1", "DEPENDS_ON", "a", "b")
out = store.outgoing_edges("a")
assert len(out) == 1
assert out[0]["target_id"] == "b"
inc = store.incoming_edges("b")
assert len(inc) == 1
assert inc[0]["source_id"] == "a"
class TestEngine:
def _build_chain(self):
store = GraphStore("inst-1", ONTOLOGY)
store.add_node("a", "entity", "A")
store.add_node("b", "entity", "B")
store.add_node("c", "entity", "C")
store.add_node("d", "entity", "D")
store.add_edge("ab", "DEPENDS_ON", "a", "b")
store.add_edge("bc", "DEPENDS_ON", "b", "c")
store.add_edge("cd", "DEPENDS_ON", "c", "d")
return store
def test_bfs(self):
store = self._build_chain()
visited = store.bfs("a")
assert visited == ["a", "b", "c", "d"]
def test_bfs_depth_limit(self):
store = self._build_chain()
visited = store.bfs("a", max_depth=2)
assert visited == ["a", "b", "c"]
def test_bfs_edge_type_filter(self):
store = self._build_chain()
visited = store.bfs("a", edge_type="NONEXISTENT")
assert visited == ["a"]
def test_shortest_path(self):
store = self._build_chain()
path = store.shortest_path("a", "d")
assert path == ["a", "b", "c", "d"]
def test_shortest_path_no_path(self):
store = GraphStore("inst-1", ONTOLOGY)
store.add_node("a", "entity", "A")
store.add_node("b", "entity", "B")
assert store.shortest_path("a", "b") is None
def test_impact_analysis(self):
store = self._build_chain()
impact = store.impact_analysis("d")
assert set(impact) == {"a", "b", "c", "d"}
def test_subgraph(self):
store = self._build_chain()
result = store.subgraph("b", 1)
assert "b" in result["nodes"]
assert "a" in result["nodes"]
assert "c" in result["nodes"]
assert "d" not in result["nodes"]
def test_pattern_match(self):
store = GraphStore("inst-1", ONTOLOGY)
store.add_node("src1", "source", "Data Source")
store.add_node("proc1", "processor", "Processor 1")
store.add_node("q1", "queue", "Queue 1")
store.add_node("snk1", "sink", "Sink 1")
store.add_edge("e1", "FEEDS", "src1", "proc1")
store.add_edge("e2", "ROUTES", "proc1", "q1")
store.add_edge("e3", "ROUTES", "q1", "snk1")
chains = store.pattern_match(["source", "processor", "queue", "sink"])
assert len(chains) == 1
assert chains[0] == ["src1", "proc1", "q1", "snk1"]
def test_topological_sort(self):
store = self._build_chain()
order = store.topological_sort()
assert order is not None
assert order.index("a") < order.index("b")
assert order.index("b") < order.index("c")
assert order.index("c") < order.index("d")
def test_cycle_detection(self):
store = GraphStore("inst-1", ONTOLOGY)
store.add_node("a", "entity", "A")
store.add_node("b", "entity", "B")
store.add_node("c", "entity", "C")
store.add_edge("ab", "DEPENDS_ON", "a", "b")
store.add_edge("bc", "DEPENDS_ON", "b", "c")
store.add_edge("ca", "DEPENDS_ON", "c", "a")
assert store.has_cycle() is True
assert store.topological_sort() is None
def test_no_cycle(self):
store = self._build_chain()
assert store.has_cycle() is False