@jterrazz/test 3.3.1 → 3.4.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/dist/index.cjs +74 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -2
- package/dist/index.d.ts +16 -2
- package/dist/index.js +75 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -118,6 +118,15 @@ interface CommandResult {
|
|
|
118
118
|
stdout: string;
|
|
119
119
|
stderr: string;
|
|
120
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Options for spawning a long-running process.
|
|
123
|
+
*/
|
|
124
|
+
interface SpawnOptions {
|
|
125
|
+
/** Resolve when stdout/stderr contains this string. */
|
|
126
|
+
waitFor: string;
|
|
127
|
+
/** Kill the process after this many milliseconds. */
|
|
128
|
+
timeout: number;
|
|
129
|
+
}
|
|
121
130
|
/**
|
|
122
131
|
* Abstract CLI interface for specification runners.
|
|
123
132
|
* Implement this to plug in your command execution strategy.
|
|
@@ -125,6 +134,8 @@ interface CommandResult {
|
|
|
125
134
|
interface CommandPort {
|
|
126
135
|
/** Execute a CLI command with the given arguments in the given working directory. */
|
|
127
136
|
exec(args: string, cwd: string): Promise<CommandResult>;
|
|
137
|
+
/** Spawn a long-running process and wait for a pattern or timeout. */
|
|
138
|
+
spawn(args: string, cwd: string, options: SpawnOptions): Promise<CommandResult>;
|
|
128
139
|
}
|
|
129
140
|
//#endregion
|
|
130
141
|
//#region src/specification/ports/server.port.d.ts
|
|
@@ -200,6 +211,7 @@ declare class SpecificationBuilder {
|
|
|
200
211
|
private projectName;
|
|
201
212
|
private request;
|
|
202
213
|
private seeds;
|
|
214
|
+
private spawnConfig;
|
|
203
215
|
private testDir;
|
|
204
216
|
constructor(config: SpecificationConfig, testDir: string, label: string);
|
|
205
217
|
seed(file: string, options?: {
|
|
@@ -212,7 +224,8 @@ declare class SpecificationBuilder {
|
|
|
212
224
|
post(path: string, bodyFile?: string): this;
|
|
213
225
|
put(path: string, bodyFile?: string): this;
|
|
214
226
|
delete(path: string): this;
|
|
215
|
-
exec(args: string): this;
|
|
227
|
+
exec(args: string | string[]): this;
|
|
228
|
+
spawn(args: string, options: SpawnOptions): this;
|
|
216
229
|
run(): Promise<SpecificationResult>;
|
|
217
230
|
private prepareWorkDir;
|
|
218
231
|
private runHttpAction;
|
|
@@ -289,13 +302,14 @@ declare function redis(options?: RedisOptions): RedisHandle;
|
|
|
289
302
|
//#endregion
|
|
290
303
|
//#region src/specification/adapters/exec.adapter.d.ts
|
|
291
304
|
/**
|
|
292
|
-
* Executes CLI commands via execSync.
|
|
305
|
+
* Executes CLI commands via execSync (blocking) or spawn (long-running).
|
|
293
306
|
* Used by cli() for local command execution.
|
|
294
307
|
*/
|
|
295
308
|
declare class ExecAdapter implements CommandPort {
|
|
296
309
|
private command;
|
|
297
310
|
constructor(command: string);
|
|
298
311
|
exec(args: string, cwd: string): Promise<CommandResult>;
|
|
312
|
+
spawn(args: string, cwd: string, options: SpawnOptions): Promise<CommandResult>;
|
|
299
313
|
}
|
|
300
314
|
//#endregion
|
|
301
315
|
//#region src/specification/adapters/fetch.adapter.d.ts
|
package/dist/index.d.ts
CHANGED
|
@@ -118,6 +118,15 @@ interface CommandResult {
|
|
|
118
118
|
stdout: string;
|
|
119
119
|
stderr: string;
|
|
120
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Options for spawning a long-running process.
|
|
123
|
+
*/
|
|
124
|
+
interface SpawnOptions {
|
|
125
|
+
/** Resolve when stdout/stderr contains this string. */
|
|
126
|
+
waitFor: string;
|
|
127
|
+
/** Kill the process after this many milliseconds. */
|
|
128
|
+
timeout: number;
|
|
129
|
+
}
|
|
121
130
|
/**
|
|
122
131
|
* Abstract CLI interface for specification runners.
|
|
123
132
|
* Implement this to plug in your command execution strategy.
|
|
@@ -125,6 +134,8 @@ interface CommandResult {
|
|
|
125
134
|
interface CommandPort {
|
|
126
135
|
/** Execute a CLI command with the given arguments in the given working directory. */
|
|
127
136
|
exec(args: string, cwd: string): Promise<CommandResult>;
|
|
137
|
+
/** Spawn a long-running process and wait for a pattern or timeout. */
|
|
138
|
+
spawn(args: string, cwd: string, options: SpawnOptions): Promise<CommandResult>;
|
|
128
139
|
}
|
|
129
140
|
//#endregion
|
|
130
141
|
//#region src/specification/ports/server.port.d.ts
|
|
@@ -200,6 +211,7 @@ declare class SpecificationBuilder {
|
|
|
200
211
|
private projectName;
|
|
201
212
|
private request;
|
|
202
213
|
private seeds;
|
|
214
|
+
private spawnConfig;
|
|
203
215
|
private testDir;
|
|
204
216
|
constructor(config: SpecificationConfig, testDir: string, label: string);
|
|
205
217
|
seed(file: string, options?: {
|
|
@@ -212,7 +224,8 @@ declare class SpecificationBuilder {
|
|
|
212
224
|
post(path: string, bodyFile?: string): this;
|
|
213
225
|
put(path: string, bodyFile?: string): this;
|
|
214
226
|
delete(path: string): this;
|
|
215
|
-
exec(args: string): this;
|
|
227
|
+
exec(args: string | string[]): this;
|
|
228
|
+
spawn(args: string, options: SpawnOptions): this;
|
|
216
229
|
run(): Promise<SpecificationResult>;
|
|
217
230
|
private prepareWorkDir;
|
|
218
231
|
private runHttpAction;
|
|
@@ -289,13 +302,14 @@ declare function redis(options?: RedisOptions): RedisHandle;
|
|
|
289
302
|
//#endregion
|
|
290
303
|
//#region src/specification/adapters/exec.adapter.d.ts
|
|
291
304
|
/**
|
|
292
|
-
* Executes CLI commands via execSync.
|
|
305
|
+
* Executes CLI commands via execSync (blocking) or spawn (long-running).
|
|
293
306
|
* Used by cli() for local command execution.
|
|
294
307
|
*/
|
|
295
308
|
declare class ExecAdapter implements CommandPort {
|
|
296
309
|
private command;
|
|
297
310
|
constructor(command: string);
|
|
298
311
|
exec(args: string, cwd: string): Promise<CommandResult>;
|
|
312
|
+
spawn(args: string, cwd: string, options: SpawnOptions): Promise<CommandResult>;
|
|
299
313
|
}
|
|
300
314
|
//#endregion
|
|
301
315
|
//#region src/specification/adapters/fetch.adapter.d.ts
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import MockDatePackage from "mockdate";
|
|
|
4
4
|
import { mockDeep } from "vitest-mock-extended";
|
|
5
5
|
import { cpSync, existsSync, mkdtempSync, readFileSync } from "node:fs";
|
|
6
6
|
import { dirname, isAbsolute, resolve } from "node:path";
|
|
7
|
-
import { execSync } from "node:child_process";
|
|
7
|
+
import { execSync, spawn } from "node:child_process";
|
|
8
8
|
import { tmpdir } from "node:os";
|
|
9
9
|
//#region src/mocking/mock-of-date.ts
|
|
10
10
|
const mockOfDate = MockDatePackage;
|
|
@@ -4899,7 +4899,7 @@ var Orchestrator = class {
|
|
|
4899
4899
|
//#endregion
|
|
4900
4900
|
//#region src/specification/adapters/exec.adapter.ts
|
|
4901
4901
|
/**
|
|
4902
|
-
* Executes CLI commands via execSync.
|
|
4902
|
+
* Executes CLI commands via execSync (blocking) or spawn (long-running).
|
|
4903
4903
|
* Used by cli() for local command execution.
|
|
4904
4904
|
*/
|
|
4905
4905
|
var ExecAdapter = class {
|
|
@@ -4935,6 +4935,55 @@ var ExecAdapter = class {
|
|
|
4935
4935
|
};
|
|
4936
4936
|
}
|
|
4937
4937
|
}
|
|
4938
|
+
async spawn(args, cwd, options) {
|
|
4939
|
+
const env = {
|
|
4940
|
+
...process.env,
|
|
4941
|
+
INIT_CWD: void 0
|
|
4942
|
+
};
|
|
4943
|
+
return new Promise((resolve) => {
|
|
4944
|
+
let stdout = "";
|
|
4945
|
+
let stderr = "";
|
|
4946
|
+
let resolved = false;
|
|
4947
|
+
const child = spawn(this.command, args.split(/\s+/).filter(Boolean), {
|
|
4948
|
+
cwd,
|
|
4949
|
+
env,
|
|
4950
|
+
stdio: [
|
|
4951
|
+
"pipe",
|
|
4952
|
+
"pipe",
|
|
4953
|
+
"pipe"
|
|
4954
|
+
]
|
|
4955
|
+
});
|
|
4956
|
+
const finish = (exitCode) => {
|
|
4957
|
+
if (resolved) return;
|
|
4958
|
+
resolved = true;
|
|
4959
|
+
child.kill("SIGTERM");
|
|
4960
|
+
resolve({
|
|
4961
|
+
exitCode,
|
|
4962
|
+
stdout,
|
|
4963
|
+
stderr
|
|
4964
|
+
});
|
|
4965
|
+
};
|
|
4966
|
+
let patternMatched = false;
|
|
4967
|
+
const checkPattern = () => {
|
|
4968
|
+
if (!patternMatched && (stdout.includes(options.waitFor) || stderr.includes(options.waitFor))) {
|
|
4969
|
+
patternMatched = true;
|
|
4970
|
+
finish(0);
|
|
4971
|
+
}
|
|
4972
|
+
};
|
|
4973
|
+
child.stdout?.on("data", (data) => {
|
|
4974
|
+
stdout += data.toString();
|
|
4975
|
+
checkPattern();
|
|
4976
|
+
});
|
|
4977
|
+
child.stderr?.on("data", (data) => {
|
|
4978
|
+
stderr += data.toString();
|
|
4979
|
+
checkPattern();
|
|
4980
|
+
});
|
|
4981
|
+
child.on("exit", (code) => {
|
|
4982
|
+
if (!patternMatched) finish(code === 0 ? 1 : code ?? 1);
|
|
4983
|
+
});
|
|
4984
|
+
setTimeout(() => finish(124), options.timeout);
|
|
4985
|
+
});
|
|
4986
|
+
}
|
|
4938
4987
|
};
|
|
4939
4988
|
//#endregion
|
|
4940
4989
|
//#region src/specification/adapters/fetch.adapter.ts
|
|
@@ -5093,6 +5142,7 @@ var SpecificationBuilder = class {
|
|
|
5093
5142
|
projectName = null;
|
|
5094
5143
|
request = null;
|
|
5095
5144
|
seeds = [];
|
|
5145
|
+
spawnConfig = null;
|
|
5096
5146
|
testDir;
|
|
5097
5147
|
constructor(config, testDir, label) {
|
|
5098
5148
|
this.config = config;
|
|
@@ -5152,11 +5202,18 @@ var SpecificationBuilder = class {
|
|
|
5152
5202
|
this.commandArgs = args;
|
|
5153
5203
|
return this;
|
|
5154
5204
|
}
|
|
5205
|
+
spawn(args, options) {
|
|
5206
|
+
this.spawnConfig = {
|
|
5207
|
+
args,
|
|
5208
|
+
options
|
|
5209
|
+
};
|
|
5210
|
+
return this;
|
|
5211
|
+
}
|
|
5155
5212
|
async run() {
|
|
5156
5213
|
const hasHttpAction = this.request !== null;
|
|
5157
|
-
const hasCliAction = this.commandArgs !== null;
|
|
5214
|
+
const hasCliAction = this.commandArgs !== null || this.spawnConfig !== null;
|
|
5158
5215
|
if (!hasHttpAction && !hasCliAction) throw new Error(`Specification "${this.label}": no action defined. Call .get(), .post(), .exec(), etc. before .run()`);
|
|
5159
|
-
if (hasHttpAction && hasCliAction) throw new Error(`Specification "${this.label}": cannot mix HTTP (.get/.post) and CLI (.exec) actions`);
|
|
5216
|
+
if (hasHttpAction && hasCliAction) throw new Error(`Specification "${this.label}": cannot mix HTTP (.get/.post) and CLI (.exec/.spawn) actions`);
|
|
5160
5217
|
let workDir = null;
|
|
5161
5218
|
if (hasCliAction) workDir = this.prepareWorkDir();
|
|
5162
5219
|
if (this.config.databases) for (const db of this.config.databases.values()) await db.reset();
|
|
@@ -5203,8 +5260,21 @@ var SpecificationBuilder = class {
|
|
|
5203
5260
|
}
|
|
5204
5261
|
async runCliAction(workDir) {
|
|
5205
5262
|
if (!this.config.command) throw new Error("CLI actions require a command adapter (use cli())");
|
|
5263
|
+
let commandResult;
|
|
5264
|
+
if (this.spawnConfig) commandResult = await this.config.command.spawn(this.spawnConfig.args, workDir, this.spawnConfig.options);
|
|
5265
|
+
else if (Array.isArray(this.commandArgs)) {
|
|
5266
|
+
commandResult = {
|
|
5267
|
+
exitCode: 0,
|
|
5268
|
+
stdout: "",
|
|
5269
|
+
stderr: ""
|
|
5270
|
+
};
|
|
5271
|
+
for (const args of this.commandArgs) {
|
|
5272
|
+
commandResult = await this.config.command.exec(args, workDir);
|
|
5273
|
+
if (commandResult.exitCode !== 0) break;
|
|
5274
|
+
}
|
|
5275
|
+
} else commandResult = await this.config.command.exec(this.commandArgs, workDir);
|
|
5206
5276
|
return new SpecificationResult({
|
|
5207
|
-
commandResult
|
|
5277
|
+
commandResult,
|
|
5208
5278
|
config: this.config,
|
|
5209
5279
|
testDir: this.testDir,
|
|
5210
5280
|
workDir
|