phlo-testing 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- phlo_testing/__init__.py +297 -0
- phlo_testing/conftest_template.py +65 -0
- phlo_testing/execution.py +405 -0
- phlo_testing/fixtures.py +447 -0
- phlo_testing/hooks.py +135 -0
- phlo_testing/local_mode.py +366 -0
- phlo_testing/mock_dlt.py +287 -0
- phlo_testing/mock_iceberg.py +435 -0
- phlo_testing/mock_trino.py +486 -0
- phlo_testing/placeholders.py +830 -0
- phlo_testing/utils.py +20 -0
- phlo_testing-0.1.0.dist-info/METADATA +22 -0
- phlo_testing-0.1.0.dist-info/RECORD +15 -0
- phlo_testing-0.1.0.dist-info/WHEEL +5 -0
- phlo_testing-0.1.0.dist-info/top_level.txt +1 -0
phlo_testing/fixtures.py
ADDED
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pytest fixtures for testing Phlo assets and workflows.
|
|
3
|
+
|
|
4
|
+
Provides reusable fixtures for common test scenarios including mock resources,
|
|
5
|
+
test data, and temporary directories.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> def test_my_asset(mock_iceberg_catalog, sample_partition_date):
|
|
9
|
+
... # Use fixtures automatically
|
|
10
|
+
... pass
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import tempfile
|
|
17
|
+
from datetime import datetime, timedelta
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any, Callable, Iterator
|
|
20
|
+
|
|
21
|
+
import pandas as pd
|
|
22
|
+
import pytest
|
|
23
|
+
|
|
24
|
+
from phlo_testing.execution import MockAssetContext
|
|
25
|
+
from phlo_testing.hooks import MockHookBus
|
|
26
|
+
from phlo_testing.conftest_template import get_conftest_template
|
|
27
|
+
from phlo_testing.mock_dlt import MockDLTResource, mock_dlt_source
|
|
28
|
+
from phlo_testing.mock_iceberg import MockIcebergCatalog
|
|
29
|
+
from phlo_testing.mock_trino import MockTrinoResource
|
|
30
|
+
|
|
31
|
+
# --- Mock Resource Fixtures ---
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@pytest.fixture
|
|
35
|
+
def mock_iceberg_catalog() -> Iterator[MockIcebergCatalog]:
|
|
36
|
+
"""
|
|
37
|
+
Provide a fresh MockIcebergCatalog for each test.
|
|
38
|
+
|
|
39
|
+
Fixture is function-scoped and auto-cleaned up after test.
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
>>> def test_with_catalog(mock_iceberg_catalog):
|
|
43
|
+
... table = mock_iceberg_catalog.create_table(
|
|
44
|
+
... "raw.users",
|
|
45
|
+
... schema=get_schema(),
|
|
46
|
+
... )
|
|
47
|
+
"""
|
|
48
|
+
catalog = MockIcebergCatalog()
|
|
49
|
+
yield catalog
|
|
50
|
+
catalog.close()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@pytest.fixture
|
|
54
|
+
def mock_trino() -> Iterator[MockTrinoResource]:
|
|
55
|
+
"""
|
|
56
|
+
Provide a fresh MockTrinoResource for each test.
|
|
57
|
+
|
|
58
|
+
Fixture is function-scoped and auto-cleaned up after test.
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
>>> def test_with_trino(mock_trino):
|
|
62
|
+
... cursor = mock_trino.cursor()
|
|
63
|
+
... cursor.execute("SELECT 1 as id")
|
|
64
|
+
"""
|
|
65
|
+
trino = MockTrinoResource()
|
|
66
|
+
yield trino
|
|
67
|
+
trino.close()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@pytest.fixture
|
|
71
|
+
def mock_asset_context() -> Iterator[MockAssetContext]:
|
|
72
|
+
"""
|
|
73
|
+
Provide a fresh MockAssetContext for each test.
|
|
74
|
+
|
|
75
|
+
Includes mock Iceberg and Trino resources plus logging capture.
|
|
76
|
+
|
|
77
|
+
Example:
|
|
78
|
+
>>> def test_with_context(mock_asset_context):
|
|
79
|
+
... context.log("test message")
|
|
80
|
+
... assert "test message" in context.logs
|
|
81
|
+
"""
|
|
82
|
+
context = MockAssetContext()
|
|
83
|
+
yield context
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@pytest.fixture
|
|
87
|
+
def mock_hook_bus() -> MockHookBus:
|
|
88
|
+
"""Provide a mock hook bus without plugin discovery."""
|
|
89
|
+
return MockHookBus()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@pytest.fixture
|
|
93
|
+
def mock_resources(
|
|
94
|
+
mock_iceberg_catalog: MockIcebergCatalog,
|
|
95
|
+
mock_trino: MockTrinoResource,
|
|
96
|
+
) -> dict[str, Any]:
|
|
97
|
+
"""
|
|
98
|
+
Provide all mock resources in a dict.
|
|
99
|
+
|
|
100
|
+
Useful for passing to functions that need multiple resources.
|
|
101
|
+
|
|
102
|
+
Example:
|
|
103
|
+
>>> def test_with_resources(mock_resources):
|
|
104
|
+
... iceberg = mock_resources["iceberg"]
|
|
105
|
+
... trino = mock_resources["trino"]
|
|
106
|
+
"""
|
|
107
|
+
return {
|
|
108
|
+
"iceberg": mock_iceberg_catalog,
|
|
109
|
+
"trino": mock_trino,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# --- Test Data Fixtures ---
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@pytest.fixture
|
|
117
|
+
def sample_partition_date() -> str:
|
|
118
|
+
"""
|
|
119
|
+
Provide a standard partition date for tests.
|
|
120
|
+
|
|
121
|
+
Returns ISO format date string (e.g., "2024-01-15").
|
|
122
|
+
|
|
123
|
+
Example:
|
|
124
|
+
>>> def test_asset(sample_partition_date):
|
|
125
|
+
... assert sample_partition_date == "2024-01-15"
|
|
126
|
+
"""
|
|
127
|
+
return "2024-01-15"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@pytest.fixture
|
|
131
|
+
def sample_partition_range() -> tuple[str, str]:
|
|
132
|
+
"""
|
|
133
|
+
Provide a range of partition dates.
|
|
134
|
+
|
|
135
|
+
Returns tuple of (start_date, end_date) in ISO format.
|
|
136
|
+
|
|
137
|
+
Example:
|
|
138
|
+
>>> def test_backfill(sample_partition_range):
|
|
139
|
+
... start, end = sample_partition_range
|
|
140
|
+
... # start = "2024-01-01"
|
|
141
|
+
... # end = "2024-01-31"
|
|
142
|
+
"""
|
|
143
|
+
start = "2024-01-01"
|
|
144
|
+
end = "2024-01-31"
|
|
145
|
+
return (start, end)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@pytest.fixture
|
|
149
|
+
def sample_dlt_data() -> list[dict[str, Any]]:
|
|
150
|
+
"""
|
|
151
|
+
Provide sample DLT source data.
|
|
152
|
+
|
|
153
|
+
Returns list of test records.
|
|
154
|
+
|
|
155
|
+
Example:
|
|
156
|
+
>>> def test_ingestion(sample_dlt_data):
|
|
157
|
+
... source = mock_dlt_source(sample_dlt_data)
|
|
158
|
+
"""
|
|
159
|
+
return [
|
|
160
|
+
{
|
|
161
|
+
"id": 1,
|
|
162
|
+
"name": "Alice",
|
|
163
|
+
"email": "alice@example.com",
|
|
164
|
+
"created_at": "2024-01-15T10:00:00Z",
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"id": 2,
|
|
168
|
+
"name": "Bob",
|
|
169
|
+
"email": "bob@example.com",
|
|
170
|
+
"created_at": "2024-01-15T11:00:00Z",
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"id": 3,
|
|
174
|
+
"name": "Charlie",
|
|
175
|
+
"email": "charlie@example.com",
|
|
176
|
+
"created_at": "2024-01-15T12:00:00Z",
|
|
177
|
+
},
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@pytest.fixture
|
|
182
|
+
def sample_dataframe() -> pd.DataFrame:
|
|
183
|
+
"""
|
|
184
|
+
Provide a sample DataFrame for testing.
|
|
185
|
+
|
|
186
|
+
Example:
|
|
187
|
+
>>> def test_transform(sample_dataframe):
|
|
188
|
+
... assert len(sample_dataframe) == 3
|
|
189
|
+
"""
|
|
190
|
+
return pd.DataFrame(
|
|
191
|
+
{
|
|
192
|
+
"id": [1, 2, 3],
|
|
193
|
+
"name": ["Alice", "Bob", "Charlie"],
|
|
194
|
+
"value": [100.5, 200.75, 150.25],
|
|
195
|
+
"date": pd.date_range("2024-01-01", periods=3),
|
|
196
|
+
}
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@pytest.fixture
|
|
201
|
+
def mock_dlt_source_fixture(sample_dlt_data: list[dict]) -> MockDLTResource:
|
|
202
|
+
"""
|
|
203
|
+
Provide a mock DLT source with sample data.
|
|
204
|
+
|
|
205
|
+
Example:
|
|
206
|
+
>>> def test_with_source(mock_dlt_source_fixture):
|
|
207
|
+
... for record in mock_dlt_source_fixture:
|
|
208
|
+
... # Process record
|
|
209
|
+
"""
|
|
210
|
+
return mock_dlt_source(sample_dlt_data, resource_name="test_data")
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
# --- File System Fixtures ---
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@pytest.fixture
|
|
217
|
+
def temp_staging_dir() -> Iterator[Path]:
|
|
218
|
+
"""
|
|
219
|
+
Provide a temporary directory for test files.
|
|
220
|
+
|
|
221
|
+
Auto-cleaned up after test.
|
|
222
|
+
|
|
223
|
+
Example:
|
|
224
|
+
>>> def test_with_temp_dir(temp_staging_dir):
|
|
225
|
+
... parquet_file = temp_staging_dir / "data.parquet"
|
|
226
|
+
... df.to_parquet(parquet_file)
|
|
227
|
+
"""
|
|
228
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
229
|
+
yield Path(tmpdir)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@pytest.fixture
|
|
233
|
+
def test_data_dir() -> Path:
|
|
234
|
+
"""
|
|
235
|
+
Provide path to test data directory.
|
|
236
|
+
|
|
237
|
+
Looks for `tests/fixtures/data/` relative to project root.
|
|
238
|
+
|
|
239
|
+
Example:
|
|
240
|
+
>>> def test_with_data_dir(test_data_dir):
|
|
241
|
+
... data_file = test_data_dir / "users.json"
|
|
242
|
+
"""
|
|
243
|
+
# Find project root by looking for pyproject.toml
|
|
244
|
+
current = Path(__file__).parent
|
|
245
|
+
while current != current.parent:
|
|
246
|
+
if (current / "pyproject.toml").exists():
|
|
247
|
+
return current / "tests" / "fixtures" / "data"
|
|
248
|
+
current = current.parent
|
|
249
|
+
|
|
250
|
+
# Fallback to temp dir if not found
|
|
251
|
+
return Path(tempfile.gettempdir()) / "test_data"
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# --- Composite Fixtures ---
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@pytest.fixture
|
|
258
|
+
def setup_test_catalog(
|
|
259
|
+
mock_iceberg_catalog: MockIcebergCatalog,
|
|
260
|
+
sample_dataframe: pd.DataFrame,
|
|
261
|
+
) -> MockIcebergCatalog:
|
|
262
|
+
"""
|
|
263
|
+
Provide a pre-populated test catalog.
|
|
264
|
+
|
|
265
|
+
Creates sample tables in the catalog.
|
|
266
|
+
|
|
267
|
+
Example:
|
|
268
|
+
>>> def test_with_setup_catalog(setup_test_catalog):
|
|
269
|
+
... table = setup_test_catalog.load_table("raw.users")
|
|
270
|
+
"""
|
|
271
|
+
from pyiceberg.schema import Schema
|
|
272
|
+
from pyiceberg.types import DoubleType, IntegerType, NestedField, StringType
|
|
273
|
+
|
|
274
|
+
# Create sample table
|
|
275
|
+
schema = Schema(
|
|
276
|
+
NestedField(field_id=1, name="id", type=IntegerType(), required=True),
|
|
277
|
+
NestedField(field_id=2, name="name", type=StringType(), required=True),
|
|
278
|
+
NestedField(field_id=3, name="value", type=DoubleType(), required=False),
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
table = mock_iceberg_catalog.create_table("raw.test_data", schema=schema)
|
|
282
|
+
table.append(sample_dataframe)
|
|
283
|
+
|
|
284
|
+
return mock_iceberg_catalog
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@pytest.fixture
|
|
288
|
+
def setup_test_trino(
|
|
289
|
+
mock_trino: MockTrinoResource,
|
|
290
|
+
sample_dataframe: pd.DataFrame,
|
|
291
|
+
) -> MockTrinoResource:
|
|
292
|
+
"""
|
|
293
|
+
Provide a pre-populated Trino resource.
|
|
294
|
+
|
|
295
|
+
Loads sample tables into Trino.
|
|
296
|
+
|
|
297
|
+
Example:
|
|
298
|
+
>>> def test_with_setup_trino(setup_test_trino):
|
|
299
|
+
... cursor = setup_test_trino.cursor()
|
|
300
|
+
... cursor.execute("SELECT * FROM test.sample_data")
|
|
301
|
+
"""
|
|
302
|
+
mock_trino.load_table("test.sample_data", sample_dataframe)
|
|
303
|
+
return mock_trino
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
# --- Data Loading Fixtures ---
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
@pytest.fixture
|
|
310
|
+
def load_json_fixture(test_data_dir: Path) -> Callable[[str], Any]:
|
|
311
|
+
"""
|
|
312
|
+
Provide helper to load JSON fixture files.
|
|
313
|
+
|
|
314
|
+
Example:
|
|
315
|
+
>>> def test_with_json(load_json_fixture):
|
|
316
|
+
... data = load_json_fixture("users.json")
|
|
317
|
+
"""
|
|
318
|
+
|
|
319
|
+
def _load_json(filename: str) -> Any:
|
|
320
|
+
filepath = test_data_dir / filename
|
|
321
|
+
if not filepath.exists():
|
|
322
|
+
raise FileNotFoundError(f"Fixture not found: {filepath}")
|
|
323
|
+
|
|
324
|
+
with open(filepath) as f:
|
|
325
|
+
return json.load(f)
|
|
326
|
+
|
|
327
|
+
return _load_json
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
@pytest.fixture
|
|
331
|
+
def load_csv_fixture(test_data_dir: Path) -> Callable[[str], pd.DataFrame]:
|
|
332
|
+
"""
|
|
333
|
+
Provide helper to load CSV fixture files.
|
|
334
|
+
|
|
335
|
+
Example:
|
|
336
|
+
>>> def test_with_csv(load_csv_fixture):
|
|
337
|
+
... df = load_csv_fixture("users.csv")
|
|
338
|
+
"""
|
|
339
|
+
|
|
340
|
+
def _load_csv(filename: str) -> pd.DataFrame:
|
|
341
|
+
filepath = test_data_dir / filename
|
|
342
|
+
if not filepath.exists():
|
|
343
|
+
raise FileNotFoundError(f"Fixture not found: {filepath}")
|
|
344
|
+
|
|
345
|
+
return pd.read_csv(filepath)
|
|
346
|
+
|
|
347
|
+
return _load_csv
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
# --- Configuration Fixtures ---
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
@pytest.fixture
|
|
354
|
+
def test_config() -> dict[str, Any]:
|
|
355
|
+
"""
|
|
356
|
+
Provide test configuration overrides.
|
|
357
|
+
|
|
358
|
+
Returns dict with test-specific config values.
|
|
359
|
+
|
|
360
|
+
Example:
|
|
361
|
+
>>> def test_with_config(test_config, monkeypatch):
|
|
362
|
+
... monkeypatch.setenv("PHLO_ENV", "test")
|
|
363
|
+
"""
|
|
364
|
+
return {
|
|
365
|
+
"environment": "test",
|
|
366
|
+
"log_level": "DEBUG",
|
|
367
|
+
"parallel_workers": 1,
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
# --- Session-scoped Fixtures ---
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
@pytest.fixture(scope="session")
|
|
375
|
+
def session_temp_dir() -> Iterator[Path]:
|
|
376
|
+
"""
|
|
377
|
+
Provide a temporary directory for the entire test session.
|
|
378
|
+
|
|
379
|
+
Useful for shared test data.
|
|
380
|
+
|
|
381
|
+
Example:
|
|
382
|
+
>>> def test_with_session_dir(session_temp_dir):
|
|
383
|
+
... # Shared across all tests in session
|
|
384
|
+
"""
|
|
385
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
386
|
+
yield Path(tmpdir)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
@pytest.fixture(scope="session")
|
|
390
|
+
def session_catalog() -> Iterator[MockIcebergCatalog]:
|
|
391
|
+
"""
|
|
392
|
+
Provide a catalog shared across all tests in session.
|
|
393
|
+
|
|
394
|
+
Use carefully - tests should clean up their own tables.
|
|
395
|
+
|
|
396
|
+
Example:
|
|
397
|
+
>>> def test_with_session_catalog(session_catalog):
|
|
398
|
+
... # Shared across all tests
|
|
399
|
+
"""
|
|
400
|
+
catalog = MockIcebergCatalog()
|
|
401
|
+
yield catalog
|
|
402
|
+
catalog.close()
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
# --- Parametrization Helpers ---
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def create_partition_dates(start: str, end: str, step_days: int = 1) -> list[str]:
|
|
409
|
+
"""
|
|
410
|
+
Create list of partition dates for parametrized tests.
|
|
411
|
+
|
|
412
|
+
Args:
|
|
413
|
+
start: Start date (ISO format)
|
|
414
|
+
end: End date (ISO format)
|
|
415
|
+
step_days: Days between partitions
|
|
416
|
+
|
|
417
|
+
Returns:
|
|
418
|
+
List of date strings
|
|
419
|
+
|
|
420
|
+
Example:
|
|
421
|
+
>>> dates = create_partition_dates("2024-01-01", "2024-01-31", step_days=7)
|
|
422
|
+
>>> # ["2024-01-01", "2024-01-08", "2024-01-15", ...]
|
|
423
|
+
"""
|
|
424
|
+
start_dt = datetime.fromisoformat(start)
|
|
425
|
+
end_dt = datetime.fromisoformat(end)
|
|
426
|
+
|
|
427
|
+
dates = []
|
|
428
|
+
current = start_dt
|
|
429
|
+
|
|
430
|
+
while current <= end_dt:
|
|
431
|
+
dates.append(current.isoformat()[:10])
|
|
432
|
+
current += timedelta(days=step_days)
|
|
433
|
+
|
|
434
|
+
return dates
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
@pytest.fixture
|
|
438
|
+
def conftest_template() -> str:
|
|
439
|
+
"""
|
|
440
|
+
Get template for conftest.py file.
|
|
441
|
+
|
|
442
|
+
Provides a ready-to-use conftest.py for new test directories.
|
|
443
|
+
|
|
444
|
+
Returns:
|
|
445
|
+
String content for conftest.py
|
|
446
|
+
"""
|
|
447
|
+
return get_conftest_template()
|
phlo_testing/hooks.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Hook testing utilities."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Iterable
|
|
7
|
+
|
|
8
|
+
from phlo.hooks import (
|
|
9
|
+
HookEvent,
|
|
10
|
+
IngestionEvent,
|
|
11
|
+
LineageEvent,
|
|
12
|
+
PublishEvent,
|
|
13
|
+
QualityResultEvent,
|
|
14
|
+
ServiceLifecycleEvent,
|
|
15
|
+
TelemetryEvent,
|
|
16
|
+
TransformEvent,
|
|
17
|
+
)
|
|
18
|
+
from phlo.hooks.bus import HookBus
|
|
19
|
+
from phlo.plugins.hooks import HookFilter, HookRegistration
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MockHookBus(HookBus):
|
|
23
|
+
"""Hook bus that skips plugin discovery for tests."""
|
|
24
|
+
|
|
25
|
+
def _ensure_discovered(self) -> None:
|
|
26
|
+
self._discovered = True
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class CapturedEvents:
|
|
31
|
+
"""Capture hook events in memory for assertions."""
|
|
32
|
+
|
|
33
|
+
events: list[HookEvent]
|
|
34
|
+
|
|
35
|
+
def handler(self, event: HookEvent) -> None:
|
|
36
|
+
"""Append a hook event to the captured list."""
|
|
37
|
+
|
|
38
|
+
self.events.append(event)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def capture_events(
|
|
42
|
+
*,
|
|
43
|
+
bus: HookBus,
|
|
44
|
+
event_types: Iterable[str] | None = None,
|
|
45
|
+
) -> CapturedEvents:
|
|
46
|
+
"""Register a hook handler that collects emitted events."""
|
|
47
|
+
|
|
48
|
+
captured = CapturedEvents(events=[])
|
|
49
|
+
filters = HookFilter(event_types=set(event_types)) if event_types else None
|
|
50
|
+
bus.register(
|
|
51
|
+
HookRegistration(
|
|
52
|
+
hook_name="capture_events",
|
|
53
|
+
handler=captured.handler,
|
|
54
|
+
filters=filters,
|
|
55
|
+
),
|
|
56
|
+
plugin_name="phlo-testing",
|
|
57
|
+
)
|
|
58
|
+
return captured
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def sample_ingestion_event() -> IngestionEvent:
|
|
62
|
+
"""Return a sample ingestion event for tests."""
|
|
63
|
+
|
|
64
|
+
return IngestionEvent(
|
|
65
|
+
event_type="ingestion.end",
|
|
66
|
+
asset_key="dlt_sample",
|
|
67
|
+
table_name="bronze.sample",
|
|
68
|
+
group_name="sample",
|
|
69
|
+
partition_key="2024-01-01",
|
|
70
|
+
status="success",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def sample_quality_event() -> QualityResultEvent:
|
|
75
|
+
"""Return a sample quality event for tests."""
|
|
76
|
+
|
|
77
|
+
return QualityResultEvent(
|
|
78
|
+
event_type="quality.result",
|
|
79
|
+
asset_key="sample_asset",
|
|
80
|
+
check_name="null_check",
|
|
81
|
+
passed=True,
|
|
82
|
+
check_type="NullCheck",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def sample_transform_event() -> TransformEvent:
|
|
87
|
+
"""Return a sample transform event for tests."""
|
|
88
|
+
|
|
89
|
+
return TransformEvent(
|
|
90
|
+
event_type="transform.end",
|
|
91
|
+
tool="dbt",
|
|
92
|
+
status="success",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def sample_publish_event() -> PublishEvent:
|
|
97
|
+
"""Return a sample publish event for tests."""
|
|
98
|
+
|
|
99
|
+
return PublishEvent(
|
|
100
|
+
event_type="publish.end",
|
|
101
|
+
asset_key="publish_sample_marts",
|
|
102
|
+
target_system="postgres",
|
|
103
|
+
tables={"sample": "marts.sample"},
|
|
104
|
+
status="success",
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def sample_lineage_event() -> LineageEvent:
|
|
109
|
+
"""Return a sample lineage event for tests."""
|
|
110
|
+
|
|
111
|
+
return LineageEvent(
|
|
112
|
+
event_type="lineage.edges",
|
|
113
|
+
edges=[("raw.sample", "marts.sample")],
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def sample_telemetry_event() -> TelemetryEvent:
|
|
118
|
+
"""Return a sample telemetry event for tests."""
|
|
119
|
+
|
|
120
|
+
return TelemetryEvent(
|
|
121
|
+
event_type="telemetry.metric",
|
|
122
|
+
name="sample_metric",
|
|
123
|
+
value=1,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def sample_service_event() -> ServiceLifecycleEvent:
|
|
128
|
+
"""Return a sample service lifecycle event for tests."""
|
|
129
|
+
|
|
130
|
+
return ServiceLifecycleEvent(
|
|
131
|
+
event_type="service.post_start",
|
|
132
|
+
service_name="postgres",
|
|
133
|
+
phase="post_start",
|
|
134
|
+
status="success",
|
|
135
|
+
)
|