@organon-methodology/testing 0.3.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/LICENSE +21 -0
- package/README.md +80 -0
- package/dist/adapters/vitest.d.ts +49 -0
- package/dist/adapters/vitest.d.ts.map +1 -0
- package/dist/adapters/vitest.js +60 -0
- package/dist/adapters/vitest.js.map +1 -0
- package/dist/core/assert-custom.d.ts +43 -0
- package/dist/core/assert-custom.d.ts.map +1 -0
- package/dist/core/assert-custom.js +48 -0
- package/dist/core/assert-custom.js.map +1 -0
- package/dist/core/assert-exports-present.d.ts +52 -0
- package/dist/core/assert-exports-present.d.ts.map +1 -0
- package/dist/core/assert-exports-present.js +125 -0
- package/dist/core/assert-exports-present.js.map +1 -0
- package/dist/core/assert-file-exists.d.ts +41 -0
- package/dist/core/assert-file-exists.d.ts.map +1 -0
- package/dist/core/assert-file-exists.js +52 -0
- package/dist/core/assert-file-exists.js.map +1 -0
- package/dist/core/assert-max-value.d.ts +79 -0
- package/dist/core/assert-max-value.d.ts.map +1 -0
- package/dist/core/assert-max-value.js +90 -0
- package/dist/core/assert-max-value.js.map +1 -0
- package/dist/core/assert-naming-convention.d.ts +61 -0
- package/dist/core/assert-naming-convention.d.ts.map +1 -0
- package/dist/core/assert-naming-convention.js +92 -0
- package/dist/core/assert-naming-convention.js.map +1 -0
- package/dist/core/assert-no-side-effects.d.ts +55 -0
- package/dist/core/assert-no-side-effects.d.ts.map +1 -0
- package/dist/core/assert-no-side-effects.js +72 -0
- package/dist/core/assert-no-side-effects.js.map +1 -0
- package/dist/core/assertions/exports-present.d.ts +38 -0
- package/dist/core/assertions/exports-present.d.ts.map +1 -0
- package/dist/core/assertions/exports-present.js +47 -0
- package/dist/core/assertions/exports-present.js.map +1 -0
- package/dist/core/assertions/file-exists.d.ts +45 -0
- package/dist/core/assertions/file-exists.d.ts.map +1 -0
- package/dist/core/assertions/file-exists.js +46 -0
- package/dist/core/assertions/file-exists.js.map +1 -0
- package/dist/core/assertions/max-value.d.ts +77 -0
- package/dist/core/assertions/max-value.d.ts.map +1 -0
- package/dist/core/assertions/max-value.js +60 -0
- package/dist/core/assertions/max-value.js.map +1 -0
- package/dist/core/assertions/naming-convention.d.ts +51 -0
- package/dist/core/assertions/naming-convention.d.ts.map +1 -0
- package/dist/core/assertions/naming-convention.js +73 -0
- package/dist/core/assertions/naming-convention.js.map +1 -0
- package/dist/core/assertions/no-side-effects.d.ts +47 -0
- package/dist/core/assertions/no-side-effects.d.ts.map +1 -0
- package/dist/core/assertions/no-side-effects.js +70 -0
- package/dist/core/assertions/no-side-effects.js.map +1 -0
- package/dist/core/invariant-test.d.ts +128 -0
- package/dist/core/invariant-test.d.ts.map +1 -0
- package/dist/core/invariant-test.js +174 -0
- package/dist/core/invariant-test.js.map +1 -0
- package/dist/core/resolvers/file-resolver.d.ts +92 -0
- package/dist/core/resolvers/file-resolver.d.ts.map +1 -0
- package/dist/core/resolvers/file-resolver.js +103 -0
- package/dist/core/resolvers/file-resolver.js.map +1 -0
- package/dist/core/resolvers/import-resolver.d.ts +55 -0
- package/dist/core/resolvers/import-resolver.d.ts.map +1 -0
- package/dist/core/resolvers/import-resolver.js +99 -0
- package/dist/core/resolvers/import-resolver.js.map +1 -0
- package/dist/core/resolvers/node-fs.d.ts +13 -0
- package/dist/core/resolvers/node-fs.d.ts.map +1 -0
- package/dist/core/resolvers/node-fs.js +32 -0
- package/dist/core/resolvers/node-fs.js.map +1 -0
- package/dist/core/resolvers/parallel-reader.d.ts +35 -0
- package/dist/core/resolvers/parallel-reader.d.ts.map +1 -0
- package/dist/core/resolvers/parallel-reader.js +56 -0
- package/dist/core/resolvers/parallel-reader.js.map +1 -0
- package/dist/core/resolvers/string-resolver.d.ts +66 -0
- package/dist/core/resolvers/string-resolver.d.ts.map +1 -0
- package/dist/core/resolvers/string-resolver.js +127 -0
- package/dist/core/resolvers/string-resolver.js.map +1 -0
- package/dist/core/resolvers/types.d.ts +16 -0
- package/dist/core/resolvers/types.d.ts.map +1 -0
- package/dist/core/resolvers/types.js +10 -0
- package/dist/core/resolvers/types.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* validateFileExists — Pure assertion that validates file existence results.
|
|
3
|
+
*
|
|
4
|
+
* Invariants enforced:
|
|
5
|
+
* - INV-TEST-1 (assertion-logic-pure): No I/O imports. Operates on pre-resolved data.
|
|
6
|
+
* - INV-TEST-2 (fail-fast): Throws on violation (collects all before throwing for better diagnostics).
|
|
7
|
+
* - INV-TEST-6 (always-async): Public API is async; pure validators are sync.
|
|
8
|
+
* - INV-TEST-7 (composable): No module-level mutable state.
|
|
9
|
+
*
|
|
10
|
+
* This module must NEVER import fs, http, process, or any I/O module.
|
|
11
|
+
* File existence checking is handled by the resolver layer (core/resolvers/).
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Error thrown when a file-exists assertion fails.
|
|
15
|
+
* Contains structured violation data for programmatic access.
|
|
16
|
+
*/
|
|
17
|
+
export class FileExistsAssertionError extends Error {
|
|
18
|
+
violations;
|
|
19
|
+
constructor(violations) {
|
|
20
|
+
const details = violations
|
|
21
|
+
.map((v) => ` ${v.file}`)
|
|
22
|
+
.join('\n');
|
|
23
|
+
super(`File exists assertion failed: ${violations.length} missing file(s)\n${details}`);
|
|
24
|
+
this.name = 'FileExistsAssertionError';
|
|
25
|
+
this.violations = violations;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Validate that all checked files exist.
|
|
30
|
+
*
|
|
31
|
+
* This is a pure, synchronous function: no I/O, no side effects, deterministic.
|
|
32
|
+
*
|
|
33
|
+
* @throws {FileExistsAssertionError} if any file does not exist (INV-TEST-2: fail-fast)
|
|
34
|
+
*/
|
|
35
|
+
export function validateFileExists(entries) {
|
|
36
|
+
const violations = [];
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
if (!entry.exists) {
|
|
39
|
+
violations.push({ file: entry.file });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (violations.length > 0) {
|
|
43
|
+
throw new FileExistsAssertionError(violations);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=file-exists.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-exists.js","sourceRoot":"","sources":["../../../src/core/assertions/file-exists.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAoBH;;;GAGG;AACH,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACjC,UAAU,CAAwB;IAElD,YAAY,UAAiC;QAC3C,MAAM,OAAO,GAAG,UAAU;aACvB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;aACzB,IAAI,CAAC,IAAI,CAAC,CAAC;QAEd,KAAK,CACH,iCAAiC,UAAU,CAAC,MAAM,qBAAqB,OAAO,EAAE,CACjF,CAAC;QAEF,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAA0B;IAC3D,MAAM,UAAU,GAA0B,EAAE,CAAC;IAE7C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAClB,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,wBAAwB,CAAC,UAAU,CAAC,CAAC;IACjD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* assertMaxValue — Pure assertion that validates numeric values against a maximum.
|
|
3
|
+
*
|
|
4
|
+
* Invariants enforced:
|
|
5
|
+
* - INV-TEST-1 (assertion-logic-pure): No I/O imports. Operates on pre-resolved data.
|
|
6
|
+
* - INV-TEST-2 (fail-fast): Throws on violation (collects all before throwing for better diagnostics).
|
|
7
|
+
* - INV-TEST-6 (always-async): Public API is async; pure validators are sync.
|
|
8
|
+
* - INV-TEST-7 (composable): No module-level mutable state.
|
|
9
|
+
*
|
|
10
|
+
* This module must NEVER import fs, http, process, or any I/O module.
|
|
11
|
+
* File reading is handled by the resolver layer (core/resolvers/).
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* A single extracted value to validate against the maximum.
|
|
15
|
+
* This is the pre-resolved data structure fed by the resolver layer.
|
|
16
|
+
*/
|
|
17
|
+
export interface MaxValueEntry {
|
|
18
|
+
/** Path of the file where the value was found */
|
|
19
|
+
file: string;
|
|
20
|
+
/** The line number (1-based) where the value was found */
|
|
21
|
+
line: number;
|
|
22
|
+
/** The full line of text containing the value */
|
|
23
|
+
lineText: string;
|
|
24
|
+
/** The extracted numeric value */
|
|
25
|
+
value: number;
|
|
26
|
+
/** The raw string that was parsed as a number */
|
|
27
|
+
rawValue: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Options for the pure max-value assertion.
|
|
31
|
+
* Operates on pre-resolved values (no file paths or patterns).
|
|
32
|
+
*/
|
|
33
|
+
export interface ValidateMaxValueOptions {
|
|
34
|
+
/** Pre-resolved numeric values to check */
|
|
35
|
+
values: MaxValueEntry[];
|
|
36
|
+
/** Upper bound (inclusive) */
|
|
37
|
+
maxValue: number;
|
|
38
|
+
/** Optional unit for error messages (e.g., "seconds", "bytes") */
|
|
39
|
+
unit?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A violation found by the max-value assertion.
|
|
43
|
+
*/
|
|
44
|
+
export interface MaxValueViolation {
|
|
45
|
+
/** File where the violation occurred */
|
|
46
|
+
file: string;
|
|
47
|
+
/** Line number (1-based) */
|
|
48
|
+
line: number;
|
|
49
|
+
/** The line text */
|
|
50
|
+
lineText: string;
|
|
51
|
+
/** The actual value that exceeded the max */
|
|
52
|
+
actualValue: number;
|
|
53
|
+
/** The maximum allowed value */
|
|
54
|
+
maxValue: number;
|
|
55
|
+
/** Optional unit */
|
|
56
|
+
unit?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Error thrown when a max-value assertion fails.
|
|
60
|
+
* Contains structured violation data for programmatic access
|
|
61
|
+
* plus a human-readable message.
|
|
62
|
+
*/
|
|
63
|
+
export declare class MaxValueAssertionError extends Error {
|
|
64
|
+
readonly violations: MaxValueViolation[];
|
|
65
|
+
constructor(violations: MaxValueViolation[]);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Validate that all pre-resolved values do not exceed a maximum.
|
|
69
|
+
*
|
|
70
|
+
* This is a pure, synchronous function: no I/O, no side effects, deterministic.
|
|
71
|
+
* The resolver layer feeds pre-extracted values; this function only validates.
|
|
72
|
+
* Collects all violations before throwing for complete diagnostic output.
|
|
73
|
+
*
|
|
74
|
+
* @throws {MaxValueAssertionError} if any value exceeds maxValue (INV-TEST-2: fail-fast)
|
|
75
|
+
*/
|
|
76
|
+
export declare function validateMaxValue(options: ValidateMaxValueOptions): void;
|
|
77
|
+
//# sourceMappingURL=max-value.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"max-value.d.ts","sourceRoot":"","sources":["../../../src/core/assertions/max-value.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC,2CAA2C;IAC3C,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,SAAgB,UAAU,EAAE,iBAAiB,EAAE,CAAC;gBAEpC,UAAU,EAAE,iBAAiB,EAAE;CAe5C;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,uBAAuB,GAC/B,IAAI,CAqBN"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* assertMaxValue — Pure assertion that validates numeric values against a maximum.
|
|
3
|
+
*
|
|
4
|
+
* Invariants enforced:
|
|
5
|
+
* - INV-TEST-1 (assertion-logic-pure): No I/O imports. Operates on pre-resolved data.
|
|
6
|
+
* - INV-TEST-2 (fail-fast): Throws on violation (collects all before throwing for better diagnostics).
|
|
7
|
+
* - INV-TEST-6 (always-async): Public API is async; pure validators are sync.
|
|
8
|
+
* - INV-TEST-7 (composable): No module-level mutable state.
|
|
9
|
+
*
|
|
10
|
+
* This module must NEVER import fs, http, process, or any I/O module.
|
|
11
|
+
* File reading is handled by the resolver layer (core/resolvers/).
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Error thrown when a max-value assertion fails.
|
|
15
|
+
* Contains structured violation data for programmatic access
|
|
16
|
+
* plus a human-readable message.
|
|
17
|
+
*/
|
|
18
|
+
export class MaxValueAssertionError extends Error {
|
|
19
|
+
violations;
|
|
20
|
+
constructor(violations) {
|
|
21
|
+
const details = violations
|
|
22
|
+
.map((v) => {
|
|
23
|
+
const unitSuffix = v.unit ? ` ${v.unit}` : '';
|
|
24
|
+
return ` ${v.file}:${v.line} — found ${v.actualValue}${unitSuffix}, max allowed is ${v.maxValue}${unitSuffix}\n > ${v.lineText.trim()}`;
|
|
25
|
+
})
|
|
26
|
+
.join('\n');
|
|
27
|
+
super(`Max value assertion failed: ${violations.length} violation(s) found\n${details}`);
|
|
28
|
+
this.name = 'MaxValueAssertionError';
|
|
29
|
+
this.violations = violations;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validate that all pre-resolved values do not exceed a maximum.
|
|
34
|
+
*
|
|
35
|
+
* This is a pure, synchronous function: no I/O, no side effects, deterministic.
|
|
36
|
+
* The resolver layer feeds pre-extracted values; this function only validates.
|
|
37
|
+
* Collects all violations before throwing for complete diagnostic output.
|
|
38
|
+
*
|
|
39
|
+
* @throws {MaxValueAssertionError} if any value exceeds maxValue (INV-TEST-2: fail-fast)
|
|
40
|
+
*/
|
|
41
|
+
export function validateMaxValue(options) {
|
|
42
|
+
const { values, maxValue, unit } = options;
|
|
43
|
+
const violations = [];
|
|
44
|
+
for (const entry of values) {
|
|
45
|
+
if (entry.value > maxValue) {
|
|
46
|
+
violations.push({
|
|
47
|
+
file: entry.file,
|
|
48
|
+
line: entry.line,
|
|
49
|
+
lineText: entry.lineText,
|
|
50
|
+
actualValue: entry.value,
|
|
51
|
+
maxValue,
|
|
52
|
+
unit,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (violations.length > 0) {
|
|
57
|
+
throw new MaxValueAssertionError(violations);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=max-value.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"max-value.js","sourceRoot":"","sources":["../../../src/core/assertions/max-value.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAkDH;;;;GAIG;AACH,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAC/B,UAAU,CAAsB;IAEhD,YAAY,UAA+B;QACzC,MAAM,OAAO,GAAG,UAAU;aACvB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACT,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9C,OAAO,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,WAAW,GAAG,UAAU,oBAAoB,CAAC,CAAC,QAAQ,GAAG,UAAU,WAAW,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;QAC9I,CAAC,CAAC;aACD,IAAI,CAAC,IAAI,CAAC,CAAC;QAEd,KAAK,CACH,+BAA+B,UAAU,CAAC,MAAM,wBAAwB,OAAO,EAAE,CAClF,CAAC;QAEF,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAgC;IAEhC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IAE3C,MAAM,UAAU,GAAwB,EAAE,CAAC;IAE3C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,KAAK,GAAG,QAAQ,EAAE,CAAC;YAC3B,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,WAAW,EAAE,KAAK,CAAC,KAAK;gBACxB,QAAQ;gBACR,IAAI;aACL,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,sBAAsB,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* validateNamingConvention — Pure assertion that checks strings against naming conventions.
|
|
3
|
+
*
|
|
4
|
+
* Invariants enforced:
|
|
5
|
+
* - INV-TEST-1 (assertion-logic-pure): No I/O imports. Operates on pre-resolved data.
|
|
6
|
+
* - INV-TEST-2 (fail-fast): Throws on violation (collects all before throwing).
|
|
7
|
+
* - INV-TEST-7 (composable): No module-level mutable state.
|
|
8
|
+
*
|
|
9
|
+
* This module must NEVER import fs, http, process, or any I/O module.
|
|
10
|
+
*/
|
|
11
|
+
import type { ExtractedString } from '../resolvers/string-resolver.js';
|
|
12
|
+
/**
|
|
13
|
+
* Supported naming conventions.
|
|
14
|
+
*/
|
|
15
|
+
export type Convention = 'kebab-case' | 'camelCase' | 'PascalCase' | 'SCREAMING_SNAKE_CASE' | 'snake_case';
|
|
16
|
+
/**
|
|
17
|
+
* Check if a string matches a naming convention.
|
|
18
|
+
*/
|
|
19
|
+
export declare function matchesConvention(value: string, convention: Convention): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* A naming convention violation.
|
|
22
|
+
*/
|
|
23
|
+
export interface NamingConventionViolation {
|
|
24
|
+
/** Path of the file */
|
|
25
|
+
file: string;
|
|
26
|
+
/** The line number (1-based), or 0 for file-level checks */
|
|
27
|
+
line: number;
|
|
28
|
+
/** The name that violates the convention */
|
|
29
|
+
value: string;
|
|
30
|
+
/** The expected convention */
|
|
31
|
+
convention: Convention;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Error thrown when a naming convention assertion fails.
|
|
35
|
+
*/
|
|
36
|
+
export declare class NamingConventionAssertionError extends Error {
|
|
37
|
+
readonly violations: NamingConventionViolation[];
|
|
38
|
+
constructor(violations: NamingConventionViolation[]);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Validate that all extracted strings match a naming convention.
|
|
42
|
+
*
|
|
43
|
+
* This is a pure, synchronous function: no I/O, no side effects, deterministic.
|
|
44
|
+
*
|
|
45
|
+
* @param strings - Pre-resolved strings to validate
|
|
46
|
+
* @param convention - The naming convention to enforce
|
|
47
|
+
* @param exceptions - Names to exclude from validation
|
|
48
|
+
* @throws {NamingConventionAssertionError} if any string violates the convention
|
|
49
|
+
*/
|
|
50
|
+
export declare function validateNamingConvention(strings: ExtractedString[], convention: Convention, exceptions?: string[]): void;
|
|
51
|
+
//# sourceMappingURL=naming-convention.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naming-convention.d.ts","sourceRoot":"","sources":["../../../src/core/assertions/naming-convention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAEvE;;GAEG;AACH,MAAM,MAAM,UAAU,GAClB,YAAY,GACZ,WAAW,GACX,YAAY,GACZ,sBAAsB,GACtB,YAAY,CAAC;AAajB;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,UAAU,GACrB,OAAO,CAET;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,UAAU,EAAE,UAAU,CAAC;CACxB;AAED;;GAEG;AACH,qBAAa,8BAA+B,SAAQ,KAAK;IACvD,SAAgB,UAAU,EAAE,yBAAyB,EAAE,CAAC;gBAE5C,UAAU,EAAE,yBAAyB,EAAE;CAepD;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,eAAe,EAAE,EAC1B,UAAU,EAAE,UAAU,EACtB,UAAU,GAAE,MAAM,EAAO,GACxB,IAAI,CAoBN"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* validateNamingConvention — Pure assertion that checks strings against naming conventions.
|
|
3
|
+
*
|
|
4
|
+
* Invariants enforced:
|
|
5
|
+
* - INV-TEST-1 (assertion-logic-pure): No I/O imports. Operates on pre-resolved data.
|
|
6
|
+
* - INV-TEST-2 (fail-fast): Throws on violation (collects all before throwing).
|
|
7
|
+
* - INV-TEST-7 (composable): No module-level mutable state.
|
|
8
|
+
*
|
|
9
|
+
* This module must NEVER import fs, http, process, or any I/O module.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Regex patterns for each convention.
|
|
13
|
+
*/
|
|
14
|
+
const CONVENTION_PATTERNS = {
|
|
15
|
+
'kebab-case': /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/,
|
|
16
|
+
'camelCase': /^[a-z][a-zA-Z0-9]*$/,
|
|
17
|
+
'PascalCase': /^[A-Z][a-zA-Z0-9]*$/,
|
|
18
|
+
'SCREAMING_SNAKE_CASE': /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$/,
|
|
19
|
+
'snake_case': /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/,
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Check if a string matches a naming convention.
|
|
23
|
+
*/
|
|
24
|
+
export function matchesConvention(value, convention) {
|
|
25
|
+
return CONVENTION_PATTERNS[convention].test(value);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Error thrown when a naming convention assertion fails.
|
|
29
|
+
*/
|
|
30
|
+
export class NamingConventionAssertionError extends Error {
|
|
31
|
+
violations;
|
|
32
|
+
constructor(violations) {
|
|
33
|
+
const details = violations
|
|
34
|
+
.map((v) => {
|
|
35
|
+
const location = v.line > 0 ? `${v.file}:${v.line}` : v.file;
|
|
36
|
+
return ` ${location} — "${v.value}" does not match ${v.convention}`;
|
|
37
|
+
})
|
|
38
|
+
.join('\n');
|
|
39
|
+
super(`Naming convention assertion failed: ${violations.length} violation(s) found\n${details}`);
|
|
40
|
+
this.name = 'NamingConventionAssertionError';
|
|
41
|
+
this.violations = violations;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Validate that all extracted strings match a naming convention.
|
|
46
|
+
*
|
|
47
|
+
* This is a pure, synchronous function: no I/O, no side effects, deterministic.
|
|
48
|
+
*
|
|
49
|
+
* @param strings - Pre-resolved strings to validate
|
|
50
|
+
* @param convention - The naming convention to enforce
|
|
51
|
+
* @param exceptions - Names to exclude from validation
|
|
52
|
+
* @throws {NamingConventionAssertionError} if any string violates the convention
|
|
53
|
+
*/
|
|
54
|
+
export function validateNamingConvention(strings, convention, exceptions = []) {
|
|
55
|
+
const exceptionSet = new Set(exceptions);
|
|
56
|
+
const violations = [];
|
|
57
|
+
for (const entry of strings) {
|
|
58
|
+
if (exceptionSet.has(entry.value))
|
|
59
|
+
continue;
|
|
60
|
+
if (!matchesConvention(entry.value, convention)) {
|
|
61
|
+
violations.push({
|
|
62
|
+
file: entry.file,
|
|
63
|
+
line: entry.line,
|
|
64
|
+
value: entry.value,
|
|
65
|
+
convention,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (violations.length > 0) {
|
|
70
|
+
throw new NamingConventionAssertionError(violations);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=naming-convention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naming-convention.js","sourceRoot":"","sources":["../../../src/core/assertions/naming-convention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAcH;;GAEG;AACH,MAAM,mBAAmB,GAA+B;IACtD,YAAY,EAAE,+BAA+B;IAC7C,WAAW,EAAE,qBAAqB;IAClC,YAAY,EAAE,qBAAqB;IACnC,sBAAsB,EAAE,+BAA+B;IACvD,YAAY,EAAE,+BAA+B;CAC9C,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAa,EACb,UAAsB;IAEtB,OAAO,mBAAmB,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACrD,CAAC;AAgBD;;GAEG;AACH,MAAM,OAAO,8BAA+B,SAAQ,KAAK;IACvC,UAAU,CAA8B;IAExD,YAAY,UAAuC;QACjD,MAAM,OAAO,GAAG,UAAU;aACvB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACT,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7D,OAAO,KAAK,QAAQ,OAAO,CAAC,CAAC,KAAK,oBAAoB,CAAC,CAAC,UAAU,EAAE,CAAC;QACvE,CAAC,CAAC;aACD,IAAI,CAAC,IAAI,CAAC,CAAC;QAEd,KAAK,CACH,uCAAuC,UAAU,CAAC,MAAM,wBAAwB,OAAO,EAAE,CAC1F,CAAC;QAEF,IAAI,CAAC,IAAI,GAAG,gCAAgC,CAAC;QAC7C,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CACtC,OAA0B,EAC1B,UAAsB,EACtB,aAAuB,EAAE;IAEzB,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;IACzC,MAAM,UAAU,GAAgC,EAAE,CAAC;IAEnD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,SAAS;QAE5C,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,CAAC;YAChD,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,UAAU;aACX,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,8BAA8B,CAAC,UAAU,CAAC,CAAC;IACvD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* validateNoSideEffects — Pure assertion that checks imports against a forbidden list.
|
|
3
|
+
*
|
|
4
|
+
* Invariants enforced:
|
|
5
|
+
* - INV-TEST-1 (assertion-logic-pure): No I/O imports. Operates on pre-resolved data.
|
|
6
|
+
* - INV-TEST-2 (fail-fast): Throws on violation (collects all before throwing).
|
|
7
|
+
* - INV-TEST-7 (composable): No module-level mutable state.
|
|
8
|
+
*
|
|
9
|
+
* This module must NEVER import fs, http, process, or any I/O module.
|
|
10
|
+
*/
|
|
11
|
+
import type { ImportMatch } from '../resolvers/import-resolver.js';
|
|
12
|
+
/**
|
|
13
|
+
* A violation: a forbidden module import was found.
|
|
14
|
+
*/
|
|
15
|
+
export interface NoSideEffectsViolation {
|
|
16
|
+
/** Path of the file containing the forbidden import */
|
|
17
|
+
file: string;
|
|
18
|
+
/** The line number (1-based) */
|
|
19
|
+
line: number;
|
|
20
|
+
/** The full line text */
|
|
21
|
+
lineText: string;
|
|
22
|
+
/** The forbidden module that was imported */
|
|
23
|
+
module: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Error thrown when a no-side-effects assertion fails.
|
|
27
|
+
*/
|
|
28
|
+
export declare class NoSideEffectsAssertionError extends Error {
|
|
29
|
+
readonly violations: NoSideEffectsViolation[];
|
|
30
|
+
constructor(violations: NoSideEffectsViolation[]);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check whether a module specifier matches a forbidden module.
|
|
34
|
+
*
|
|
35
|
+
* Matches both bare specifier and node: prefixed specifier.
|
|
36
|
+
* For example, forbidden 'fs' matches 'fs', 'node:fs', 'fs/promises', 'node:fs/promises'.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isForbidden(moduleSpecifier: string, forbiddenModules: string[]): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Validate that no imports match the forbidden module list.
|
|
41
|
+
*
|
|
42
|
+
* This is a pure, synchronous function: no I/O, no side effects, deterministic.
|
|
43
|
+
*
|
|
44
|
+
* @throws {NoSideEffectsAssertionError} if any forbidden import is found
|
|
45
|
+
*/
|
|
46
|
+
export declare function validateNoSideEffects(imports: ImportMatch[], forbiddenModules: string[]): void;
|
|
47
|
+
//# sourceMappingURL=no-side-effects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-side-effects.d.ts","sourceRoot":"","sources":["../../../src/core/assertions/no-side-effects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,SAAgB,UAAU,EAAE,sBAAsB,EAAE,CAAC;gBAEzC,UAAU,EAAE,sBAAsB,EAAE;CAejD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,eAAe,EAAE,MAAM,EACvB,gBAAgB,EAAE,MAAM,EAAE,GACzB,OAAO,CAWT;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,WAAW,EAAE,EACtB,gBAAgB,EAAE,MAAM,EAAE,GACzB,IAAI,CAiBN"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* validateNoSideEffects — Pure assertion that checks imports against a forbidden list.
|
|
3
|
+
*
|
|
4
|
+
* Invariants enforced:
|
|
5
|
+
* - INV-TEST-1 (assertion-logic-pure): No I/O imports. Operates on pre-resolved data.
|
|
6
|
+
* - INV-TEST-2 (fail-fast): Throws on violation (collects all before throwing).
|
|
7
|
+
* - INV-TEST-7 (composable): No module-level mutable state.
|
|
8
|
+
*
|
|
9
|
+
* This module must NEVER import fs, http, process, or any I/O module.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Error thrown when a no-side-effects assertion fails.
|
|
13
|
+
*/
|
|
14
|
+
export class NoSideEffectsAssertionError extends Error {
|
|
15
|
+
violations;
|
|
16
|
+
constructor(violations) {
|
|
17
|
+
const details = violations
|
|
18
|
+
.map((v) => ` ${v.file}:${v.line} — imports forbidden module "${v.module}"\n > ${v.lineText.trim()}`)
|
|
19
|
+
.join('\n');
|
|
20
|
+
super(`No side effects assertion failed: ${violations.length} forbidden import(s) found\n${details}`);
|
|
21
|
+
this.name = 'NoSideEffectsAssertionError';
|
|
22
|
+
this.violations = violations;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Check whether a module specifier matches a forbidden module.
|
|
27
|
+
*
|
|
28
|
+
* Matches both bare specifier and node: prefixed specifier.
|
|
29
|
+
* For example, forbidden 'fs' matches 'fs', 'node:fs', 'fs/promises', 'node:fs/promises'.
|
|
30
|
+
*/
|
|
31
|
+
export function isForbidden(moduleSpecifier, forbiddenModules) {
|
|
32
|
+
for (const forbidden of forbiddenModules) {
|
|
33
|
+
// Exact match
|
|
34
|
+
if (moduleSpecifier === forbidden)
|
|
35
|
+
return true;
|
|
36
|
+
// node:-prefixed match
|
|
37
|
+
if (moduleSpecifier === `node:${forbidden}`)
|
|
38
|
+
return true;
|
|
39
|
+
// Subpath match (e.g., 'fs/promises' when 'fs' is forbidden)
|
|
40
|
+
if (moduleSpecifier.startsWith(`${forbidden}/`))
|
|
41
|
+
return true;
|
|
42
|
+
if (moduleSpecifier.startsWith(`node:${forbidden}/`))
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Validate that no imports match the forbidden module list.
|
|
49
|
+
*
|
|
50
|
+
* This is a pure, synchronous function: no I/O, no side effects, deterministic.
|
|
51
|
+
*
|
|
52
|
+
* @throws {NoSideEffectsAssertionError} if any forbidden import is found
|
|
53
|
+
*/
|
|
54
|
+
export function validateNoSideEffects(imports, forbiddenModules) {
|
|
55
|
+
const violations = [];
|
|
56
|
+
for (const imp of imports) {
|
|
57
|
+
if (isForbidden(imp.module, forbiddenModules)) {
|
|
58
|
+
violations.push({
|
|
59
|
+
file: imp.file,
|
|
60
|
+
line: imp.line,
|
|
61
|
+
lineText: imp.lineText,
|
|
62
|
+
module: imp.module,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (violations.length > 0) {
|
|
67
|
+
throw new NoSideEffectsAssertionError(violations);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=no-side-effects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-side-effects.js","sourceRoot":"","sources":["../../../src/core/assertions/no-side-effects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAkBH;;GAEG;AACH,MAAM,OAAO,2BAA4B,SAAQ,KAAK;IACpC,UAAU,CAA2B;IAErD,YAAY,UAAoC;QAC9C,MAAM,OAAO,GAAG,UAAU;aACvB,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,gCAAgC,CAAC,CAAC,MAAM,YAAY,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAC/F;aACA,IAAI,CAAC,IAAI,CAAC,CAAC;QAEd,KAAK,CACH,qCAAqC,UAAU,CAAC,MAAM,+BAA+B,OAAO,EAAE,CAC/F,CAAC;QAEF,IAAI,CAAC,IAAI,GAAG,6BAA6B,CAAC;QAC1C,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,eAAuB,EACvB,gBAA0B;IAE1B,KAAK,MAAM,SAAS,IAAI,gBAAgB,EAAE,CAAC;QACzC,cAAc;QACd,IAAI,eAAe,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QAC/C,uBAAuB;QACvB,IAAI,eAAe,KAAK,QAAQ,SAAS,EAAE;YAAE,OAAO,IAAI,CAAC;QACzD,6DAA6D;QAC7D,IAAI,eAAe,CAAC,UAAU,CAAC,GAAG,SAAS,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QAC7D,IAAI,eAAe,CAAC,UAAU,CAAC,QAAQ,SAAS,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;IACpE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAAsB,EACtB,gBAA0B;IAE1B,MAAM,UAAU,GAA6B,EAAE,CAAC;IAEhD,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAAE,CAAC;YAC9C,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,QAAQ,EAAE,GAAG,CAAC,QAAQ;gBACtB,MAAM,EAAE,GAAG,CAAC,MAAM;aACnB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,2BAA2B,CAAC,UAAU,CAAC,CAAC;IACpD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* testInvariant — Wrapper that links tests to invariant IDs.
|
|
3
|
+
*
|
|
4
|
+
* Invariants enforced:
|
|
5
|
+
* - INV-TEST-3 (invariant-id-required): Every test must link to an invariant ID.
|
|
6
|
+
* - INV-TEST-4 (framework-agnostic-core): Core logic has zero test-framework deps.
|
|
7
|
+
* - INV-TEST-6 (always-async): Test function is always async.
|
|
8
|
+
* - INV-TEST-7 (composable): No module-level mutable state that affects test outcomes.
|
|
9
|
+
*
|
|
10
|
+
* Design:
|
|
11
|
+
* - The metadata registry is a read-only record of test registrations.
|
|
12
|
+
* It stores what tests were registered but does not affect test execution.
|
|
13
|
+
* Tests execute identically regardless of registry state (INV-TEST-7).
|
|
14
|
+
* - The wrapper delegates to a configurable test runner function.
|
|
15
|
+
* By default, it uses a pass-through runner that just calls the test function.
|
|
16
|
+
* Adapters (e.g., vitest.ts) provide framework-specific runners.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Metadata about a registered invariant test.
|
|
20
|
+
* Stored in the registry for coverage tracking.
|
|
21
|
+
*/
|
|
22
|
+
export interface InvariantTestMetadata {
|
|
23
|
+
/** Invariant ID (e.g., "INV-CACHE-1") */
|
|
24
|
+
invariantId: string;
|
|
25
|
+
/** Human-readable description of what the test verifies */
|
|
26
|
+
description: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A test function that verifies an invariant.
|
|
30
|
+
* Always async (INV-TEST-6).
|
|
31
|
+
*/
|
|
32
|
+
export type InvariantTestFn = () => Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* A test runner function provided by framework adapters.
|
|
35
|
+
* Receives the test name, the async test function, and the invariant metadata.
|
|
36
|
+
* Framework adapters (e.g., Vitest adapter) wrap this to call `it()` or `test()`.
|
|
37
|
+
*/
|
|
38
|
+
export type TestRunner = (testName: string, testFn: InvariantTestFn, metadata: InvariantTestMetadata) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Options for configuring testInvariant behavior.
|
|
41
|
+
*/
|
|
42
|
+
export interface TestInvariantOptions {
|
|
43
|
+
/** Custom test runner (default: passthrough that executes the function) */
|
|
44
|
+
runner?: TestRunner;
|
|
45
|
+
/** Custom registry for tracking test registrations (default: global registry) */
|
|
46
|
+
registry?: InvariantTestRegistry;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Error thrown when testInvariant is called with invalid arguments.
|
|
50
|
+
*/
|
|
51
|
+
export declare class InvariantTestError extends Error {
|
|
52
|
+
constructor(message: string);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Validate an invariant ID format.
|
|
56
|
+
*
|
|
57
|
+
* Valid format: PREFIX-WORD-NUMBER (e.g., "INV-CACHE-1", "INV-TEST-42")
|
|
58
|
+
* Must be non-empty and follow the ID convention.
|
|
59
|
+
*/
|
|
60
|
+
export declare function validateInvariantId(id: string): void;
|
|
61
|
+
/**
|
|
62
|
+
* Validate a test description.
|
|
63
|
+
*/
|
|
64
|
+
export declare function validateDescription(description: string): void;
|
|
65
|
+
/**
|
|
66
|
+
* Validate a test function.
|
|
67
|
+
*/
|
|
68
|
+
export declare function validateTestFn(testFn: unknown): asserts testFn is InvariantTestFn;
|
|
69
|
+
/**
|
|
70
|
+
* Create a new invariant test registry.
|
|
71
|
+
*
|
|
72
|
+
* The registry is a simple container that tracks which invariant IDs have
|
|
73
|
+
* been registered with testInvariant(). It supports:
|
|
74
|
+
* - Registering tests (invariant ID + description)
|
|
75
|
+
* - Querying registered tests (for coverage calculation)
|
|
76
|
+
* - Clearing (for test isolation)
|
|
77
|
+
*
|
|
78
|
+
* The registry does NOT affect test execution. Tests run identically
|
|
79
|
+
* regardless of registry state (INV-TEST-7: composable).
|
|
80
|
+
*/
|
|
81
|
+
export interface InvariantTestRegistry {
|
|
82
|
+
/** Register a test for an invariant */
|
|
83
|
+
register(metadata: InvariantTestMetadata): void;
|
|
84
|
+
/** Get all registered test metadata (snapshot, not a live reference) */
|
|
85
|
+
getAll(): InvariantTestMetadata[];
|
|
86
|
+
/** Check if an invariant ID has been registered */
|
|
87
|
+
has(invariantId: string): boolean;
|
|
88
|
+
/** Get the count of registered tests (may exceed distinct invariants if multiple tests cover the same ID) */
|
|
89
|
+
size(): number;
|
|
90
|
+
/** Get the count of distinct invariant IDs covered */
|
|
91
|
+
coveredCount(): number;
|
|
92
|
+
/** Clear all registrations (for test isolation) */
|
|
93
|
+
clear(): void;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Create a new empty registry instance.
|
|
97
|
+
*/
|
|
98
|
+
export declare function createRegistry(): InvariantTestRegistry;
|
|
99
|
+
/**
|
|
100
|
+
* Get the default global registry for coverage reporting.
|
|
101
|
+
*/
|
|
102
|
+
export declare function getDefaultRegistry(): InvariantTestRegistry;
|
|
103
|
+
/**
|
|
104
|
+
* Register and declare an invariant test.
|
|
105
|
+
*
|
|
106
|
+
* This is the primary API for linking tests to invariant IDs (INV-TEST-3).
|
|
107
|
+
*
|
|
108
|
+
* Usage:
|
|
109
|
+
* ```typescript
|
|
110
|
+
* testInvariant('INV-CACHE-1', 'cache TTL max 24h', async () => {
|
|
111
|
+
* await assertMaxValue({ ... });
|
|
112
|
+
* });
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* What it does:
|
|
116
|
+
* 1. Validates inputs (invariant ID, description, test function)
|
|
117
|
+
* 2. Registers the test in the metadata registry (for coverage tracking)
|
|
118
|
+
* 3. Delegates to the configured test runner (framework adapter)
|
|
119
|
+
*
|
|
120
|
+
* @param invariantId - The invariant ID this test verifies (e.g., "INV-CACHE-1")
|
|
121
|
+
* @param description - Human-readable description of what the test checks
|
|
122
|
+
* @param testFn - Async function that performs assertions
|
|
123
|
+
* @param options - Optional configuration (custom runner, custom registry)
|
|
124
|
+
*
|
|
125
|
+
* @throws {InvariantTestError} if inputs are invalid
|
|
126
|
+
*/
|
|
127
|
+
export declare function testInvariant(invariantId: string, description: string, testFn: InvariantTestFn, options?: TestInvariantOptions): void;
|
|
128
|
+
//# sourceMappingURL=invariant-test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invariant-test.d.ts","sourceRoot":"","sources":["../../src/core/invariant-test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAElD;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,CACvB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,qBAAqB,KAC5B,IAAI,CAAC;AAEV;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CAClC;AAMD;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAmBpD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAa7D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,IAAI,eAAe,CAMjF;AAMD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,qBAAqB;IACpC,uCAAuC;IACvC,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAChD,wEAAwE;IACxE,MAAM,IAAI,qBAAqB,EAAE,CAAC;IAClC,mDAAmD;IACnD,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,6GAA6G;IAC7G,IAAI,IAAI,MAAM,CAAC;IACf,sDAAsD;IACtD,YAAY,IAAI,MAAM,CAAC;IACvB,mDAAmD;IACnD,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,qBAAqB,CA4BtD;AAaD;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,qBAAqB,CAE1D;AA2BD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,eAAe,EACvB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,IAAI,CAoBN"}
|