Skip to content

Add datacontract test --dry-run - #1523

Open
OGsiji wants to merge 2 commits into
datacontract:mainfrom
OGsiji:dry-run-test
Open

Add datacontract test --dry-run#1523
OGsiji wants to merge 2 commits into
datacontract:mainfrom
OGsiji:dry-run-test

Conversation

@OGsiji

@OGsiji OGsiji commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Closes #1510.

datacontract test datacontract.yaml --dry-run
⚪ no checks were executed. Planned 33 checks. Took 0.16 seconds.

Every check is already registered as a stub before execution begins, so the plan is that list with a result of skipped, carrying the assertion each check would have made. The existing output formats render it, so the machine-readable view comes for free:

{ "name": "Check that field order_total has no missing values",
  "implementation": "missing_count(order_total) = 0",
  "result": "skipped" }

Since nothing connects, a plan works where a run cannot. On a contract with a Postgres server and no credentials set:

$ datacontract test contract.yaml
missing_env_DATACONTRACT_POSTGRES_USERNAME: Required configuration ... is not set.   (exit 1)

$ datacontract test contract.yaml --dry-run
⚪ no checks were executed. Planned 10 checks.                                        (exit 0)

On the two questions from the issue

Azure blob. Excluded, as you suggested. Those checks read the file listing to decide which checks exist at all, so there is nothing to plan without reading. A dry run appends a warning check saying the plan is incomplete rather than quietly omitting them.

JSON Schema. Included, since it looked like the case where an empty plan would be most misleading. The schema is still built and compiled and only the file read is skipped, so a contract that could never validate still fails in a dry run — which seemed closer to the point of the feature than skipping it wholesale.

skipped for the checks works as you said. The run-level result needed a little more care, though, and it is the one thing I would like a second opinion on.

The one judgement call

An all-skipped run resolves to unknown, which the writer treats as invalid and exits 1. So a plan initially failed the build, which defeats the CI use case.

My first attempt was to make calculate_result report skipped when every check is skipped. That broke test_metadata_only_all_skipped_remains_unknown, which says plainly that you decided otherwise for --metadata-only, so I backed it out.

Instead the run records dryRun: true and a dry run resolves to skipped on that basis — a plan asserts nothing, so it can neither pass nor fail. --metadata-only still resolves to unknown exactly as before, and its test is untouched.

The field is also what lets anything reading the JSON tell a plan from a run that happened to skip everything, which matters for the case in the issue: without it, "all checks skipped" is ambiguous.

Happy to change how that is modelled if you would rather it worked differently — it is additive to the Run schema, which I did not want to do silently.

Notes

datacontract ci does not get the flag; it seemed odd for a command whose job is to run. Easy to add if you want it.

Full suite green (2103 passed). Nine tests added, the main one asserting the plan lists exactly the checks a real run against the same contract executes, so the plan cannot drift from reality without failing.

@OGsiji

OGsiji commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

Pushed two fixes after testing the paths I had not exercised.

An incomplete plan was invisible. The blob-schema warning was being added correctly, but a dry run resolved to skipped unconditionally, so the summary read ⚪ no checks were executed while the plan was quietly missing checks — the opposite of the requirement to make incompleteness obvious. A dry run now reports the most severe result its own checks carry:

🟠 data contract has warnings. Found the following warnings:
1) Check that blob files match the contract: Dry run is incomplete:
   file-metadata checks for blob schemas are not planned.

Still exits 0. Two tests cover it.

The JSON Schema condition was duplicated. Deciding whether those checks apply was written out twice, once in the execution path and once in the dry run. That is the one place a plan could silently drift from what actually runs, which would defeat the point of the feature. Both now call the same predicate, so the execution path lost a copy rather than gaining one.

Also verified along the way, none of which needed changes: the plan matches a real run under --checks and --dimension filters (now asserted), Kafka and no-server contracts behave as they do without the flag, and --output-format junit reports the planned checks as skipped. The credentials test now clears the environment variables it assumes are absent, so it cannot block on a connection where a developer happens to have them set.

On the 3.13 job: it was cancelled at the 20 minute limit rather than failing an assertion, with orphan pytest/uv processes. I ran the whole suite on 3.13 locally — 2104 passed, and the only two failures were in test_download_datacontract_file.py, which pass on their own. Wall clock was 58 minutes at 5% CPU, so the time went on network stalls in those download tests, not on work. Nothing in this change makes a network call; it removes one, since a dry run no longer fetches the API response. Happy to be told otherwise if you have seen that job wedge differently.

OGsiji added 2 commits August 23, 2026 19:16
Reports the checks a run would execute and stops there. Nothing connects and
nothing is read, so a plan works where a run cannot: on a pull request build
with no warehouse credentials.

Every check is already registered as a stub before execution begins, so the
plan is that list with a result of skipped, carrying the assertion each check
would make. The existing output formats render it, which gives a machine
readable plan through --output-format json.

The JSON Schema checks do not come from the spec list, so they are planned
separately. The schema is still built and compiled, and only the file read is
skipped, so a contract that could never validate still fails in a dry run.

Blob schemas on Azure read the file listing to decide which checks exist at
all, so they cannot be planned. A dry run reports a warning rather than
silently leaving them out of the plan.

A plan asserts nothing, so it can neither pass nor fail: the run records that
it was a dry run and reports skipped, and the command exits 0. That is kept
separate from how a run of all-skipped checks is reported, which still resolves
to unknown.
A dry run resolved to skipped unconditionally, so the warning saying blob
schemas could not be planned was swallowed: the summary read "no checks were
executed" while the plan was quietly missing checks. A dry run now reports the
most severe result its own checks carry, so an incomplete plan surfaces as a
warning and still exits 0.

The condition deciding whether the JSON Schema checks apply was duplicated
between the execution path and the dry run, which is the one place a plan could
drift from what actually runs. Both now call the same predicate.

The credentials test clears the environment variables it depends on being
absent, so it cannot block on a connection where they happen to be set.
@OGsiji

OGsiji commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

@jochenchrist

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dry-run feature for the tests

1 participant