# Turbine demo

A small, self-contained energy-metering project that exercises Turbine end to end
against DuckDB, PostgreSQL, and Snowflake. Every contract and quality spec lints
clean and every check passes green.

## Layout

```
contracts/     5 data contracts, each with embedded quality checks
quality/       4 standalone quality specs that attach to those contracts
checks/        Python check functions referenced by the specs
datasources/   one connection per engine: duckdb, postgres, snowflake
seeds/         per-engine seed SQL that loads identical clean data
```

### Contracts (`contracts/`)

| Contract      | Table                    | Embedded checks                                    |
|---------------|--------------------------|----------------------------------------------------|
| `customers`   | `turbine_data.customers` | row count, email format (SQL)                      |
| `meters`      | `turbine_data.meters`    | row count, status allowed values                   |
| `readings`    | `turbine_data.readings`  | row count, voltage in range (SQL)                  |
| `invoices`    | `turbine_data.invoices`  | row count, amount non-negative (SQL), status set   |
| `daily_usage` | `turbine_data.daily_usage` | row count, one row per meter/day (duplicate)     |

Required columns and the `slaProperties` time element add automatic
missing / schema / latency checks on top of the ones written above.

### Quality specs (`quality/`)

Two attach to `readings`, one each to `meters` and `invoices`:

| Spec                    | Target     | Checks                                  |
|-------------------------|------------|-----------------------------------------|
| `readings_completeness` | `readings` | null count, distinct count              |
| `readings_anomaly`      | `readings` | z-score outlier detection               |
| `meters_quality`        | `meters`   | distinct count, null count              |
| `invoices_quality`      | `invoices` | Python check (`positive_amount`), SQL   |

## Run it

The three engines share one schema name (`turbine_data`) and all-lowercase
identifiers, so the same contracts and specs run unchanged against each.

```bash
# from the repo root, load credentials (PG_* / SF_* / proxy)
set -a && . ./.env && set +a

# 1. Seed the data
#    DuckDB:
python -c "import duckdb; duckdb.connect('demo/demo.duckdb').execute(open('demo/seeds/seed.duckdb.sql').read())"
#    Postgres:
PGPASSWORD="$POSTGRES_PASSWORD" psql -h "$POSTGRES_HOST" -p "$POSTGRES_PORT" \
  -U "$POSTGRES_USER" -d "$POSTGRES_DATABASE" -f demo/seeds/seed.postgres.sql
#    Snowflake (needs the SOCKS proxy): snow sql -c turbine_test -f demo/seeds/seed.snowflake.sql

# 2. Run every check (from inside demo/)
cd demo
uv run turbine check --all --datasource duckdb
uv run turbine check --all --datasource postgres
uv run turbine check --all --datasource snowflake
```

Each run reports `Quality run succeeded` with 0 failures.
