Data-Driven Tests
datacur8 is primarily tested with data-driven fixtures under tests/<case>/. The fixtures are the test contract. A case is considered incomplete if required files or snapshots are missing, and the test suite must fail in that situation.
Table of contents
- Philosophy
- Required Case Structure
expected/File Reference- Fixture Completeness Rules Enforced by Tests
- Success vs Failure Cases
- Documentation Example Fixtures (
example_*) - Authoring Checklist
- CSV export coverage
- Configurable delimiters and TSV coverage
- TOML coverage
- JSONL / NDJSON coverage
Philosophy
- Test behavior and test fixture completeness are both enforced.
- A case that is missing required snapshots is a broken test, even if the CLI behavior being exercised would otherwise pass.
- Failure cases are first-class test cases. An invalid
.datacur8or invalid data file is valid test input when the expected results are captured underexpected/.
Required Case Structure
Every top-level folder under tests/ is treated as a test case.
tests/<case>/
.datacur8 # required (may intentionally be invalid)
... input files ...
expected/ # required
validate.exit # required
validate.args # optional
validate.stdout # optional
validate.stderr # optional
export/... # required when validate.exit == 0 and outputs are configured
tidy/... # required for tidy cases
expected/ File Reference
expected/validate.exit (required)
- The expected exit code for
datacur8 validate. - Parsed as a single integer.
- Common values:
0: validation succeeded1: config/discovery failure2: data validation failure
expected/validate.args (optional)
- Extra CLI args appended to
validate. - Typical use:
--format jsonfor stable machine-readable error snapshots. - Whitespace-separated.
expected/validate.stdout (optional)
- Snapshot of
validatestdout. - Used most often with
--format json. - Compared as JSON (structural equality), not raw text.
expected/validate.stderr (optional)
- Snapshot of
validatestderr. - Compared line-by-line (order-insensitive for non-empty lines).
expected/export/... (conditionally required)
- Required when:
expected/validate.exitis0, and.datacur8declares one or moretypes[].output.path.
- Must include a snapshot file for every configured output path.
- Example:
expected/
export/
out/
teams.json
services.jsonl
expected/tidy/... (required for tidy cases)
- Contains the expected post-
tidy --writefile content. - Paths are relative to the case root, mirrored under
expected/tidy/. - Example:
expected/tidy/data/w1.yaml - The integration suite also runs plain
tidy(check mode) for the same fixture and asserts:- files are not rewritten in check mode
- exit code is non-zero when the snapshot differs from the original input
- diff output is emitted
Fixture Completeness Rules Enforced by Tests
The integration suite includes a fixture meta-test that fails when:
- a
tests/<case>/directory is missing.datacur8 expected/is missingexpected/validate.exitis missingexpected/export/exists but contains no filesexpected/tidy/exists but contains no filesvalidate.exit == 0and a configuredoutput.pathis missing a matchingexpected/export/...snapshot
This prevents silent skips and partial fixtures.
Success vs Failure Cases
Success cases
expected/validate.exitis0- If outputs are configured,
expected/export/...snapshots are required - Add
expected/tidy/...when the case is intended to exercisetidy(used for both check mode and--write)
Failure cases
- Non-zero
expected/validate.exitis expected .datacur8may be invalid (schema/semantic errors) or the data may be invalid- Prefer
expected/validate.argswith--format jsonplusexpected/validate.stdoutso failure intent is explicit and stable
Documentation Example Fixtures (example_*)
Use tests/example_* for fixtures that back examples shown in user-facing docs. This keeps examples traceable and makes documentation coverage auditable.
Current examples include:
tests/example_readme_quick_start_successtests/example_readme_quick_start_unique_id_failuretests/example_readme_quick_start_path_file_mismatch_failuretests/example_readme_quick_start_foreign_key_failuretests/example_examples_team_service_registry_successtests/example_examples_team_service_registry_foreign_key_failuretests/example_examples_hcl_catalog_successtests/example_examples_toml_catalog_successtests/example_examples_csv_product_catalog_successtests/example_examples_csv_product_catalog_foreign_key_failuretests/example_examples_csv_product_catalog_type_conversion_failuretests/example_examples_strict_mode_enabled_failuretests/example_examples_strict_mode_force_failuretests/example_examples_multi_format_export_jsontests/example_examples_multi_format_export_yamltests/example_examples_multi_format_export_jsonl
Behavior-focused condition examples can also use example_* naming (for example tests/example_conditions_*) when they exist to illustrate a specific error mode.
Authoring Checklist
Before committing a new fixture:
- Add
.datacur8 - Add input files for the scenario
- Add
expected/validate.exit - Add
expected/validate.argsandexpected/validate.stdoutfor failure cases when possible - Add
expected/export/...for every configuredoutput.pathwhen validation succeeds - Add
expected/tidy/...when testingtidy - Do not keep generated outputs in the case root (store snapshots under
expected/export/...instead) - Prefer one clearly named behavior per case
CSV export coverage
example_examples_csv_export_success covers the documented JSON-to-CSV example. csv_export_from_formats uses equivalent JSON/YAML/CSV/HCL records across multiple files and byte-identical snapshots, including optional-column ordering, Unicode, quoting, embedded LF/CR, empty strings, booleans, and numeric precision. csv_export_empty requires a header even without input items. csv_export_numeric_boundaries covers signed 64-bit YAML integers and finite float64 extremes. invalid_csv_export_schema_* and invalid_csv_export_option exercise configuration rejection.
The fixture contract is unchanged. Focused CLI tests in tests/csv_export_test.go create temporary repositories for exit 3 conversion/write failures and assert existing destinations remain untouched. They also verify validation gating, repeat-export byte equality, and export→CSV-input→export with the same schema. Exporter unit tests cover explicit null/structured cells and numeric boundaries that source schema validation may reject before conversion. A successful validate fixture with configured outputs still requires every export snapshot.
Configurable delimiters and TSV coverage
example_examples_tsv_catalog_success backs the TSV example and checks quoted tabs/newlines/quotes, scalar conversion, empty strings, explicit extension matching, constraints, TSV→CSV, and tidy snapshots. tsv_export_from_formats checks JSON/YAML/HCL/comma CSV→TSV with byte snapshots; tsv_export_empty covers header-only output. csv_unicode_delimiter checks a non-ASCII separator. tsv_single_empty_cell preserves a quoted empty record through tidy/export.
invalid_csv_delimiter_* and invalid_csv_options_* cover embedded-schema rejection; config unit tests independently check semantic validation. tsv_* failures cover headers, syntax, width, conversion/schema logical row indices, uniqueness, path matching, and cross-format foreign keys. tsv_strict_* preserve successful processing under both strict overlays. Focused tests in tests/tsv_test.go check repeated export/tidy, post-tidy value alignment, validation-gated exports, and unchanged malformed inputs/destinations. The existing fixture contract and completeness checks are unchanged.
TSV snapshots can end a record with a tab to represent an empty final cell. The *.tsv rule in .gitattributes disables end-of-line whitespace warnings for that data syntax; preserve these tabs when editing snapshots.
TOML coverage
toml_to_* covers TOML→JSON/YAML/JSONL/HCL/TOML with native temporal values, nested/inline tables, arrays of tables, mixed arrays, and multiline/literal strings. toml_export_from_* covers compatible JSON/YAML/CSV/TSV/HCL→TOML. toml_to_tsv checks TOML→tab-delimited CSV. invalid_toml_csv_*_options ensures CSV-only settings remain invalid for TOML. toml_export_empty and toml_numeric_boundaries lock down explicit empty aggregates and checked numeric conversion. Parser/conversion failures, schema/strict-mode failures, unsupported config options, and cross-format constraints have individual fixtures. The documented catalog is example_examples_toml_catalog_success.
tidy_toml locks down comment removal, sorted keys, inline tables, and native temporal types. Focused CLI tests in tests/toml_test.go prove idempotence, invalid-input no-write behavior (exit 4), null export rejection without replacing a destination (exit 3), validation gating (exit 2), write failures, output exclusion, and repeat-export equality. Converter tests explicitly exercise TOML 1.1.0 syntax, temporal precision, arbitrary keys/Unicode, heterogeneous arrays, empty collections, exact integer boundaries, and deterministic aggregate round-trip. The fixture harness and completeness contract are unchanged.
JSONL / NDJSON coverage
example_examples_jsonl_ndjson_catalog_success backs the documented mixed-extension catalog. jsonl_to_* and ndjson_to_* verify every output format; *_empty_to_* lock down zero-item output shapes and unchanged empty tidy files. tidy_jsonl and tidy_ndjson cover CRLF, missing final newline, recursive key ordering, Unicode, escaped newlines, mixed arrays, null, empty collections, unusual keys, and exact numeric preservation. *_large_record contains a record over 64 KiB. Framing, schema, both strict overlays, no coercion, constraint locations, and cross-format references have individual failure/success fixtures. Every error fixture snapshots structured JSON diagnostics with original one-based line.
Focused tests in tests/jsonl_test.go check text/JSON/YAML locations, malformed input no-write behavior (tidy exit 4), validation-gated export with unchanged destinations (exit 2), tidy idempotence, existing JSONL-export read-back without a wrapper, output exclusion, and repeat-export equality. Parser tests verify that a malformed later record returns no partial items. Config fixtures and semantic tests cover both aliases and unsupported options. Existing CSV snapshots and the fixture completeness contract/harness remain unchanged.
The .gitattributes rules permit CRLF in NDJSON fixture inputs and intentionally invalid whitespace-only records in *_whitespace_line; preserve those bytes.