@systemfsoftware/oxlint-plugin-test-discipline
v3.8.0
Published
Oxlint rules enforcing property-based test laws, test placement, and test naming hygiene.
Maintainers
Readme
@systemfsoftware/oxlint-plugin-test-discipline
Oxlint rules enforcing property-based test laws, test placement, and test naming hygiene.
Rules
| Rule | What it enforces |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| no-test-file-in-src | Under src/, the only sanctioned test file is a single-segment <stem>.workflow.property.test.ts inside a sanctioned test directory, plus the generated schema-laws.test.ts entry point. Every other test file is banned: a kernel, policy, or schema suite becomes an in-source import.meta.vitest block, and a public-surface test moves outside src/ as an integration test. |
| src-property-test-cell | A property test under src/ must be a single-segment <stem>.workflow.property.test.ts beside the <stem>.workflow.ts it covers. A source file whose suffix names a cell listed in cellsRequiringTest must also carry an in-source vitest block; that list is empty by default. |
| test-file-outside-tests-dir | A test file outside src/ must live under tests/. |
| test-suffix-outside-src | Outside src/, a test file must end .integration.test.ts, .differential.test.ts, or .trace.test.ts — the three behaviour suffixes. |
| tests-dir-helpers-in-fixtures | Under tests/, the only non-test modules are helpers and fixtures, and they live inside tests/__fixtures__/. |
| no-io-module-in-source-test | An in-source import.meta.vitest test block is forbidden in a module that performs I/O — decided from the module's own syntax (a non-type import from a filesystem, process or network module, called at least once), never from its filename. Only the in-source-test idiom is judged: a module whose tests live in separate files is a no-op for this rule. |
| behaviour-test-requires-gherkin | A .integration.test.ts must import makeFeature from @systemfsoftware/effect-gherkin-spec and must not import test runners directly from vitest or @effect/vitest. |
| behaviour-exercises-use-case | A .integration.test.ts must reach at least one shell entry — an executor/handler/adapter/store, the package main/mod/index, or a non-foundation package — so it drives a use case. |
| behaviour-one-feature-per-file | A .integration.test.ts must contain exactly one Feature(...) call; zero or two-or-more is the junk-drawer signal. |
| differential-test-requires-harness | A .differential.test.ts must import @systemfsoftware/differential-spec and invoke its harness; direct vitest/@effect/vitest imports and raw runner calls (it, test, describe, it.effect, and aliases) are forbidden, and importing the harness without invoking it is equally non-compliant. |
| tests-import-public-api | Every file under the package-root tests/ or __tests__/ trees, regardless of basename, may not relative-import src or climb into an internal folder. Import the published package name or a sibling helper. |
| suffix | layer | doubles | location |
| ----------------------------- | ------------ | ------------------------ | ------------------ |
| *.workflow.property.test.ts | Property | none — pure core | ONLY under src/ |
| *.integration.test.ts | Behaviour | permitted, at ports only | NEVER under src/ |
| *.differential.test.ts | Differential | permitted, at ports only | NEVER under src/ |
| *.trace.test.ts | Trace spec | none — observes graphs | NEVER under src/ |
Enrollment
The rules are turned on by @systemfsoftware/oxlint-config/base, which spreads @systemfsoftware/oxlint-plugin-effect-dmmf's recommended set; that aggregate re-exports all nine under the @systemfsoftware/effect-dmmf/ namespace, so a package extending only base already enforces them. strict adds three TypeScript rules and nothing from this plugin. Neither preset registers this plugin standalone in jsPlugins, so its own recommended config is never the thing being loaded.
Testing
Each rule ships a RuleTester suite at src/rules/__tests__/<rule>.test.ts, with 100% mutation coverage required.
