@systemfsoftware/vitest 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +66 -4
- package/dist/engine-D7Q6-BxD.d.ts +503 -0
- package/dist/{failure-D8mPXwf4.mjs → failure-nvq5TwWX.mjs} +1074 -215
- package/dist/failure.d.ts +28 -115
- package/dist/failure.mjs +2 -2
- package/dist/index.d.ts +3 -10
- package/dist/index.mjs +19 -6
- package/package.json +4 -1
- package/dist/engine-CMvYG5V0.d.ts +0 -166
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @systemfsoftware/vitest
|
|
2
2
|
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- Property failures (`PropertyRefuted`, `NonBooleanVerdict`, `CoverageBelowMinimum`, `VacuousProperty`, `SelfModelLaw`, `SeedStoreUnreadable`, `ReplayUnreadable`) are exported tagged errors whose fields (property name, site, seed, runs, witness, `replay` text) reach reporters, replacing the `detail` prose. A provided budget `seed` now gives each property its own stable seed; failing seeds are recorded in `__property_seeds__/<test file>.jsonl` and replayed first; vitest-config sets `record: false` in CI and Stryker.
|
|
8
|
+
|
|
9
|
+
- To keep one literal seed, set `arbitrary: { seed }` on the property.
|
|
10
|
+
- Commit `__property_seeds__` files; a case that must always run belongs in the test source.
|
|
11
|
+
- Catch a self-model law as `SelfModelLaw`, no longer `VacuousProperty`.
|
|
12
|
+
|
|
3
13
|
## 1.0.0
|
|
4
14
|
|
|
5
15
|
### Major Changes
|
package/README.md
CHANGED
|
@@ -171,16 +171,78 @@ it.prop(
|
|
|
171
171
|
|
|
172
172
|
### The constant-impostor gate
|
|
173
173
|
|
|
174
|
-
After a property holds, it runs again against a constant impostor of its subject — a fake that returns the first output forever, whatever the input. Each subject is judged across every property in its file, when the file ends. If nothing in the file refutes the impostor, the file fails `VacuousProperty`:
|
|
174
|
+
After a property holds, it runs again against a constant impostor of its subject — a fake that returns the first output forever, whatever the input. Each subject is judged across every property in its file, when the file ends. If nothing in the file refutes the impostor, the file fails with `VacuousProperty`:
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
```ts
|
|
177
|
+
export interface VacuousProperty {
|
|
178
|
+
readonly _tag: 'VacuousProperty'
|
|
179
|
+
readonly subjects: ReadonlyArray<{
|
|
180
|
+
readonly label: string
|
|
181
|
+
readonly properties: ReadonlyArray<{
|
|
182
|
+
readonly property: { name: string; site: string | null; seed: number; runs: number }
|
|
183
|
+
readonly frozen:
|
|
184
|
+
| { _tag: 'Frozen'; outputs: ReadonlyArray<{ member: string; output: { rendered: string; value: unknown } }> }
|
|
185
|
+
| { _tag: 'NeverCalled' }
|
|
186
|
+
}>
|
|
187
|
+
}>
|
|
188
|
+
readonly exempt: ReadonlyArray<{ name: string; kind: string }>
|
|
189
|
+
readonly replay: string
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The `frozen` union distinguishes a subject whose impostor was called (`Frozen`, with rendered outputs for each member) from one never called during the vacuity check (`NeverCalled`). Replay the same verdict with:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
CONFORMANCE_REPLAY="property=<hash>;seed=<n>;runs=<n>|..." pnpm test
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Other property failures are exported from the package root as tagged errors — each carries its own fields:
|
|
200
|
+
|
|
201
|
+
- **`PropertyRefuted`**: `property` (name, site, seed, runs), `counterexample` (rendered, value), `shrinks`, `replay`
|
|
202
|
+
- **`NonBooleanVerdict`**: `property`, `drawn` (rendered, value), `returned` (array of kinds), `replay`
|
|
203
|
+
- **`CoverageBelowMinimum`**: `property`, `classes` (array of {label, hits, runs, minimum}), `replay`
|
|
204
|
+
- **`SelfModelLaw`**: `name`, `site` — the law compared the subject to itself
|
|
205
|
+
- **`SeedStoreUnreadable`**: `file`, `line`, `detail` — a seed store file's line could not be decoded
|
|
206
|
+
- **`ReplayUnreadable`**: `text` — `CONFORMANCE_REPLAY` names neither the kernel's `seed=…;path=…` nor a property entry
|
|
177
207
|
|
|
178
|
-
|
|
208
|
+
A reporter receives every field on the raw error, so a script or agent narrows by `_tag` and reads the fields directly without parsing messages:
|
|
179
209
|
|
|
180
210
|
```ts
|
|
181
|
-
|
|
211
|
+
if (error._tag === 'VacuousProperty') {
|
|
212
|
+
for (const subject of error.subjects) {
|
|
213
|
+
for (const run of subject.properties) {
|
|
214
|
+
console.log(run.property.name, run.frozen._tag, run.property.seed)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
182
218
|
```
|
|
183
219
|
|
|
220
|
+
### Seeds and the seed store
|
|
221
|
+
|
|
222
|
+
A provided budget asks for derandomized runs by carrying a `seed`. Each property then gets its own stable seed derived from that seed and its identity, so different properties draw different inputs and the same property draws the same inputs in every process and on every machine:
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
// vitest.config.ts
|
|
226
|
+
export default defineConfig({
|
|
227
|
+
test: { provide: { '@systemfsoftware/vitest:property-check': { runs: 30, seed: 1 } } },
|
|
228
|
+
})
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Seed precedence**: a property's own `arbitrary: { seed: 7 }` wins over a provided seed. With a provided `seed`, the engine hashes it (FNV-1a) with the property's identity — its package, its file relative to the Vitest project root, and its full test name. With neither, each property draws from a fresh random seed. Every failure reports the seed it ran with.
|
|
232
|
+
|
|
233
|
+
**Migration**: code that relied on a provided seed making every property share one stream must set `arbitrary: { seed: <literal> }` on each property that needs a literal seed.
|
|
234
|
+
|
|
235
|
+
**The seed store and recording**: failing seeds are automatically saved to a checked-in JSON Lines file `__property_seeds__/<test file>.jsonl` beside the test file. Each line holds either:
|
|
236
|
+
|
|
237
|
+
- Refuted: `{ "_tag": "Refuted", "property": "full test name", "seed": 42, "attempt": 5, "size": 3, "path": [0,1], "failure": "ReturnedFalse" }`
|
|
238
|
+
- NonBoolean: `{ "_tag": "NonBoolean", "property": "full test name", "seed": 42, "runs": 100 }`
|
|
239
|
+
|
|
240
|
+
Recorded seeds replay before novel draws on every run, whatever the budget. Only a refuted or non-boolean failure writes, and only in a run that is not derandomized and does not set `record: false`; the shared config sets `record: false` in CI and in Stryker workers. Commit seed store updates like snapshot files.
|
|
241
|
+
|
|
242
|
+
**Generator changes**: if a property's generator changes, its recorded seeds may no longer produce their old counterexamples, so the failure stops reproducing. Cases that must always run belong in the test source itself, not in the seed store.
|
|
243
|
+
|
|
244
|
+
**The `record` option**: `{ runs: 30, record: false }` opts out of writing seeds for a run; the engine still reads and replays existing seeds.
|
|
245
|
+
|
|
184
246
|
### Law kinds
|
|
185
247
|
|
|
186
248
|
`it.law.model` (an independent oracle), `it.law.metamorphic` (the input transform), `it.law.roundTrip` (the encode/decode pair) and `it.law.invariant` (a predicate on the output) are sugar over the same gate.
|
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
|
|
2
|
+
import * as Cause from "effect/Cause";
|
|
3
|
+
import * as Effect$1 from "effect/Effect";
|
|
4
|
+
import * as Schema$1 from "effect/Schema";
|
|
5
|
+
import * as Tracer from "effect/Tracer";
|
|
6
|
+
import { Schema } from "effect";
|
|
7
|
+
import * as Arbitrary from "effect/Arbitrary";
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/internal/errors.schema.d.ts
|
|
10
|
+
/**
|
|
11
|
+
* The contract rules a rendered failure record can break (R10, KTD7).
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
declare const Breach: Schema.Literals<readonly ["R1", "R2", "R6"]>;
|
|
16
|
+
/** @internal */
|
|
17
|
+
type Breach = typeof Breach.Type;
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/internal/property/error.schema.d.ts
|
|
20
|
+
/**
|
|
21
|
+
* One witness: the text the renderer prints for a value, and a JSON-safe projection of the same traversal (R3, KD7).
|
|
22
|
+
*
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
declare const Witness: Schema.Struct<{
|
|
26
|
+
readonly rendered: Schema.String;
|
|
27
|
+
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
28
|
+
}>;
|
|
29
|
+
/** @internal */
|
|
30
|
+
type Witness = typeof Witness.Type;
|
|
31
|
+
/**
|
|
32
|
+
* One property involved in a failure: its full name, its declaration site, its seed and its run count (R2).
|
|
33
|
+
*
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
declare const PropertyRun: Schema.Struct<{
|
|
37
|
+
readonly name: Schema.String;
|
|
38
|
+
readonly site: Schema.NullOr<Schema.String>;
|
|
39
|
+
readonly seed: Schema.brand<Schema.Int, "PropertySeed">;
|
|
40
|
+
readonly runs: Schema.brand<Schema.Int, "PropertyRunCount">;
|
|
41
|
+
}>;
|
|
42
|
+
/** @internal */
|
|
43
|
+
type PropertyRun = typeof PropertyRun.Type;
|
|
44
|
+
declare const PropertyRefuted_base: Schema.Class<PropertyRefuted, Schema.TaggedStruct<"PropertyRefuted", {
|
|
45
|
+
readonly property: Schema.Struct<{
|
|
46
|
+
readonly name: Schema.String;
|
|
47
|
+
readonly site: Schema.NullOr<Schema.String>;
|
|
48
|
+
readonly seed: Schema.brand<Schema.Int, "PropertySeed">;
|
|
49
|
+
readonly runs: Schema.brand<Schema.Int, "PropertyRunCount">;
|
|
50
|
+
}>;
|
|
51
|
+
readonly counterexample: Schema.Struct<{
|
|
52
|
+
readonly rendered: Schema.String;
|
|
53
|
+
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
54
|
+
}>;
|
|
55
|
+
readonly shrinks: Schema.brand<Schema.Int, "PropertyShrinkCount">;
|
|
56
|
+
readonly replay: Schema.String;
|
|
57
|
+
}>, import("effect/Cause").YieldableError>;
|
|
58
|
+
/**
|
|
59
|
+
* A falsified property: the property that ran, the shrunk counterexample the generator settled on, and how many
|
|
60
|
+
* shrink steps it took to reach it (R2-R4).
|
|
61
|
+
*
|
|
62
|
+
* @internal
|
|
63
|
+
*/
|
|
64
|
+
declare class PropertyRefuted extends PropertyRefuted_base {
|
|
65
|
+
get message(): string;
|
|
66
|
+
}
|
|
67
|
+
declare const NonBooleanVerdict_base: Schema.Class<NonBooleanVerdict, Schema.TaggedStruct<"NonBooleanVerdict", {
|
|
68
|
+
readonly property: Schema.Struct<{
|
|
69
|
+
readonly name: Schema.String;
|
|
70
|
+
readonly site: Schema.NullOr<Schema.String>;
|
|
71
|
+
readonly seed: Schema.brand<Schema.Int, "PropertySeed">;
|
|
72
|
+
readonly runs: Schema.brand<Schema.Int, "PropertyRunCount">;
|
|
73
|
+
}>;
|
|
74
|
+
readonly drawn: Schema.Struct<{
|
|
75
|
+
readonly rendered: Schema.String;
|
|
76
|
+
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
77
|
+
}>;
|
|
78
|
+
readonly returned: Schema.$Array<Schema.Literals<readonly ["undefined", "object", "boolean", "number", "bigint", "string", "symbol", "function", "effect"]>>;
|
|
79
|
+
readonly replay: Schema.String;
|
|
80
|
+
}>, import("effect/Cause").YieldableError>;
|
|
81
|
+
/**
|
|
82
|
+
* A verdict that was not a literal boolean: the property that ran, the first drawn values whose verdict was not a
|
|
83
|
+
* boolean, and every kind of value the property returned (R4).
|
|
84
|
+
*
|
|
85
|
+
* @internal
|
|
86
|
+
*/
|
|
87
|
+
declare class NonBooleanVerdict extends NonBooleanVerdict_base {
|
|
88
|
+
get message(): string;
|
|
89
|
+
}
|
|
90
|
+
declare const CoverageBelowMinimum_base: Schema.Class<CoverageBelowMinimum, Schema.TaggedStruct<"CoverageBelowMinimum", {
|
|
91
|
+
readonly property: Schema.Struct<{
|
|
92
|
+
readonly name: Schema.String;
|
|
93
|
+
readonly site: Schema.NullOr<Schema.String>;
|
|
94
|
+
readonly seed: Schema.brand<Schema.Int, "PropertySeed">;
|
|
95
|
+
readonly runs: Schema.brand<Schema.Int, "PropertyRunCount">;
|
|
96
|
+
}>;
|
|
97
|
+
readonly classes: Schema.$Array<Schema.Struct<{
|
|
98
|
+
readonly label: Schema.String;
|
|
99
|
+
readonly hits: Schema.Int;
|
|
100
|
+
readonly runs: Schema.Int;
|
|
101
|
+
readonly minimum: Schema.Finite;
|
|
102
|
+
}>>;
|
|
103
|
+
readonly replay: Schema.String;
|
|
104
|
+
}>, import("effect/Cause").YieldableError>;
|
|
105
|
+
/**
|
|
106
|
+
* A property whose declared coverage classes stayed under their minima: the property and the failed classes, in
|
|
107
|
+
* declaration order (R7).
|
|
108
|
+
*
|
|
109
|
+
* @internal
|
|
110
|
+
*/
|
|
111
|
+
declare class CoverageBelowMinimum extends CoverageBelowMinimum_base {
|
|
112
|
+
get message(): string;
|
|
113
|
+
}
|
|
114
|
+
declare const SelfModelLaw_base: Schema.Class<SelfModelLaw, Schema.TaggedStruct<"SelfModelLaw", {
|
|
115
|
+
readonly name: Schema.String;
|
|
116
|
+
readonly site: Schema.NullOr<Schema.String>;
|
|
117
|
+
}>, import("effect/Cause").YieldableError>;
|
|
118
|
+
/**
|
|
119
|
+
* A model law given the subject itself as its oracle, so it compares nothing (R13).
|
|
120
|
+
*
|
|
121
|
+
* @internal
|
|
122
|
+
*/
|
|
123
|
+
declare class SelfModelLaw extends SelfModelLaw_base {
|
|
124
|
+
get message(): string;
|
|
125
|
+
}
|
|
126
|
+
declare const SeedStoreUnreadable_base: Schema.Class<SeedStoreUnreadable, Schema.TaggedStruct<"SeedStoreUnreadable", {
|
|
127
|
+
readonly file: Schema.String;
|
|
128
|
+
readonly line: Schema.Int;
|
|
129
|
+
readonly detail: Schema.String;
|
|
130
|
+
}>, import("effect/Cause").YieldableError>;
|
|
131
|
+
/**
|
|
132
|
+
* A seed-store line that could not be decoded (R16, KTD6).
|
|
133
|
+
*
|
|
134
|
+
* @internal
|
|
135
|
+
*/
|
|
136
|
+
declare class SeedStoreUnreadable extends SeedStoreUnreadable_base {
|
|
137
|
+
get message(): string;
|
|
138
|
+
}
|
|
139
|
+
declare const VacuousProperty_base: Schema.Class<VacuousProperty, Schema.TaggedStruct<"VacuousProperty", {
|
|
140
|
+
readonly subjects: Schema.$Array<Schema.Struct<{
|
|
141
|
+
readonly label: Schema.String;
|
|
142
|
+
readonly properties: Schema.$Array<Schema.Struct<{
|
|
143
|
+
readonly property: Schema.Struct<{
|
|
144
|
+
readonly name: Schema.String;
|
|
145
|
+
readonly site: Schema.NullOr<Schema.String>;
|
|
146
|
+
readonly seed: Schema.brand<Schema.Int, "PropertySeed">;
|
|
147
|
+
readonly runs: Schema.brand<Schema.Int, "PropertyRunCount">;
|
|
148
|
+
}>;
|
|
149
|
+
readonly frozen: Schema.Union<readonly [Schema.TaggedStruct<"Frozen", {
|
|
150
|
+
readonly outputs: Schema.$Array<Schema.Struct<{
|
|
151
|
+
readonly member: Schema.String;
|
|
152
|
+
readonly output: Schema.Struct<{
|
|
153
|
+
readonly rendered: Schema.String;
|
|
154
|
+
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
155
|
+
}>;
|
|
156
|
+
}>>;
|
|
157
|
+
}>, Schema.TaggedStruct<"NeverCalled", {}>]>;
|
|
158
|
+
}>>;
|
|
159
|
+
}>>;
|
|
160
|
+
readonly exempt: Schema.$Array<Schema.Struct<{
|
|
161
|
+
readonly name: Schema.String;
|
|
162
|
+
readonly kind: Schema.String;
|
|
163
|
+
}>>;
|
|
164
|
+
readonly replay: Schema.String;
|
|
165
|
+
}>, import("effect/Cause").YieldableError>;
|
|
166
|
+
/**
|
|
167
|
+
* Thrown at file end when no property in the file refuted a subject's constant impostor (R12, R5, R6).
|
|
168
|
+
*
|
|
169
|
+
* @internal
|
|
170
|
+
*/
|
|
171
|
+
declare class VacuousProperty extends VacuousProperty_base {
|
|
172
|
+
get message(): string;
|
|
173
|
+
}
|
|
174
|
+
declare const ReplayUnreadable_base: Schema.Class<ReplayUnreadable, Schema.TaggedStruct<"ReplayUnreadable", {
|
|
175
|
+
readonly text: Schema.String;
|
|
176
|
+
}>, import("effect/Cause").YieldableError>;
|
|
177
|
+
/** @internal */
|
|
178
|
+
declare class ReplayUnreadable extends ReplayUnreadable_base {
|
|
179
|
+
get message(): string;
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/internal/failure-record.d.ts
|
|
183
|
+
/** A value the renderer narrows rather than assumes. */
|
|
184
|
+
type Opaque$1<A = unknown> = A;
|
|
185
|
+
/**
|
|
186
|
+
* A value a recorded span attribute or a replay path carries. Structured values stay structured on the span
|
|
187
|
+
* (KTD2); the renderer decides the text.
|
|
188
|
+
*
|
|
189
|
+
* @internal
|
|
190
|
+
*/
|
|
191
|
+
type AttributeValue = string | number | boolean | null | ReadonlyArray<AttributeValue> | {
|
|
192
|
+
readonly [key: string]: AttributeValue;
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* The test a failure happened in, as Vitest knows it. `file` is the path the rerun command names, so a caller
|
|
196
|
+
* that holds an absolute path derives the repo-relative one before handing it over.
|
|
197
|
+
*
|
|
198
|
+
* @internal
|
|
199
|
+
*/
|
|
200
|
+
interface TestIdentity {
|
|
201
|
+
/**
|
|
202
|
+
* The npm package the test file belongs to, e.g. `@systemfsoftware/effect-readiness`. Empty when the run
|
|
203
|
+
* provided no name: the rerun command then drops its `pnpm --filter` prefix and runs from the package
|
|
204
|
+
* directory, which is not a breach (R6).
|
|
205
|
+
*/
|
|
206
|
+
readonly package: string;
|
|
207
|
+
/** The test file, as the rerun command writes it. */
|
|
208
|
+
readonly file: string;
|
|
209
|
+
/** The scenario name the rerun command filters on with `-t`. */
|
|
210
|
+
readonly name: string;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* The value a generator chose a run by: the kernel's seed and decision path, or a property counterexample's
|
|
214
|
+
* seed and path. A path without a seed is a replay value offered for a baseline run, which R6 refuses (KTD5).
|
|
215
|
+
*
|
|
216
|
+
* @internal
|
|
217
|
+
*/
|
|
218
|
+
interface ReplayValue {
|
|
219
|
+
/** The generator's seed, or `undefined` when only a path was recorded. */
|
|
220
|
+
readonly seed: number | undefined;
|
|
221
|
+
/** The decisions the generator chose, in order; a decision is the index the scheduler picked (KTD5). */
|
|
222
|
+
readonly path: ReadonlyArray<number>;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* A contract rule a rendered record breaks; the runner refuses such a record (R10, KTD7).
|
|
226
|
+
*
|
|
227
|
+
* @internal
|
|
228
|
+
*/
|
|
229
|
+
/**
|
|
230
|
+
* What a rendered failure gives Vitest: the tag the error carries, the record text, and the rules it breaks.
|
|
231
|
+
*
|
|
232
|
+
* @internal
|
|
233
|
+
*/
|
|
234
|
+
interface FailureRecord {
|
|
235
|
+
/** The headline's failure tag, e.g. `StepError`; the runner sets it as the thrown error's `name`. */
|
|
236
|
+
readonly name: string;
|
|
237
|
+
/** The text Vitest prints as the failure's message. */
|
|
238
|
+
readonly record: string;
|
|
239
|
+
/** The contract rules the record breaks: `R1` empty headline, `R2` no location, `R6` replay or rerun. */
|
|
240
|
+
readonly breaches: ReadonlyArray<Breach>;
|
|
241
|
+
/**
|
|
242
|
+
* The raised failure itself when it is one of the property-channel variants, so an in-process corpus run reads
|
|
243
|
+
* its fields without parsing the record text (R1, R2). Absent for every other failure.
|
|
244
|
+
*/
|
|
245
|
+
readonly failure?: Opaque$1;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Everything one failure is rendered from: the failure, its run's spans, the test and the replay value.
|
|
249
|
+
*
|
|
250
|
+
* @internal
|
|
251
|
+
*/
|
|
252
|
+
interface FailureRecordInput<E = never> {
|
|
253
|
+
/** The failure itself: a `Cause`, or the value a program threw or failed with. */
|
|
254
|
+
readonly failure: Cause.Cause<E> | E;
|
|
255
|
+
/** Every span the failing run recorded, in creation order. */
|
|
256
|
+
readonly spans: ReadonlyArray<Tracer.NativeSpan>;
|
|
257
|
+
/** The test the run belonged to. */
|
|
258
|
+
readonly identity: TestIdentity;
|
|
259
|
+
/** The generator that chose this run, or `undefined` on a run no generator chose. */
|
|
260
|
+
readonly replay: ReplayValue | undefined;
|
|
261
|
+
/**
|
|
262
|
+
* The seed the kernel's scheduler chose. The kernel knows its own scheduler, so it hands the note over rather
|
|
263
|
+
* than the renderer guessing it; a run no scheduler chose renders no note (KTD5).
|
|
264
|
+
*/
|
|
265
|
+
readonly schedule?: {
|
|
266
|
+
readonly seed: number;
|
|
267
|
+
} | undefined;
|
|
268
|
+
/**
|
|
269
|
+
* The workspace root the rendered paths are relativized against, provided by the shared config the same way the
|
|
270
|
+
* package name is. Omitted, paths print absolute.
|
|
271
|
+
*/
|
|
272
|
+
readonly root?: string | undefined;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* A value `JSON.stringify` carries: the projection a witness travels to a consumer as (R3, KD7).
|
|
276
|
+
*
|
|
277
|
+
* @internal
|
|
278
|
+
*/
|
|
279
|
+
type JsonValue = Schema$1.Json;
|
|
280
|
+
/**
|
|
281
|
+
* The one traversal a value's rendered text and its JSON projection come from (R3, KD7): `rendered` is exactly
|
|
282
|
+
* `renderValue`'s text, `value` is the same walk with every value JSON cannot carry marked by kind, and a cycle
|
|
283
|
+
* marks `circular` rather than recursing forever.
|
|
284
|
+
*
|
|
285
|
+
* @internal
|
|
286
|
+
*/
|
|
287
|
+
declare const witnessOf: (value: Opaque$1) => Witness;
|
|
288
|
+
/** @internal */
|
|
289
|
+
declare const summaryOf: (value: Opaque$1) => string;
|
|
290
|
+
/**
|
|
291
|
+
* Renders the record Vitest prints for one spec failure, and the contract rules it breaks.
|
|
292
|
+
*
|
|
293
|
+
* Never throws: a `Cause` renders layer by layer, a thrown `Error` renders as one layer, and any other value
|
|
294
|
+
* renders as itself. The first location the record names is the raising frame — the first stack frame outside
|
|
295
|
+
* `node_modules` and outside the spec libraries' own `src/` and `dist/` — and when no layer has one, the
|
|
296
|
+
* innermost failed span's site stands in, or the innermost unfinished cell's decide site for a deadlock (KTD6).
|
|
297
|
+
*
|
|
298
|
+
* @internal
|
|
299
|
+
*/
|
|
300
|
+
declare const renderFailureRecord: <E>(input: FailureRecordInput<E>) => FailureRecord;
|
|
301
|
+
/** @internal */
|
|
302
|
+
declare const throwFailureRecord: <E>(input: FailureRecordInput<E>) => never;
|
|
303
|
+
interface TaskLike {
|
|
304
|
+
readonly name?: string | undefined;
|
|
305
|
+
readonly fullName?: string | undefined;
|
|
306
|
+
readonly fullTestName?: string | undefined;
|
|
307
|
+
}
|
|
308
|
+
/** @internal */
|
|
309
|
+
declare const testIdentityOf: (task?: TaskLike | null) => TestIdentity;
|
|
310
|
+
//#endregion
|
|
311
|
+
//#region src/internal/property/defaults.d.ts
|
|
312
|
+
/** @internal The `test.provide` key the shared config fills with this run's tier's check options. */
|
|
313
|
+
declare const checkDefaultsKey = "@systemfsoftware/vitest:property-check";
|
|
314
|
+
/**
|
|
315
|
+
* The check options a run supplies as its property default, minus `replay`. `record` asks the engine to write a
|
|
316
|
+
* failing seed to the checked-in seed store, which it does unless a run opts out.
|
|
317
|
+
*
|
|
318
|
+
* @internal
|
|
319
|
+
*/
|
|
320
|
+
interface PropertyBudget extends Omit<Arbitrary.CheckOptions, 'replay'> {
|
|
321
|
+
/** Write failing seeds to the seed store; default true. A derandomized run never writes regardless. */
|
|
322
|
+
readonly record?: boolean | undefined;
|
|
323
|
+
}
|
|
324
|
+
/** @internal The check options a run supplies as its property default (KTD4). */
|
|
325
|
+
type ProvidedCheckDefaults = PropertyBudget;
|
|
326
|
+
//#endregion
|
|
327
|
+
//#region src/internal/property/impostor.d.ts
|
|
328
|
+
/**
|
|
329
|
+
* A value the fork hands over without inspecting: an argument to the subject, or one of its outputs.
|
|
330
|
+
*
|
|
331
|
+
* @internal
|
|
332
|
+
*/
|
|
333
|
+
type Opaque<A = unknown> = A;
|
|
334
|
+
/**
|
|
335
|
+
* A function under test: any argument, any output.
|
|
336
|
+
*
|
|
337
|
+
* @internal
|
|
338
|
+
*/
|
|
339
|
+
type SubjectFunction = (...values: ReadonlyArray<never>) => Opaque;
|
|
340
|
+
/**
|
|
341
|
+
* A record of functions under test; every member is replaced by a constant.
|
|
342
|
+
*
|
|
343
|
+
* @internal
|
|
344
|
+
*/
|
|
345
|
+
type MemberRecord = {
|
|
346
|
+
readonly [key: string]: SubjectFunction;
|
|
347
|
+
};
|
|
348
|
+
/**
|
|
349
|
+
* The function under test, or a record of them.
|
|
350
|
+
*
|
|
351
|
+
* @internal
|
|
352
|
+
*/
|
|
353
|
+
type Subject = SubjectFunction | MemberRecord;
|
|
354
|
+
/**
|
|
355
|
+
* One key the impostor froze and the raw output it answered with: the subject itself, or a record member.
|
|
356
|
+
*
|
|
357
|
+
* @internal
|
|
358
|
+
*/
|
|
359
|
+
interface FrozenOutput {
|
|
360
|
+
readonly member: string;
|
|
361
|
+
readonly output: Opaque;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* One property's verdict against a subject's impostor: whether the property refuted the fake, the property it
|
|
365
|
+
* ran, and the raw outputs the fake froze for it.
|
|
366
|
+
*
|
|
367
|
+
* @internal
|
|
368
|
+
*/
|
|
369
|
+
interface Refutation {
|
|
370
|
+
/** `true` when the property falsified for the impostor, so the property does pin the subject down. */
|
|
371
|
+
readonly refuted: boolean;
|
|
372
|
+
readonly property: PropertyRun;
|
|
373
|
+
/** The property's identity hash, so a vacuous verdict's replay text can name it (R9, R11). */
|
|
374
|
+
readonly identity: number;
|
|
375
|
+
readonly frozen: ReadonlyArray<FrozenOutput>;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* The R12 repair verdict for one subject no property in the file refuted.
|
|
379
|
+
*
|
|
380
|
+
* @internal
|
|
381
|
+
*/
|
|
382
|
+
interface FileLedger {
|
|
383
|
+
readonly record: (subject: Subject, verdict: Refutation) => void;
|
|
384
|
+
/** Records a law kind exempt from the gate, so a refusal can list it (R6). */
|
|
385
|
+
readonly recordExempt: (name: string, kind: string) => void;
|
|
386
|
+
/** The single refusal when at least one subject is vacuous, or `undefined` when the file holds. */
|
|
387
|
+
readonly finalise: () => VacuousProperty | undefined;
|
|
388
|
+
}
|
|
389
|
+
declare namespace kinds_d_exports {
|
|
390
|
+
export { LawApi, LawKind, LawSubject, MetamorphicRelation, deterministicHolds, idempotentHolds, invariantHolds, metamorphicHolds, modelHolds, roundTripHolds, spreadValues };
|
|
391
|
+
}
|
|
392
|
+
/** @internal */
|
|
393
|
+
type LawKind = 'model' | 'metamorphic' | 'roundTrip' | 'invariant' | 'idempotent' | 'deterministic';
|
|
394
|
+
/**
|
|
395
|
+
* A subject of a law kind: a function of the generated values.
|
|
396
|
+
*
|
|
397
|
+
* @internal
|
|
398
|
+
*/
|
|
399
|
+
type LawSubject<G extends Gens, A> = (...values: ReadonlyArray<ElementValues<G>>) => A;
|
|
400
|
+
/**
|
|
401
|
+
* A metamorphic relation: the input transform, and how the two outputs relate (structural equality by default).
|
|
402
|
+
*
|
|
403
|
+
* @internal
|
|
404
|
+
*/
|
|
405
|
+
interface MetamorphicRelation<G extends Gens, A> {
|
|
406
|
+
readonly transform: (...values: ReadonlyArray<ElementValues<G>>) => Values<G>;
|
|
407
|
+
readonly relate?: (original: A, transformed: A) => boolean;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* The R13 law surface: four gated kinds and two declared exemptions.
|
|
411
|
+
*
|
|
412
|
+
* @internal
|
|
413
|
+
*/
|
|
414
|
+
interface LawApi {
|
|
415
|
+
readonly model: <const G extends Gens, A, N extends number, S extends LawSubject<G, A>>(name: string, spec: PropertySpec<G, S, N>, oracle: LawSubject<G, A>) => void;
|
|
416
|
+
readonly metamorphic: <const G extends Gens, A, N extends number, S extends LawSubject<G, A>>(name: string, spec: PropertySpec<G, S, N>, relation: MetamorphicRelation<G, A>) => void;
|
|
417
|
+
readonly roundTrip: <const G extends Gens, E, N extends number, S extends LawSubject<G, E>>(name: string, spec: PropertySpec<G, S, N>, decode: (encoded: E) => Values<G>) => void;
|
|
418
|
+
readonly invariant: <const G extends Gens, A, N extends number, S extends LawSubject<G, A>>(name: string, spec: PropertySpec<G, S, N>, holds: (output: A, ...values: ReadonlyArray<ElementValues<G>>) => boolean) => void;
|
|
419
|
+
readonly idempotent: <const G extends Gens, N extends number, S extends (value: ElementValues<G>) => ElementValues<G>>(name: string, spec: PropertySpec<G, S, N>) => void;
|
|
420
|
+
readonly deterministic: <const G extends Gens, A, N extends number, S extends LawSubject<G, A>>(name: string, spec: PropertySpec<G, S, N>) => void;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* The generated values as the argument list a law subject is called with.
|
|
424
|
+
*
|
|
425
|
+
* @internal
|
|
426
|
+
*/
|
|
427
|
+
declare const spreadValues: <G extends Gens>(values: Values<G>) => ReadonlyArray<ElementValues<G>>;
|
|
428
|
+
/** @internal */
|
|
429
|
+
declare const modelHolds: <G extends Gens, A>(oracle: LawSubject<G, A>) => (subject: LawSubject<G, A>, values: Values<G>) => boolean;
|
|
430
|
+
/** @internal */
|
|
431
|
+
declare const metamorphicHolds: <G extends Gens, A>(relation: MetamorphicRelation<G, A>) => (subject: LawSubject<G, A>, values: Values<G>) => boolean;
|
|
432
|
+
/** @internal */
|
|
433
|
+
declare const roundTripHolds: <G extends Gens, E>(decode: (encoded: E) => Values<G>) => (subject: LawSubject<G, E>, values: Values<G>) => boolean;
|
|
434
|
+
/** @internal */
|
|
435
|
+
declare const invariantHolds: <G extends Gens, A>(holds: (output: A, ...values: ReadonlyArray<ElementValues<G>>) => boolean) => (subject: LawSubject<G, A>, values: Values<G>) => boolean;
|
|
436
|
+
/** @internal */
|
|
437
|
+
declare const idempotentHolds: <G extends Gens>() => (subject: (value: ElementValues<G>) => ElementValues<G>, values: Values<G>) => boolean;
|
|
438
|
+
/** @internal */
|
|
439
|
+
declare const deterministicHolds: <G extends Gens, A>() => (subject: LawSubject<G, A>, values: Values<G>) => boolean;
|
|
440
|
+
declare namespace engine_d_exports {
|
|
441
|
+
export { ArbitraryInput, CoveragePredicate, ElementValues, Gens, PropApi, PropertyRuntime, PropertySpec, PropertySubject, PropertyTask, Values, makeProperty };
|
|
442
|
+
}
|
|
443
|
+
/** @internal */
|
|
444
|
+
type ArbitraryInput = Schema$1.Top | Arbitrary.Arbitrary<Schema$1.Top['Type']>;
|
|
445
|
+
/** @internal */
|
|
446
|
+
type Gens = ReadonlyArray<ArbitraryInput> | {
|
|
447
|
+
readonly [key: string]: ArbitraryInput;
|
|
448
|
+
};
|
|
449
|
+
type Generated<I> = I extends Schema$1.Schema<infer T> ? T : I extends Arbitrary.Arbitrary<infer A> ? A : never;
|
|
450
|
+
/** @internal */
|
|
451
|
+
type Values<G extends Gens> = { readonly [K in keyof G]: Generated<G[K]>; };
|
|
452
|
+
/** @internal */
|
|
453
|
+
type ElementValues<G extends Gens> = G extends ReadonlyArray<ArbitraryInput> ? Generated<G[number]> : never;
|
|
454
|
+
/** @internal */
|
|
455
|
+
type PropertySubject = Subject;
|
|
456
|
+
/** @internal */
|
|
457
|
+
type CoveragePredicate<G extends Gens> = (...values: ReadonlyArray<ElementValues<G>>) => boolean;
|
|
458
|
+
type PropertyRuns<N extends number> = number extends N ? number : `${N}` extends `-${bigint}` ? never : `${N}` extends `${bigint}` ? (N extends 0 ? never : N) : never;
|
|
459
|
+
/** @internal */
|
|
460
|
+
interface PropertySpec<G extends Gens, S extends PropertySubject, N extends number> {
|
|
461
|
+
readonly of: G;
|
|
462
|
+
readonly subject: S;
|
|
463
|
+
/**
|
|
464
|
+
* The property's own run count. Omitted, the configured default applies; when the run provides none,
|
|
465
|
+
* the built-in default of 100 does.
|
|
466
|
+
*/
|
|
467
|
+
readonly runs?: N & PropertyRuns<N>;
|
|
468
|
+
/** Labelled input predicates, each with the minimum share of runs it must see (R14). */
|
|
469
|
+
readonly cover?: Readonly<Record<string, readonly [CoveragePredicate<G>, number]>>;
|
|
470
|
+
/** The property's own check options, merged field by field over the provided and built-in defaults. */
|
|
471
|
+
readonly arbitrary?: Arbitrary.CheckOptions;
|
|
472
|
+
}
|
|
473
|
+
/** @internal */
|
|
474
|
+
interface PropertyTask {
|
|
475
|
+
/** The property's run-time identity (KTD3): its package, project-relative file and full name. */
|
|
476
|
+
readonly identity: TestIdentity;
|
|
477
|
+
/** The absolute path of the test file whose seed store is read and written beside it (KTD6); `null` keeps no store. */
|
|
478
|
+
readonly filepath: string | null;
|
|
479
|
+
/** The provided property budget, or `undefined` when the run provided none. */
|
|
480
|
+
readonly budget: ProvidedCheckDefaults | undefined;
|
|
481
|
+
}
|
|
482
|
+
/** @internal */
|
|
483
|
+
interface PropertyRuntime<R> {
|
|
484
|
+
/** Registers the property's program; the runtime runs it inside the test it registers, under that test's binding. */
|
|
485
|
+
readonly register: (name: string, program: (task: PropertyTask) => Effect$1.Effect<void, Cause.Cause<NonBooleanVerdict>, never>) => void;
|
|
486
|
+
readonly provide: <A, E>(effect: Effect$1.Effect<A, E, R>) => Effect$1.Effect<A, E, never>;
|
|
487
|
+
/** The file ledger every property of this runtime records its impostor verdict into (R20, KTD10). */
|
|
488
|
+
readonly ledger: FileLedger;
|
|
489
|
+
}
|
|
490
|
+
/** @internal */
|
|
491
|
+
interface PropApi<R> {
|
|
492
|
+
readonly prop: <const G extends Gens, S extends PropertySubject, N extends number>(name: string, spec: PropertySpec<G, S, N>, holds: (subject: S, values: Values<G>) => boolean) => void;
|
|
493
|
+
readonly effectProp: <const G extends Gens, S extends PropertySubject, N extends number, E>(name: string, spec: PropertySpec<G, S, N>, holds: (subject: S, values: Values<G>) => Effect$1.Effect<boolean, E, R>) => void;
|
|
494
|
+
readonly law: LawApi;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* The property surface over one runtime: `prop`, `effectProp`, and the R13 law kinds routed through the gate.
|
|
498
|
+
*
|
|
499
|
+
* @internal
|
|
500
|
+
*/
|
|
501
|
+
declare const makeProperty: <R>(runtime: PropertyRuntime<R>) => PropApi<R>;
|
|
502
|
+
//#endregion
|
|
503
|
+
export { VacuousProperty as A, witnessOf as C, ReplayUnreadable as D, PropertyRefuted as E, Breach as M, __exportAll as N, SeedStoreUnreadable as O, throwFailureRecord as S, NonBooleanVerdict as T, ReplayValue as _, PropertySubject as a, summaryOf as b, LawApi as c, ProvidedCheckDefaults as d, checkDefaultsKey as f, JsonValue as g, FailureRecordInput as h, PropertySpec as i, Witness as j, SelfModelLaw as k, kinds_d_exports as l, FailureRecord as m, Gens as n, Values as o, AttributeValue as p, PropApi as r, engine_d_exports as s, ArbitraryInput as t, PropertyBudget as u, TestIdentity as v, CoverageBelowMinimum as w, testIdentityOf as x, renderFailureRecord as y };
|