Skip to content

Prove Enforcement with the Regression Pack

ContractEnforcementRule gates every Spark write against a contract before it executes: a write that violates its contract is aborted, and no output is ever created. The regression pack re-runs that guarantee as a script instead of a one-off manual check, with real spark-submit invocations and real assertions — not mocks.

Terminal window
./dev/regression

Requires a working ./dev/test environment (Codespaces already provides one).

Ten real cases, exiting non-zero if any behaves unexpectedly — schema-level enforcement, then the same pass/fail proof for static data-quality verification, for input/output type role-consistency, for a job that checkpoints between reading and writing, and for per-output input scoping (derivedFrom):

  1. Contract satisfied (demo/contracts/invaract_output.yaml) — the job exits 0, the report says PASS, and the output file exists.

  2. Contract violated (demo/contracts/invaract_output_broken_example.yaml, which requires a customer_name column the demo transformation never produces) — the job exits non-zero, the report says FAIL with a MISSING_OUTPUT_FIELD violation, and — the core guarantee — the output file is never created.

  3. Data-quality constraint satisfied (demo/contracts/invaract_output_data_quality_pass.yaml, declaring value_tier must be one of [high, low]) — the demo transformation’s own CASE WHEN value > 50 THEN 'high' ELSE 'low' END provably satisfies that, so the job exits 0 and the published ContractValidationEvent carries a Guaranteed verdict for value_tier.

  4. Data-quality constraint violated (demo/contracts/invaract_output_data_quality_fail.yaml, declaring value_tier must be [high] only) — the same transformation provably produces 'low' on its ELSE branch, so the job exits non-zero, the report shows a DATA_QUALITY_VIOLATION, and the output file is never created.

  5. Declared type consistent with observed usage (demo/contracts/invaract_output_role_consistency_pass.yaml, declaring the orders input DATA_ASSET) — orders’ value column genuinely does derive value_squared/value_tier in the produced output, so the job exits 0 and the published ContractValidationEvent carries a Conforms verdict for orders.

  6. Declared type contradicted by observed usage (demo/contracts/invaract_output_role_consistency_fail.yaml, declaring the same orders input CONTROL) — its data still reaches a produced output column, the exact high-confidence contradiction role-consistency checking exists to catch, so the job exits non-zero, the report shows a ROLE_CONSISTENCY_VIOLATION, and the output file is never created.

  7. Checkpointed job, contract satisfied (demo/contracts/invaract_output.yaml, the same contract as case 1, run with the demo harness’s --checkpoint flag, which .checkpoint()s the input right after reading it) — the job really does checkpoint (Spark’s checkpoint directory exists), yet the declared input is found: it exits 0 and the published ContractValidationEvent reports PASSED with no unverifiable inputs.

  8. Checkpointed job, declared input never read (demo/contracts/invaract_output_checkpoint_missing_input.yaml, which also declares an orders_typo input the job never reads) — a checkpoint must not hide that: the job exits non-zero, the report shows MISSING_INPUT for demo/input/sample_typo.csv, the event’s unverifiableInputs is empty (the input was proven missing, not merely unverifiable), and the output file is never created.

  9. derivedFrom, a write that reads only its own inputs (demo/contracts/invaract_output_derived_from.yaml, two outputs and three inputs; orders_report is derivedFrom: [orders]) — the job writes orders_report and reads only orders, so it exits 0 even though customers and products are declared and never read: they are only expected of the other output.

  10. derivedFrom, a write that reads an input outside its output’s derivedFrom (the same contract, the same job, now landing on catalog_report, which is derivedFrom: [customers, products]) — the job still reads orders, an input the contract declares but not as a source of this output, so it exits non-zero, the report shows UNDECLARED_INPUT, and the output file is never created.

Cases 3/4 attach spark.invaract.staticDataQuality=true purely via --conf, the same way a platform team would against a job whose source they don’t own — see Verify Static Data Quality. Cases 5/6 attach spark.invaract.roleConsistency=true the identical way — see Declaring Input and Output Types. Cases 9/10 attach spark.invaract.rejectUndeclaredInputs=true the same way — see Map Outputs to Their Inputs.

Unit tests can assert that ContractEnforcementRule.verifyOrThrow throws for a given input, but that doesn’t prove the real Spark write never happens end-to-end, via a real spark-submit process, the way a production job would run. The regression pack closes that gap: it’s the same proof Your First Contract walked through manually, automated and asserted on every run.