Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Scheduling options are documented in [`docs/scheduling.md`](docs/scheduling.md).

## Current capabilities

- [x] YAML data contracts with column rules and foreign keys
- [x] YAML data contracts with column rules and foreign keys (orders, customers)
- [x] Schema validation against contract columns
- [x] Null, uniqueness, freshness, and referential-integrity checks
- [x] Check run history with SQLite (default) or PostgreSQL
Expand Down Expand Up @@ -101,7 +101,8 @@ python -m src.dqo.cli history --contract orders
├── dags/
│ └── dqo_orders_checks.py
├── contracts/
│ └── orders.yml
│ ├── orders.yml
│ └── customers.yml
├── data/samples/
├── docs/
│ ├── architecture.md
Expand Down
15 changes: 15 additions & 0 deletions contracts/customers.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: customers
version: "1.0"
description: Customer dimension contract for retail analytics

columns:
customer_id:
type: string
nullable: false
unique: true
full_name:
type: string
nullable: false
region:
type: string
nullable: false
4 changes: 4 additions & 0 deletions data/samples/customers_invalid.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
customer_id,full_name,region
C001,Ada Lovelace,EMEA
C001,Grace Hopper,AMER
,Missing Name,APAC
5 changes: 5 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ def orders_contract() -> object:
return load_contract(ROOT / "contracts" / "orders.yml")


@pytest.fixture
def customers_contract() -> object:
return load_contract(ROOT / "contracts" / "customers.yml")


@pytest.fixture
def valid_orders() -> list[dict[str, str]]:
return load_csv(ROOT / "data" / "samples" / "orders.csv")
Expand Down
13 changes: 13 additions & 0 deletions tests/test_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,16 @@ def test_load_orders_contract() -> None:
assert contract.freshness is not None
assert contract.freshness.column == "updated_at"
assert len(contract.foreign_keys) == 1


def test_load_customers_contract() -> None:
contract = load_contract(Path("contracts/customers.yml"))
assert contract.name == "customers"
assert contract.freshness is None
assert {column.name for column in contract.columns} == {
"customer_id",
"full_name",
"region",
}
customer_id_column = next(column for column in contract.columns if column.name == "customer_id")
assert customer_id_column.unique is True
14 changes: 14 additions & 0 deletions tests/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,17 @@ def test_run_contract_file_fails_for_invalid_dataset(tmp_path: Path) -> None:
)
assert summary.passed is False
assert summary.failed_results


def test_run_checks_passes_for_valid_customers(customers_contract, customers) -> None:
results = run_checks(customers_contract, customers)
assert all(result.status.value != "failed" for result in results)


def test_run_contract_file_fails_for_invalid_customers() -> None:
summary = run_contract_file(
Path("contracts/customers.yml"),
Path("data/samples/customers_invalid.csv"),
)
assert summary.passed is False
assert summary.failed_results
Loading