-
Notifications
You must be signed in to change notification settings - Fork 109
Expand file tree
/
Copy pathtest_get_lineage.py
More file actions
206 lines (169 loc) · 5.99 KB
/
test_get_lineage.py
File metadata and controls
206 lines (169 loc) · 5.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
import json
from unittest.mock import AsyncMock, Mock
import pytest
from mcp.types import CallToolResult, TextContent
from dbt_mcp.discovery.tools import (
DiscoveryToolContext,
get_lineage as get_lineage_tool,
)
# Access the underlying function from the ToolDefinition
get_lineage = get_lineage_tool.fn
@pytest.fixture
def mock_discovery_tool_context():
"""Mock DiscoveryToolContext for testing."""
context = Mock(spec=DiscoveryToolContext)
context.lineage_fetcher = AsyncMock()
return context
SAMPLE_NODES = [
{
"uniqueId": "source.test.raw_customers",
"name": "raw_customers",
"resourceType": "Source",
"parentIds": [],
},
{
"uniqueId": "model.test.customers",
"name": "customers",
"resourceType": "Model",
"parentIds": ["source.test.raw_customers"],
},
{
"uniqueId": "model.test.customer_metrics",
"name": "customer_metrics",
"resourceType": "Model",
"parentIds": ["model.test.customers"],
},
]
async def test_get_lineage_returns_call_tool_result(mock_discovery_tool_context):
"""Test that get_lineage returns a CallToolResult."""
mock_discovery_tool_context.lineage_fetcher.fetch_lineage.return_value = (
SAMPLE_NODES
)
result = await get_lineage(
context=mock_discovery_tool_context,
unique_id="model.test.customers",
types=None,
depth=5,
)
assert isinstance(result, CallToolResult)
async def test_get_lineage_text_content_contains_raw_nodes(mock_discovery_tool_context):
"""Test that the text content contains the raw node data as JSON."""
mock_discovery_tool_context.lineage_fetcher.fetch_lineage.return_value = (
SAMPLE_NODES
)
result = await get_lineage(
context=mock_discovery_tool_context,
unique_id="model.test.customers",
types=None,
depth=5,
)
assert len(result.content) == 1
assert isinstance(result.content[0], TextContent)
parsed = json.loads(result.content[0].text)
assert parsed == SAMPLE_NODES
async def test_get_lineage_structured_content_has_correct_graph(
mock_discovery_tool_context,
):
"""Test that structuredContent contains a well-formed LineageGraph."""
mock_discovery_tool_context.lineage_fetcher.fetch_lineage.return_value = (
SAMPLE_NODES
)
result = await get_lineage(
context=mock_discovery_tool_context,
unique_id="model.test.customers",
types=None,
depth=5,
)
sc = result.structuredContent
assert sc["type"] == "lineage_graph"
assert sc["root_id"] == "model.test.customers"
assert len(sc["nodes"]) == 3
assert len(sc["edges"]) == 2
node_ids = {n["unique_id"] for n in sc["nodes"]}
assert node_ids == {
"source.test.raw_customers",
"model.test.customers",
"model.test.customer_metrics",
}
edges = {(e["source"], e["target"]) for e in sc["edges"]}
assert edges == {
("source.test.raw_customers", "model.test.customers"),
("model.test.customers", "model.test.customer_metrics"),
}
async def test_get_lineage_filters_edges_to_known_nodes(mock_discovery_tool_context):
"""Test that edges referencing nodes outside the graph are excluded."""
nodes_with_external_parent = [
{
"uniqueId": "model.test.customers",
"name": "customers",
"resourceType": "Model",
"parentIds": ["source.test.raw_customers", "model.other.unknown"],
},
{
"uniqueId": "source.test.raw_customers",
"name": "raw_customers",
"resourceType": "Source",
"parentIds": [],
},
]
mock_discovery_tool_context.lineage_fetcher.fetch_lineage.return_value = (
nodes_with_external_parent
)
result = await get_lineage(
context=mock_discovery_tool_context,
unique_id="model.test.customers",
types=None,
depth=5,
)
sc = result.structuredContent
# Only the edge from raw_customers should exist; model.other.unknown is not in the graph
assert len(sc["edges"]) == 1
assert sc["edges"][0]["source"] == "source.test.raw_customers"
assert sc["edges"][0]["target"] == "model.test.customers"
async def test_get_lineage_empty_result(mock_discovery_tool_context):
"""Test that an empty fetcher result produces an empty graph."""
mock_discovery_tool_context.lineage_fetcher.fetch_lineage.return_value = []
result = await get_lineage(
context=mock_discovery_tool_context,
unique_id="model.test.nonexistent",
types=None,
depth=5,
)
sc = result.structuredContent
assert sc["nodes"] == []
assert sc["edges"] == []
assert sc["root_id"] == "model.test.nonexistent"
async def test_get_lineage_passes_parameters_to_fetcher(mock_discovery_tool_context):
"""Test that parameters are correctly forwarded to the fetcher."""
mock_discovery_tool_context.lineage_fetcher.fetch_lineage.return_value = []
await get_lineage(
context=mock_discovery_tool_context,
unique_id="model.test.customers",
types=["Model", "Source"],
depth=3,
)
mock_discovery_tool_context.lineage_fetcher.fetch_lineage.assert_called_once_with(
unique_id="model.test.customers",
types=["Model", "Source"],
depth=3,
)
async def test_get_lineage_node_without_parent_ids(mock_discovery_tool_context):
"""Test handling of nodes that lack a parentIds field."""
nodes = [
{
"uniqueId": "model.test.orphan",
"name": "orphan",
"resourceType": "Model",
# no parentIds key
},
]
mock_discovery_tool_context.lineage_fetcher.fetch_lineage.return_value = nodes
result = await get_lineage(
context=mock_discovery_tool_context,
unique_id="model.test.orphan",
types=None,
depth=5,
)
sc = result.structuredContent
assert len(sc["nodes"]) == 1
assert sc["edges"] == []