@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.
@@ -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":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -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;
@@ -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 configPath = path.join(projectRoot, "cotester", "cotester.config.json");
70
- if (fs.existsSync(configPath)) {
71
- try {
72
- const raw = fs.readFileSync(configPath, "utf-8");
73
- const userConfig = JSON.parse(raw);
74
- const baseConfig = resolveExtends(userConfig, projectRoot, new Set());
75
- return mergeConfig(baseConfig, userConfig);
76
- }
77
- catch {
78
- console.warn("⚠ Could not parse cotester.config.json — using defaults.");
79
- return { ...DEFAULT_CONFIG };
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 { ...DEFAULT_CONFIG };
131
+ return { ...base };
101
132
  if (visited.has(extendsTarget)) {
102
133
  console.warn(`⚠ CoTester config: circular extends detected for "${extendsTarget}" — skipping.`);
103
- return { ...DEFAULT_CONFIG };
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 { ...DEFAULT_CONFIG };
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":"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"}
1
+ {"version":3,"file":"configManager.js","sourceRoot":"","sources":["../src/configManager.ts"],"names":[],"mappings":"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"}
@@ -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) {
@@ -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;AAc/C,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,IAAA,4BAAgB,EACpB,QAAQ,EACR,SAAS,EACT,WAAW,EACX,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,OAAO,EACd,UAAU,EACV,MAAM,CAAC,aAAa,CAAC,cAAc,CACpC,CAAC;QAEF,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"}
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"}
@@ -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<string | null>;
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
- const TESTGEN_MARKER = "// @cotester-generated";
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
- return testFilePath;
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
- return testFilePath;
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
- const addedCount = newScenarios.length;
177
- (0, utils_1.log)(`Test file updated → ${path.relative(projectRoot, testFilePath)} (+${addedCount} function(s))`, "success");
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();