Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Contract-to-test compilation and negative fixtures

Last updated: 6 Oct 20265 min read
tutorial
AdvancedBy AITrove Editorial

A data contract earns its place in a pipeline when each declared field and rule becomes an executable check with a known failure example.

Compile from one declared contract

Put column names, types, null policy, key grain, accepted ranges and ownership in a versioned contract. Generate compatible schema checks and data tests from that document instead of maintaining a separate handwritten list that can drift. A column marked required should fail on a missing field and on a null value; those are different failure modes. Schema rollout still decides when a new rule can reach consumers.

Match rule to enforcement layer

A storage engine may enforce not-null and uniqueness, but support differs by engine and table format. Run a capability check before treating a declared constraint as physical protection. Row-level predicates, reference checks and cross-table totals usually need query tests. Place cheap structural checks before expensive scans, then run business checks on the candidate release. The contract is a specification; the test result is the evidence.

Generate negative fixtures

For each rule, create at least one violating record or batch: missing amount, null payment ID, repeated ID, invalid currency and amount outside the accepted interval. Verify each fixture fails for the intended reason. A test suite that passes the current happy-path data can be disconnected from the actual candidate, misconfigured or silently disabled. A controlled failure proves its wiring.

Preserve grain and versions

Uniqueness must use the business key at the declared grain, perhaps payment ID plus revision, rather than a convenient row number. Name contract version, candidate generation and test runner revision in each result. If the upstream system starts sending a new optional field, do not reject it unless the compatibility policy calls for exact schemas. Transitive compatibility matters for consumers that skip versions.

Budget validation work

A full duplicate scan can be costly on a huge history table. Run deterministic checks on changed partitions at each release and schedule broader audits for older data, provided the publication contract permits that split. Sampled validation cannot prove uniqueness. Keep an explicit rule coverage report showing declared rules, generated tests, enforcement locations and unimplemented rules; never mark a partial compiler as complete.

Implementation

python
payment_contract = {
    "required": ("payment_id", "currency", "amount_cents"),
    "currencies": {"INR", "USD"},
    "minimum_cents": -470000,
    "maximum_cents": 470000,
}

def validate_payment(payment, contract):
    errors = []
    for field in contract["required"]:
        if field not in payment or payment[field] is None:
            errors.append("required:" + field)
    if payment.get("currency") not in contract["currencies"]:
        errors.append("currency")
    amount = payment.get("amount_cents")
    if amount is not None and (not isinstance(amount, int) or isinstance(amount, bool)):
        errors.append("amount_type")
    elif isinstance(amount, int) and not contract["minimum_cents"] <= amount <= contract["maximum_cents"]:
        errors.append("amount_range")
    return errors

valid_payment = {"payment_id": "pay-47", "currency": "INR", "amount_cents": 2375}
assert validate_payment(valid_payment, payment_contract) == []
assert "required:payment_id" in validate_payment({**valid_payment, "payment_id": None}, payment_contract)

Performance and operating cost

For N records and R simple field rules, direct validation is O(N × R) time and O(F) error output for F failures. Uniqueness and reference checks add key state or index probes. Generating tests reduces drift but cannot make an unsupported database constraint enforceable. Record scan bytes and gate duration alongside failure coverage.

Common Mistakes

  • Do not infer a declared database constraint is enforced on every engine.
  • Do not test only valid rows; verify a negative fixture fails for each rule.
  • Do not declare unique grain using a surrogate row number that is always unique.

Read next

ai-data
data-engineering
Storage details