@gnapi/cotester 1.2.5 → 1.2.6
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/dist/auditLogger.d.ts +46 -0
- package/dist/auditLogger.js +107 -0
- package/dist/auditLogger.js.map +1 -0
- package/dist/cli.js +10 -0
- package/dist/cli.js.map +1 -1
- package/dist/configManager.d.ts +16 -0
- package/dist/configManager.js +84 -17
- package/dist/configManager.js.map +1 -1
- package/dist/fileWorker.js +6 -1
- package/dist/fileWorker.js.map +1 -1
- package/dist/generator.d.ts +24 -1
- package/dist/generator.js +27 -8
- package/dist/generator.js.map +1 -1
- package/dist/importRepairer.d.ts +22 -0
- package/dist/importRepairer.js +226 -0
- package/dist/importRepairer.js.map +1 -0
- package/dist/interfaceShapeResolver.js +8 -3
- package/dist/interfaceShapeResolver.js.map +1 -1
- package/dist/migrator.d.ts +49 -0
- package/dist/migrator.js +335 -0
- package/dist/migrator.js.map +1 -0
- package/dist/mockGenerator.js +3 -1
- package/dist/mockGenerator.js.map +1 -1
- package/dist/sensitiveValueDetector.d.ts +62 -0
- package/dist/sensitiveValueDetector.js +147 -0
- package/dist/sensitiveValueDetector.js.map +1 -0
- package/dist/watcher.js +10 -1
- package/dist/watcher.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generation Audit Log (Feature 21)
|
|
3
|
+
*
|
|
4
|
+
* Appends a structured JSONL entry to `cotester/.audit.jsonl` every time
|
|
5
|
+
* cotester generates or updates a test file. Each line is a self-contained
|
|
6
|
+
* JSON object (JSON Lines format) for easy streaming/grep/compliance queries.
|
|
7
|
+
*
|
|
8
|
+
* Entry shape:
|
|
9
|
+
* {
|
|
10
|
+
* "timestamp": "2026-03-06T10:00:00.000Z", // ISO-8601 UTC
|
|
11
|
+
* "file": "src/services/user.service.ts", // relative to projectRoot
|
|
12
|
+
* "added": ["createUser", "deleteUser"], // new describe blocks written
|
|
13
|
+
* "merged": ["updateUser"], // sig-changed blocks (warning injected)
|
|
14
|
+
* "skipped": ["getUsers"] // already covered, untouched
|
|
15
|
+
* }
|
|
16
|
+
*/
|
|
17
|
+
export interface AuditEntry {
|
|
18
|
+
/** ISO-8601 UTC timestamp of the generation run */
|
|
19
|
+
timestamp: string;
|
|
20
|
+
/** Source file path relative to projectRoot, forward-slash separated */
|
|
21
|
+
file: string;
|
|
22
|
+
/** Function/method names for which a new describe block was written */
|
|
23
|
+
added: string[];
|
|
24
|
+
/** Function/method names whose signature changed (describe existed, warning injected) */
|
|
25
|
+
merged: string[];
|
|
26
|
+
/** Function/method names already covered and left untouched */
|
|
27
|
+
skipped: string[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Append one audit entry to `cotester/.audit.jsonl`.
|
|
31
|
+
*
|
|
32
|
+
* - In dry-run mode the entry is printed to stdout instead of written to disk.
|
|
33
|
+
* - Entries with all-empty lists (nothing added, merged, or skipped) are
|
|
34
|
+
* silently suppressed — they carry no useful information.
|
|
35
|
+
*/
|
|
36
|
+
export declare function writeAuditEntry(entry: AuditEntry, projectRoot: string, dryRun?: boolean): void;
|
|
37
|
+
/**
|
|
38
|
+
* Build an AuditEntry from the result of a test-file generation run.
|
|
39
|
+
*
|
|
40
|
+
* @param srcFilePath Absolute path to the source file.
|
|
41
|
+
* @param projectRoot Absolute path to the project root.
|
|
42
|
+
* @param added Function names for newly created describe blocks.
|
|
43
|
+
* @param merged Function names whose signature changed.
|
|
44
|
+
* @param skipped Function names already covered with no changes.
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildAuditEntry(srcFilePath: string, projectRoot: string, added: string[], merged: string[], skipped: string[]): AuditEntry;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Generation Audit Log (Feature 21)
|
|
4
|
+
*
|
|
5
|
+
* Appends a structured JSONL entry to `cotester/.audit.jsonl` every time
|
|
6
|
+
* cotester generates or updates a test file. Each line is a self-contained
|
|
7
|
+
* JSON object (JSON Lines format) for easy streaming/grep/compliance queries.
|
|
8
|
+
*
|
|
9
|
+
* Entry shape:
|
|
10
|
+
* {
|
|
11
|
+
* "timestamp": "2026-03-06T10:00:00.000Z", // ISO-8601 UTC
|
|
12
|
+
* "file": "src/services/user.service.ts", // relative to projectRoot
|
|
13
|
+
* "added": ["createUser", "deleteUser"], // new describe blocks written
|
|
14
|
+
* "merged": ["updateUser"], // sig-changed blocks (warning injected)
|
|
15
|
+
* "skipped": ["getUsers"] // already covered, untouched
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
21
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
22
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
23
|
+
}
|
|
24
|
+
Object.defineProperty(o, k2, desc);
|
|
25
|
+
}) : (function(o, m, k, k2) {
|
|
26
|
+
if (k2 === undefined) k2 = k;
|
|
27
|
+
o[k2] = m[k];
|
|
28
|
+
}));
|
|
29
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
30
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
31
|
+
}) : function(o, v) {
|
|
32
|
+
o["default"] = v;
|
|
33
|
+
});
|
|
34
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
35
|
+
var ownKeys = function(o) {
|
|
36
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
37
|
+
var ar = [];
|
|
38
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
39
|
+
return ar;
|
|
40
|
+
};
|
|
41
|
+
return ownKeys(o);
|
|
42
|
+
};
|
|
43
|
+
return function (mod) {
|
|
44
|
+
if (mod && mod.__esModule) return mod;
|
|
45
|
+
var result = {};
|
|
46
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
47
|
+
__setModuleDefault(result, mod);
|
|
48
|
+
return result;
|
|
49
|
+
};
|
|
50
|
+
})();
|
|
51
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
|
+
exports.writeAuditEntry = writeAuditEntry;
|
|
53
|
+
exports.buildAuditEntry = buildAuditEntry;
|
|
54
|
+
const fs = __importStar(require("fs"));
|
|
55
|
+
const path = __importStar(require("path"));
|
|
56
|
+
const utils_1 = require("./utils");
|
|
57
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
58
|
+
const AUDIT_FILE = path.join("cotester", ".audit.jsonl");
|
|
59
|
+
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
60
|
+
/**
|
|
61
|
+
* Append one audit entry to `cotester/.audit.jsonl`.
|
|
62
|
+
*
|
|
63
|
+
* - In dry-run mode the entry is printed to stdout instead of written to disk.
|
|
64
|
+
* - Entries with all-empty lists (nothing added, merged, or skipped) are
|
|
65
|
+
* silently suppressed — they carry no useful information.
|
|
66
|
+
*/
|
|
67
|
+
function writeAuditEntry(entry, projectRoot, dryRun = false) {
|
|
68
|
+
// Suppress empty entries (e.g. file had no exported functions)
|
|
69
|
+
if (entry.added.length === 0 &&
|
|
70
|
+
entry.merged.length === 0 &&
|
|
71
|
+
entry.skipped.length === 0) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const line = JSON.stringify(entry);
|
|
75
|
+
if (dryRun) {
|
|
76
|
+
(0, utils_1.log)(`[audit] ${line}`, "info");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const auditPath = path.join(projectRoot, AUDIT_FILE);
|
|
80
|
+
(0, utils_1.ensureDir)(path.dirname(auditPath));
|
|
81
|
+
try {
|
|
82
|
+
fs.appendFileSync(auditPath, line + "\n", "utf-8");
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
// Non-fatal — never block generation due to audit write failure
|
|
86
|
+
(0, utils_1.log)(` Warning: could not write audit log: ${err.message}`, "warn");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Build an AuditEntry from the result of a test-file generation run.
|
|
91
|
+
*
|
|
92
|
+
* @param srcFilePath Absolute path to the source file.
|
|
93
|
+
* @param projectRoot Absolute path to the project root.
|
|
94
|
+
* @param added Function names for newly created describe blocks.
|
|
95
|
+
* @param merged Function names whose signature changed.
|
|
96
|
+
* @param skipped Function names already covered with no changes.
|
|
97
|
+
*/
|
|
98
|
+
function buildAuditEntry(srcFilePath, projectRoot, added, merged, skipped) {
|
|
99
|
+
return {
|
|
100
|
+
timestamp: new Date().toISOString(),
|
|
101
|
+
file: path.relative(projectRoot, srcFilePath).replace(/\\/g, "/"),
|
|
102
|
+
added,
|
|
103
|
+
merged,
|
|
104
|
+
skipped,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=auditLogger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auditLogger.js","sourceRoot":"","sources":["../src/auditLogger.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCH,0CA8BC;AAWD,0CAcC;AAvFD,uCAAyB;AACzB,2CAA6B;AAC7B,mCAAyC;AAiBzC,iFAAiF;AAEjF,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;AAEzD,iFAAiF;AAEjF;;;;;;GAMG;AACH,SAAgB,eAAe,CAC3B,KAAiB,EACjB,WAAmB,EACnB,MAAM,GAAG,KAAK;IAEd,+DAA+D;IAC/D,IACI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;QACxB,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;QACzB,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAC5B,CAAC;QACC,OAAO;IACX,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAEnC,IAAI,MAAM,EAAE,CAAC;QACT,IAAA,WAAG,EAAC,WAAW,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC;QAC/B,OAAO;IACX,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACrD,IAAA,iBAAS,EAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IAEnC,IAAI,CAAC;QACD,EAAE,CAAC,cAAc,CAAC,SAAS,EAAE,IAAI,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IACvD,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAChB,gEAAgE;QAChE,IAAA,WAAG,EAAC,yCAAyC,GAAG,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC,CAAC;IACxE,CAAC;AACL,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,eAAe,CAC3B,WAAmB,EACnB,WAAmB,EACnB,KAAe,EACf,MAAgB,EAChB,OAAiB;IAEjB,OAAO;QACH,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;QACjE,KAAK;QACL,MAAM;QACN,OAAO;KACV,CAAC;AACN,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -44,6 +44,7 @@ const utils_1 = require("./utils");
|
|
|
44
44
|
const checker_1 = require("./checker");
|
|
45
45
|
const reporter_1 = require("./reporter");
|
|
46
46
|
const hooksInstaller_1 = require("./hooksInstaller");
|
|
47
|
+
const migrator_1 = require("./migrator");
|
|
47
48
|
const program = new commander_1.Command();
|
|
48
49
|
program
|
|
49
50
|
.name("cotester")
|
|
@@ -143,5 +144,14 @@ program
|
|
|
143
144
|
.action(() => {
|
|
144
145
|
(0, hooksInstaller_1.installHooks)();
|
|
145
146
|
});
|
|
147
|
+
program
|
|
148
|
+
.command("migrate [folder]")
|
|
149
|
+
.description("Scan all cotester-generated test/mock files and upgrade them to the current format version. " +
|
|
150
|
+
"Optionally restrict to a subfolder of testDir (e.g. `cotester migrate services`). " +
|
|
151
|
+
"Safe to run multiple times — already-current files are skipped.")
|
|
152
|
+
.option("--dry-run", "Show what would change without writing any files")
|
|
153
|
+
.action((folder, opts) => {
|
|
154
|
+
(0, migrator_1.runMigrate)(folder, { dryRun: opts.dryRun });
|
|
155
|
+
});
|
|
146
156
|
program.parse(process.argv);
|
|
147
157
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,uCAAyB;AACzB,2CAA6B;AAC7B,yCAAoC;AACpC,2CAAwC;AACxC,uCAAsD;AACtD,mDAA6C;AAC7C,mCAA0D;AAC1D,uCAAgD;AAChD,yCAAuC;AACvC,qDAAgD;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/configManager.d.ts
CHANGED
|
@@ -33,5 +33,21 @@ export interface TestGenConfig {
|
|
|
33
33
|
snapshotForComplexTypes: boolean;
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Load the resolved configuration for a project, applying a four-level merge chain:
|
|
38
|
+
*
|
|
39
|
+
* DEFAULT_CONFIG
|
|
40
|
+
* ↓ (lowest priority)
|
|
41
|
+
* ancestor cotester.config.json files (monorepo parents, most-distant first)
|
|
42
|
+
* ↓
|
|
43
|
+
* npm `extends` chain from the local config (explicit shared base)
|
|
44
|
+
* ↓
|
|
45
|
+
* local cotester/cotester.config.json (highest priority — child overrides win)
|
|
46
|
+
*
|
|
47
|
+
* The ancestor walk starts at the *parent* of `projectRoot` and climbs up to the
|
|
48
|
+
* filesystem root (capped at 8 levels). Only directories that actually contain a
|
|
49
|
+
* `cotester/cotester.config.json` file contribute to the merge; gaps in the tree
|
|
50
|
+
* are skipped silently.
|
|
51
|
+
*/
|
|
36
52
|
export declare function loadConfig(projectRoot: string): TestGenConfig;
|
|
37
53
|
export declare function getDefaultConfig(): TestGenConfig;
|
package/dist/configManager.js
CHANGED
|
@@ -65,21 +65,52 @@ const DEFAULT_CONFIG = {
|
|
|
65
65
|
snapshotForComplexTypes: false,
|
|
66
66
|
},
|
|
67
67
|
};
|
|
68
|
+
/**
|
|
69
|
+
* Load the resolved configuration for a project, applying a four-level merge chain:
|
|
70
|
+
*
|
|
71
|
+
* DEFAULT_CONFIG
|
|
72
|
+
* ↓ (lowest priority)
|
|
73
|
+
* ancestor cotester.config.json files (monorepo parents, most-distant first)
|
|
74
|
+
* ↓
|
|
75
|
+
* npm `extends` chain from the local config (explicit shared base)
|
|
76
|
+
* ↓
|
|
77
|
+
* local cotester/cotester.config.json (highest priority — child overrides win)
|
|
78
|
+
*
|
|
79
|
+
* The ancestor walk starts at the *parent* of `projectRoot` and climbs up to the
|
|
80
|
+
* filesystem root (capped at 8 levels). Only directories that actually contain a
|
|
81
|
+
* `cotester/cotester.config.json` file contribute to the merge; gaps in the tree
|
|
82
|
+
* are skipped silently.
|
|
83
|
+
*/
|
|
68
84
|
function loadConfig(projectRoot) {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
const localConfigPath = path.join(projectRoot, "cotester", "cotester.config.json");
|
|
86
|
+
// Step 1 — collect ancestor configs (most-distant → nearest, stops before projectRoot)
|
|
87
|
+
const ancestors = collectAncestorConfigs(projectRoot);
|
|
88
|
+
// Step 2 — start from defaults, fold in each ancestor left-to-right
|
|
89
|
+
let inherited = { ...DEFAULT_CONFIG };
|
|
90
|
+
for (const ancestor of ancestors) {
|
|
91
|
+
inherited = mergeConfig(inherited, ancestor);
|
|
92
|
+
}
|
|
93
|
+
// Step 3 — apply local config on top of the inherited base
|
|
94
|
+
if (!fs.existsSync(localConfigPath)) {
|
|
95
|
+
if (ancestors.length > 0) {
|
|
96
|
+
// Inherited config from monorepo ancestors, no local override
|
|
80
97
|
}
|
|
98
|
+
return inherited;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const raw = fs.readFileSync(localConfigPath, "utf-8");
|
|
102
|
+
const userConfig = JSON.parse(raw);
|
|
103
|
+
// Resolve `extends` using the already-inherited config as the base so the
|
|
104
|
+
// npm package chain builds ON TOP of monorepo defaults, not from scratch.
|
|
105
|
+
const withExtends = userConfig.extends
|
|
106
|
+
? resolveExtends(userConfig, projectRoot, new Set(), inherited)
|
|
107
|
+
: inherited;
|
|
108
|
+
return mergeConfig(withExtends, userConfig);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
console.warn("⚠ Could not parse cotester.config.json — using inherited/defaults.");
|
|
112
|
+
return inherited;
|
|
81
113
|
}
|
|
82
|
-
return { ...DEFAULT_CONFIG };
|
|
83
114
|
}
|
|
84
115
|
/**
|
|
85
116
|
* Resolve an `extends` chain, returning the merged config of all ancestors.
|
|
@@ -94,13 +125,13 @@ function loadConfig(projectRoot) {
|
|
|
94
125
|
*
|
|
95
126
|
* Supports up to 5 levels of chaining to guard against cycles / runaway depth.
|
|
96
127
|
*/
|
|
97
|
-
function resolveExtends(userConfig, projectRoot, visited, depth = 0) {
|
|
128
|
+
function resolveExtends(userConfig, projectRoot, visited, base = DEFAULT_CONFIG, depth = 0) {
|
|
98
129
|
const extendsTarget = userConfig.extends;
|
|
99
130
|
if (!extendsTarget || depth >= 5)
|
|
100
|
-
return { ...
|
|
131
|
+
return { ...base };
|
|
101
132
|
if (visited.has(extendsTarget)) {
|
|
102
133
|
console.warn(`⚠ CoTester config: circular extends detected for "${extendsTarget}" — skipping.`);
|
|
103
|
-
return { ...
|
|
134
|
+
return { ...base };
|
|
104
135
|
}
|
|
105
136
|
visited.add(extendsTarget);
|
|
106
137
|
try {
|
|
@@ -122,13 +153,49 @@ function resolveExtends(userConfig, projectRoot, visited, depth = 0) {
|
|
|
122
153
|
return { ...DEFAULT_CONFIG };
|
|
123
154
|
}
|
|
124
155
|
// Recurse into the base config's own extends chain first
|
|
125
|
-
const grandBase = resolveExtends(baseRaw, projectRoot, visited, depth + 1);
|
|
156
|
+
const grandBase = resolveExtends(baseRaw, projectRoot, visited, base, depth + 1);
|
|
126
157
|
return mergeConfig(grandBase, baseRaw);
|
|
127
158
|
}
|
|
128
159
|
catch (err) {
|
|
129
160
|
console.warn(`⚠ CoTester config: could not resolve extends "${extendsTarget}": ${err?.message}`);
|
|
130
|
-
return { ...
|
|
161
|
+
return { ...base };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Walk up the directory tree from `projectRoot`'s **parent** and collect any
|
|
166
|
+
* `cotester/cotester.config.json` files found along the way.
|
|
167
|
+
*
|
|
168
|
+
* Returns configs ordered most-distant-ancestor first so callers can fold them
|
|
169
|
+
* left-to-right (nearer parent wins over more-distant grandparent).
|
|
170
|
+
*
|
|
171
|
+
* Rules:
|
|
172
|
+
* - Starts at `path.dirname(projectRoot)` — the local config is NOT included.
|
|
173
|
+
* - Stops when the filesystem root is reached or after 8 levels.
|
|
174
|
+
* - The `extends` field inside ancestor configs is intentionally ignored; only
|
|
175
|
+
* their direct fields contribute to the merge (keeps the algorithm simple and
|
|
176
|
+
* avoids cross-tree dependency loops).
|
|
177
|
+
*/
|
|
178
|
+
function collectAncestorConfigs(projectRoot) {
|
|
179
|
+
const found = [];
|
|
180
|
+
let current = path.dirname(projectRoot);
|
|
181
|
+
for (let i = 0; i < 8; i++) {
|
|
182
|
+
const parent = path.dirname(current);
|
|
183
|
+
if (parent === current)
|
|
184
|
+
break; // reached filesystem root
|
|
185
|
+
const configPath = path.join(current, "cotester", "cotester.config.json");
|
|
186
|
+
if (fs.existsSync(configPath)) {
|
|
187
|
+
try {
|
|
188
|
+
const raw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
189
|
+
// Prepend so the most-distant ancestor ends up first after all iterations
|
|
190
|
+
found.unshift(raw);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// Skip unparseable ancestor configs silently
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
current = parent;
|
|
131
197
|
}
|
|
198
|
+
return found;
|
|
132
199
|
}
|
|
133
200
|
function mergeConfig(base, override) {
|
|
134
201
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configManager.js","sourceRoot":"","sources":["../src/configManager.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"configManager.js","sourceRoot":"","sources":["../src/configManager.ts"],"names":[],"mappings":"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"}
|
package/dist/fileWorker.js
CHANGED
|
@@ -13,6 +13,7 @@ const astCache_1 = require("./astCache");
|
|
|
13
13
|
const scenarioEngine_1 = require("./scenarioEngine");
|
|
14
14
|
const mockGenerator_1 = require("./mockGenerator");
|
|
15
15
|
const generator_1 = require("./generator");
|
|
16
|
+
const auditLogger_1 = require("./auditLogger");
|
|
16
17
|
const { filePath, projectRoot, config } = worker_threads_1.workerData;
|
|
17
18
|
(async () => {
|
|
18
19
|
try {
|
|
@@ -23,7 +24,11 @@ const { filePath, projectRoot, config } = worker_threads_1.workerData;
|
|
|
23
24
|
}
|
|
24
25
|
const scenarios = (0, scenarioEngine_1.generateScenarios)(functions, config, projectRoot);
|
|
25
26
|
const mockResult = await (0, mockGenerator_1.generateMockFile)(filePath, scenarios, projectRoot, config.srcDir, config.mockDir);
|
|
26
|
-
await (0, generator_1.generateTestFile)(filePath, scenarios, projectRoot, config.srcDir, config.testDir, mockResult, config.scenarioRules.tableThreshold);
|
|
27
|
+
const genResult = await (0, generator_1.generateTestFile)(filePath, scenarios, projectRoot, config.srcDir, config.testDir, mockResult, config.scenarioRules.tableThreshold);
|
|
28
|
+
if (genResult) {
|
|
29
|
+
const entry = (0, auditLogger_1.buildAuditEntry)(filePath, projectRoot, genResult.added, genResult.merged, genResult.skipped);
|
|
30
|
+
(0, auditLogger_1.writeAuditEntry)(entry, projectRoot);
|
|
31
|
+
}
|
|
27
32
|
worker_threads_1.parentPort?.postMessage({ ok: true });
|
|
28
33
|
}
|
|
29
34
|
catch (err) {
|
package/dist/fileWorker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileWorker.js","sourceRoot":"","sources":["../src/fileWorker.ts"],"names":[],"mappings":";;AAAA;;;;;;;GAOG;AACH,mDAAwD;AACxD,yCAA+C;AAC/C,qDAAqD;AACrD,mDAAmD;AACnD,2CAA+C;
|
|
1
|
+
{"version":3,"file":"fileWorker.js","sourceRoot":"","sources":["../src/fileWorker.ts"],"names":[],"mappings":";;AAAA;;;;;;;GAOG;AACH,mDAAwD;AACxD,yCAA+C;AAC/C,qDAAqD;AACrD,mDAAmD;AACnD,2CAA+C;AAE/C,+CAAiE;AAajE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,2BAAyB,CAAC;AAEpE,CAAC,KAAK,IAAI,EAAE;IACV,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAA,4BAAiB,EAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QAE3D,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,2BAAU,EAAE,WAAW,CAAC,EAAE,EAAE,EAAE,IAAI,EAAyB,CAAC,CAAC;YAC7D,OAAO;QACT,CAAC;QAED,MAAM,SAAS,GAAG,IAAA,kCAAiB,EAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;QAEpE,MAAM,UAAU,GAAG,MAAM,IAAA,gCAAgB,EACvC,QAAQ,EACR,SAAS,EACT,WAAW,EACX,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,OAAO,CACf,CAAC;QAEF,MAAM,SAAS,GAAG,MAAM,IAAA,4BAAgB,EACtC,QAAQ,EACR,SAAS,EACT,WAAW,EACX,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,OAAO,EACd,UAAU,EACV,MAAM,CAAC,aAAa,CAAC,cAAc,CACpC,CAAC;QAEF,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,KAAK,GAAG,IAAA,6BAAe,EAC3B,QAAQ,EACR,WAAW,EACX,SAAS,CAAC,KAAK,EACf,SAAS,CAAC,MAAM,EAChB,SAAS,CAAC,OAAO,CAClB,CAAC;YACF,IAAA,6BAAe,EAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QACtC,CAAC;QAED,2BAAU,EAAE,WAAW,CAAC,EAAE,EAAE,EAAE,IAAI,EAAyB,CAAC,CAAC;IAC/D,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,2BAAU,EAAE,WAAW,CAAC;YACtB,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC;SACZ,CAAC,CAAC;IAC5B,CAAC;AACH,CAAC,CAAC,EAAE,CAAC"}
|
package/dist/generator.d.ts
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
import { TestScenario } from "./scenarioEngine";
|
|
2
2
|
import { MockFileResult } from "./mockGenerator";
|
|
3
|
+
/**
|
|
4
|
+
* Marker placed as the very first line of every generated test file.
|
|
5
|
+
* Its presence lets cotester know the file was scaffolded (not hand-written),
|
|
6
|
+
* but function-level merging works regardless of whether the marker is present.
|
|
7
|
+
*/
|
|
8
|
+
export declare const TESTGEN_MARKER = "// @cotester-generated";
|
|
9
|
+
/**
|
|
10
|
+
* Format version written into the second line of every generated test file.
|
|
11
|
+
* Used by `cotester migrate` to detect files produced by older versions and
|
|
12
|
+
* upgrade them in-place. Increment this when the generated format changes.
|
|
13
|
+
*/
|
|
14
|
+
export declare const CURRENT_FILE_VERSION = 2;
|
|
15
|
+
export declare const VERSION_MARKER = "// @cotester-version: 2";
|
|
16
|
+
/** Returned by generateTestFile — carries the test file path and audit data. */
|
|
17
|
+
export interface GenerateResult {
|
|
18
|
+
testFilePath: string;
|
|
19
|
+
/** Function names for which a new describe block was written */
|
|
20
|
+
added: string[];
|
|
21
|
+
/** Function names whose signature changed (describe existed, warning injected) */
|
|
22
|
+
merged: string[];
|
|
23
|
+
/** Function names already covered and left untouched */
|
|
24
|
+
skipped: string[];
|
|
25
|
+
}
|
|
3
26
|
/**
|
|
4
27
|
* Generate (or merge into) a test file for the given source file.
|
|
5
28
|
*
|
|
@@ -13,4 +36,4 @@ import { MockFileResult } from "./mockGenerator";
|
|
|
13
36
|
* Individual functions/methods that already have a describe block are never
|
|
14
37
|
* touched, preserving any hand-written assertions inside them.
|
|
15
38
|
*/
|
|
16
|
-
export declare function generateTestFile(srcFilePath: string, scenarios: TestScenario[], projectRoot: string, srcDir: string, testDir: string, mockResult: MockFileResult | null, tableThreshold?: number, dryRun?: boolean, framework?: "jest" | "vitest", snapshotForComplexTypes?: boolean): Promise<
|
|
39
|
+
export declare function generateTestFile(srcFilePath: string, scenarios: TestScenario[], projectRoot: string, srcDir: string, testDir: string, mockResult: MockFileResult | null, tableThreshold?: number, dryRun?: boolean, framework?: "jest" | "vitest", snapshotForComplexTypes?: boolean): Promise<GenerateResult | null>;
|
package/dist/generator.js
CHANGED
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.VERSION_MARKER = exports.CURRENT_FILE_VERSION = exports.TESTGEN_MARKER = void 0;
|
|
36
37
|
exports.generateTestFile = generateTestFile;
|
|
37
38
|
const fs = __importStar(require("fs"));
|
|
38
39
|
const path = __importStar(require("path"));
|
|
@@ -45,7 +46,14 @@ const frameworkAdapter_1 = require("./frameworkAdapter");
|
|
|
45
46
|
* Its presence lets cotester know the file was scaffolded (not hand-written),
|
|
46
47
|
* but function-level merging works regardless of whether the marker is present.
|
|
47
48
|
*/
|
|
48
|
-
|
|
49
|
+
exports.TESTGEN_MARKER = "// @cotester-generated";
|
|
50
|
+
/**
|
|
51
|
+
* Format version written into the second line of every generated test file.
|
|
52
|
+
* Used by `cotester migrate` to detect files produced by older versions and
|
|
53
|
+
* upgrade them in-place. Increment this when the generated format changes.
|
|
54
|
+
*/
|
|
55
|
+
exports.CURRENT_FILE_VERSION = 2;
|
|
56
|
+
exports.VERSION_MARKER = `// @cotester-version: ${exports.CURRENT_FILE_VERSION}`;
|
|
49
57
|
/**
|
|
50
58
|
* Generate (or merge into) a test file for the given source file.
|
|
51
59
|
*
|
|
@@ -75,7 +83,8 @@ async function generateTestFile(srcFilePath, scenarios, projectRoot, srcDir, tes
|
|
|
75
83
|
process.stdout.write(`// [dry-run] Test file: ${relTest}\n`);
|
|
76
84
|
process.stdout.write(`${"─".repeat(60)}\n`);
|
|
77
85
|
process.stdout.write(formatted);
|
|
78
|
-
|
|
86
|
+
const allNames = scenarios.map((s) => s.functionName);
|
|
87
|
+
return { testFilePath, added: allNames, merged: [], skipped: [] };
|
|
79
88
|
}
|
|
80
89
|
(0, utils_1.ensureDir)(path.dirname(testFilePath));
|
|
81
90
|
// ── Brand-new file → generate everything ─────────────────────────────
|
|
@@ -84,7 +93,8 @@ async function generateTestFile(srcFilePath, scenarios, projectRoot, srcDir, tes
|
|
|
84
93
|
const formatted = await (0, formatter_1.formatCode)(code, projectRoot);
|
|
85
94
|
fs.writeFileSync(testFilePath, formatted, "utf-8");
|
|
86
95
|
(0, utils_1.log)(`Test file → ${path.relative(projectRoot, testFilePath)}`, "success");
|
|
87
|
-
|
|
96
|
+
const allNames = scenarios.map((s) => s.functionName);
|
|
97
|
+
return { testFilePath, added: allNames, merged: [], skipped: [] };
|
|
88
98
|
}
|
|
89
99
|
// ── File exists → function-level merge ───────────────────────────────
|
|
90
100
|
return mergeIntoExistingTestFile(scenarios, srcFilePath, testFilePath, mockResult, projectRoot, srcDir, tableThreshold, adapter, snapshotForComplexTypes);
|
|
@@ -105,6 +115,7 @@ async function mergeIntoExistingTestFile(scenarios, srcFilePath, testFilePath, m
|
|
|
105
115
|
const existingDescribes = detectExistingDescribes(content);
|
|
106
116
|
// ── Feature 6: signature-change detection for already-covered describes ──
|
|
107
117
|
const existingSigs = parseExistingSignatures(content);
|
|
118
|
+
const sigChangedNames = new Set();
|
|
108
119
|
for (const s of scenarios) {
|
|
109
120
|
const key = s.functionName;
|
|
110
121
|
if (!existingDescribes.has(key))
|
|
@@ -120,6 +131,7 @@ async function mergeIntoExistingTestFile(scenarios, srcFilePath, testFilePath, m
|
|
|
120
131
|
(0, utils_1.log)(` Signature changed for ${key} — injecting update warning.`, "warn");
|
|
121
132
|
content = updateSignatureComment(content, key, newSig);
|
|
122
133
|
content = injectSignatureWarning(content, key, oldSig, newSig);
|
|
134
|
+
sigChangedNames.add(key);
|
|
123
135
|
}
|
|
124
136
|
}
|
|
125
137
|
// ── Classify scenarios ───────────────────────────────────────────────
|
|
@@ -161,9 +173,16 @@ async function mergeIntoExistingTestFile(scenarios, srcFilePath, testFilePath, m
|
|
|
161
173
|
for (const fn of missingStandalone) {
|
|
162
174
|
toAppend.push(buildFunctionDescribe(fn, mockResult, tableThreshold, adapter, snapshotForComplexTypes));
|
|
163
175
|
}
|
|
176
|
+
// ── Build audit lists ────────────────────────────────────────────────
|
|
177
|
+
const addedNames = newScenarios.map((s) => s.functionName);
|
|
178
|
+
const mergedNames = Array.from(sigChangedNames);
|
|
179
|
+
const skippedNames = scenarios
|
|
180
|
+
.filter((s) => existingDescribes.has(s.functionName) &&
|
|
181
|
+
!sigChangedNames.has(s.functionName))
|
|
182
|
+
.map((s) => s.functionName);
|
|
164
183
|
if (toAppend.length === 0 && newScenarios.length === 0) {
|
|
165
184
|
(0, utils_1.log)(` ${path.relative(projectRoot, testFilePath)} — all functions already covered.`, "info");
|
|
166
|
-
return testFilePath;
|
|
185
|
+
return { testFilePath, added: [], merged: mergedNames, skipped: skippedNames };
|
|
167
186
|
}
|
|
168
187
|
// ── Append standalone / new-class blocks ─────────────────────────────
|
|
169
188
|
if (toAppend.length > 0) {
|
|
@@ -173,9 +192,8 @@ async function mergeIntoExistingTestFile(scenarios, srcFilePath, testFilePath, m
|
|
|
173
192
|
content = addMissingMocks(content, newScenarios, srcFilePath, testFilePath, adapter);
|
|
174
193
|
const formatted = await (0, formatter_1.formatCode)(content, projectRoot);
|
|
175
194
|
fs.writeFileSync(testFilePath, formatted, "utf-8");
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
return testFilePath;
|
|
195
|
+
(0, utils_1.log)(`Test file updated → ${path.relative(projectRoot, testFilePath)} (+${addedNames.length} function(s))`, "success");
|
|
196
|
+
return { testFilePath, added: addedNames, merged: mergedNames, skipped: skippedNames };
|
|
179
197
|
}
|
|
180
198
|
// ─── Merge helpers ────────────────────────────────────────────────────────────
|
|
181
199
|
/**
|
|
@@ -321,7 +339,8 @@ function buildTestSource(scenarios, srcFilePath, testFilePath, mockResult, proje
|
|
|
321
339
|
const lines = [];
|
|
322
340
|
// Marker — presence of this line tells cotester the file is auto-generated
|
|
323
341
|
// and safe to regenerate. Remove it to protect hand-written edits.
|
|
324
|
-
lines.push(TESTGEN_MARKER);
|
|
342
|
+
lines.push(exports.TESTGEN_MARKER);
|
|
343
|
+
lines.push(exports.VERSION_MARKER);
|
|
325
344
|
lines.push("");
|
|
326
345
|
// ── Framework import (Vitest only; empty for Jest) ───────────────
|
|
327
346
|
const fwImport = adapter.frameworkImport();
|