@actiondock/testing 2.2.1 → 2.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/README.md CHANGED
@@ -28,14 +28,23 @@ ActionDock 2.0 确定性测试框架与测试运行时。
28
28
  - 通过 `advance(ms)` 瞬间推进模拟时间,并以确定性顺序依次唤醒挂起的计时器与延迟任务。
29
29
  - 支持时间倒流检测与高精度时间戳快照。
30
30
 
31
+ ### FakeProcessDriver 确定性测试驱动桩
32
+
33
+ [FakeProcessDriver](./src/process-driver.ts) 完整实现 Core 层的 ProcessDriver 契约,是受管进程测试的核心桩:
34
+
35
+ - 确定性事件模拟:提供 `emitOutput`、`emitExit`、`emitOutputClosed` 与 `emitFault` 方法,在测试中以确定性时序唤醒观察者。
36
+ - 故障与异常注入:提供 `simulateSpawnFailure`、`simulateWriteFailure`、`simulateResizeFailure` 与 `simulateTerminateFailure`,精确验证业务层容错。
37
+ - 历史追踪与状态断言:维护 `spawnCalls`、`writes`、`eofCalls`、`interruptCalls`、`resizeCalls`、`terminateCalls` 与 `disposeCalls` 集合,供测试后置断言。
38
+ - 动态能力覆写:支持通过 `setCapabilities` 动态调整是否支持 PTY、resize 与 inputEOF 等特征。
39
+
31
40
  ### MockProcessExecutor 模拟进程执行器
32
41
 
33
- [MockProcessExecutor](./src/process.ts) 在沙箱中拦截并伪造所有外部系统命令与子进程调用:
42
+ [MockProcessExecutor](./src/process.ts) 深度集成 ProcessManager 与 FakeProcessDriver,拦截并模拟外部命令与受管进程调用:
34
43
 
35
- - 灵活规则匹配:通过 `onCommand` 注册匹配器,支持字符串完全匹配、正则表达式匹配或自定义断言谓词函数。
36
- - 丰富的响应定义:支持模拟标准输出、标准错误流、非零退出码、二进制字节流以及执行耗时。
37
- - 异常场景复现:可直接模拟命令执行超时(`timedOut`)或取消信号阻断(`cancelled`)。
38
- - 调用历史追踪:精确记录每次调用的完整入参、工作目录与环境变量,提供断言追踪支持。
44
+ - 规则灵活匹配:通过 `register` 注册匹配器,支持命令字符串精确匹配、正则表达式匹配或自定义断言谓词函数。
45
+ - 丰富响应定义:支持模拟标准输出、标准错误流、退出状态码、二进制字节流以及执行延迟。
46
+ - 受管进程穿透:直接暴露 `driver` 底层驱动与 `processManager` 实例,无缝承接 `run`、`start`、`acquire` 等受管操作。
47
+ - 调用历史追踪:精确记录每次调用的命令、参数、工作目录与环境变量。
39
48
 
40
49
  ### MemoryStorage 纯内存存储
41
50
 
@@ -49,36 +58,39 @@ ActionDock 2.0 确定性测试框架与测试运行时。
49
58
 
50
59
  ## 快速使用示例
51
60
 
61
+ ### 一次性受管进程命令测试
62
+
52
63
  ```ts
53
64
  import { describe, it } from "node:test";
54
65
  import assert from "node:assert/strict";
55
- import { defineAction } from "@actiondock/sdk";
56
- import { createTestRuntime, MockProcessExecutor } from "@actiondock/testing";
66
+ import { defineAction, decodeText } from "@actiondock/sdk";
67
+ import { createTestRuntime, FakeProcessDriver } from "@actiondock/testing";
57
68
 
58
69
  const gitBranchAction = defineAction(async (input: { remote?: boolean }, ctx) => {
59
- const res = await ctx.process.exec("git", ["branch"]);
60
- return { output: res.stdout.trim() };
70
+ const res = await ctx.process.run({
71
+ spec: { executable: "git", args: ["branch"], io: { mode: "pipe" } },
72
+ timeoutMs: 5000,
73
+ maxOutputBytes: 1024 * 1024,
74
+ });
75
+ return { output: decodeText(res.chunks).trim() };
61
76
  });
62
77
 
63
78
  describe("git action test", () => {
64
- it("mocks process and asserts output", async () => {
65
- // 初始化模拟执行器并配置预设响应
66
- const processExecutor = new MockProcessExecutor();
67
- processExecutor.onCommand("git", {
68
- stdout: "* main\n feature/agent\n",
69
- });
79
+ it("使用 FakeProcessDriver 模拟输出并断言", async () => {
80
+ const fakeDriver = new FakeProcessDriver();
81
+ fakeDriver.onSpawn = (handle) => {
82
+ handle.emitOutput("stdout", "* main\n feature/agent\n");
83
+ handle.emitExit(0);
84
+ handle.emitOutputClosed("natural");
85
+ };
70
86
 
71
- // 创建测试运行时并注入执行器
72
87
  const runtime = createTestRuntime({
73
- process: processExecutor,
88
+ platform: { processDriver: fakeDriver } as any,
74
89
  });
75
90
 
76
- // 执行 Action 并断言业务数据
77
91
  const result = await runtime.run(gitBranchAction, { remote: false });
78
92
  assert.equal(result.output, "* main\n feature/agent");
79
-
80
- // 断言底层命令调用历史
81
- assert.equal(processExecutor.getHistory().length, 1);
93
+ assert.equal(fakeDriver.spawnCalls.length, 1);
82
94
  });
83
95
  });
84
96
  ```
package/dist/cli.d.ts CHANGED
@@ -11,6 +11,8 @@ export interface ExecCliOptions {
11
11
  input?: string | Uint8Array;
12
12
  encoding?: string;
13
13
  throwOnError?: boolean;
14
+ /** 输出字节总量上限,超出后终止进程并标记 truncated(与 runtime-node 执行器语义对齐) */
15
+ maxOutputBytes?: number;
14
16
  }
15
17
  /**
16
18
  * CLI 执行结果结构体。
@@ -22,6 +24,8 @@ export interface ExecCliResult {
22
24
  stderr: string;
23
25
  raw: Uint8Array;
24
26
  timedOut?: boolean;
27
+ /** 输出超过 maxOutputBytes 上限被截断时置为 true */
28
+ truncated?: boolean;
25
29
  durationMs: number;
26
30
  }
27
31
  /**
package/dist/cli.js CHANGED
@@ -12,6 +12,7 @@ export { findExecutable };
12
12
  */
13
13
  export async function execCli(command, args = [], options = {}) {
14
14
  const startTime = performance.now();
15
+ const maxOutputBytes = options.maxOutputBytes ?? Infinity;
15
16
  if (options.signal?.aborted) {
16
17
  const errRes = {
17
18
  ok: false,
@@ -78,9 +79,20 @@ export async function execCli(command, args = [], options = {}) {
78
79
  }
79
80
  let settled = false;
80
81
  let timedOut = false;
82
+ let truncated = false;
81
83
  let spawnError;
82
84
  const stdoutChunks = [];
83
85
  const stderrChunks = [];
86
+ let totalOutputBytes = 0;
87
+ // 输出超限时终止进程:由 enforceOutputLimit 负责截断与标记
88
+ const terminateForLimit = () => {
89
+ try {
90
+ child.kill("SIGTERM");
91
+ }
92
+ catch {
93
+ // 忽略已退出状态
94
+ }
95
+ };
84
96
  let timeoutTimer;
85
97
  if (options.timeout && options.timeout > 0) {
86
98
  timeoutTimer = setTimeout(() => {
@@ -112,7 +124,12 @@ export async function execCli(command, args = [], options = {}) {
112
124
  stderr = `Command '${command}' timed out after ${options.timeout}ms`;
113
125
  }
114
126
  const exitCode = timedOut ? -1 : (exitCodeFromClose ?? (spawnError ? -1 : 0));
115
- const ok = !timedOut && exitCode === 0;
127
+ const ok = !timedOut && !truncated && exitCode === 0;
128
+ let limitMessage = "";
129
+ if (truncated && !stderr) {
130
+ limitMessage = `Command '${command}' output exceeded limit of ${maxOutputBytes} bytes`;
131
+ stderr = limitMessage;
132
+ }
116
133
  const result = {
117
134
  ok,
118
135
  exitCode,
@@ -120,6 +137,7 @@ export async function execCli(command, args = [], options = {}) {
120
137
  stderr,
121
138
  raw: rawStdout,
122
139
  timedOut: timedOut || undefined,
140
+ truncated: truncated || undefined,
123
141
  durationMs,
124
142
  };
125
143
  if (options.throwOnError && !ok) {
@@ -128,11 +146,35 @@ export async function execCli(command, args = [], options = {}) {
128
146
  }
129
147
  resolve(result);
130
148
  };
149
+ // 输出超限时截断已收集字节并终止进程:与 runtime-node 执行器对齐,
150
+ // 只保留上限内的字节(含同 chunk 内截断),丢弃超限部分
151
+ const enforceOutputLimit = (chunks, chunk) => {
152
+ const remaining = maxOutputBytes - totalOutputBytes;
153
+ if (remaining < chunk.length) {
154
+ if (remaining > 0) {
155
+ chunks.push(chunk.subarray(0, remaining));
156
+ totalOutputBytes += remaining;
157
+ }
158
+ else {
159
+ totalOutputBytes += chunk.length;
160
+ }
161
+ truncated = true;
162
+ terminateForLimit();
163
+ return true;
164
+ }
165
+ totalOutputBytes += chunk.length;
166
+ chunks.push(chunk);
167
+ return false;
168
+ };
131
169
  child.stdout?.on("data", (chunk) => {
132
- stdoutChunks.push(chunk);
170
+ if (truncated)
171
+ return;
172
+ enforceOutputLimit(stdoutChunks, chunk);
133
173
  });
134
174
  child.stderr?.on("data", (chunk) => {
135
- stderrChunks.push(chunk);
175
+ if (truncated)
176
+ return;
177
+ enforceOutputLimit(stderrChunks, chunk);
136
178
  });
137
179
  child.on("error", (err) => {
138
180
  spawnError = err;
package/dist/clock.d.ts CHANGED
@@ -38,11 +38,25 @@ export declare class FakeClock implements Clock {
38
38
  sleep(ms: number): Promise<void>;
39
39
  /**
40
40
  * 手动向前推进指定毫秒时间。
41
- * 严格按时间戳递增顺序触发并完成所有到期的休眠计时器。
41
+ * 严格按时间戳递增顺序触发并完成所有到期的休眠计时器;
42
+ * 每轮先排空微任务再检查队列,确保多层 async 边界内链式注册的
43
+ * 已到期 sleep 在本次 advance 终点前全部触发(含链首有 await 边界的场景)。
42
44
  *
43
45
  * @param ms 推进的毫秒数
44
46
  */
45
47
  advance(ms: number): Promise<void>;
48
+ /**
49
+ * 循环排空微任务队列直至稳定(一轮排空后无新的已到期 sleep 注册),
50
+ * 保证链式 sleep 在本次 advance 终点前全部触发。
51
+ *
52
+ * 仅排空微任务,不等待任何宏任务(定时器、IO):链式 sleep 依赖的
53
+ * async 回调链全部由微任务驱动,无需也无法跨越宏任务边界。
54
+ * 使用 setImmediate 兜底作为「微任务队列已排空」的观测哨兵:
55
+ * setImmediate 回调只能从事件循环检查点进入,它的执行必然意味着
56
+ * 在它之前排队的全部微任务(无论多少层 await 边界)都已完成,
57
+ * 以此获得不依赖拍数猜测的精确稳定性判定。
58
+ */
59
+ private drainMicrotasks;
46
60
  /**
47
61
  * 获取当前等待中的计时器数量。
48
62
  */
package/dist/clock.js CHANGED
@@ -59,7 +59,9 @@ export class FakeClock {
59
59
  }
60
60
  /**
61
61
  * 手动向前推进指定毫秒时间。
62
- * 严格按时间戳递增顺序触发并完成所有到期的休眠计时器。
62
+ * 严格按时间戳递增顺序触发并完成所有到期的休眠计时器;
63
+ * 每轮先排空微任务再检查队列,确保多层 async 边界内链式注册的
64
+ * 已到期 sleep 在本次 advance 终点前全部触发(含链首有 await 边界的场景)。
63
65
  *
64
66
  * @param ms 推进的毫秒数
65
67
  */
@@ -67,27 +69,64 @@ export class FakeClock {
67
69
  if (ms < 0) {
68
70
  throw new Error("Cannot advance clock by negative time");
69
71
  }
70
- if (ms === 0) {
71
- await Promise.resolve();
72
- return;
73
- }
74
72
  const destinationMonotonic = this.currentMonotonic + ms;
75
73
  const destinationNow = this.currentNow + ms;
76
- this.advancedMs += ms;
77
- while (this.pendingSleeps.length > 0) {
74
+ if (ms > 0) {
75
+ this.advancedMs += ms;
76
+ }
77
+ // 先同步处理当前已到期项,再排空微任务后复查:resolve 触发的回调链
78
+ // 可能在任意深度的 await 边界后注册新的到期 sleep,每轮排空后重新检查。
79
+ // 首轮无到期项时全程不经过 await,保持「无等待计时器时同步推进时间」契约
80
+ for (;;) {
78
81
  const nextSleep = this.pendingSleeps[0];
79
- if (nextSleep.targetMonotonic > destinationMonotonic) {
82
+ if (!nextSleep || nextSleep.targetMonotonic > destinationMonotonic) {
80
83
  break;
81
84
  }
82
85
  this.pendingSleeps.shift();
83
86
  this.currentMonotonic = nextSleep.targetMonotonic;
84
87
  this.currentNow = nextSleep.targetNow;
85
88
  nextSleep.resolve();
86
- await Promise.resolve();
89
+ await this.drainMicrotasks();
87
90
  }
88
91
  this.currentMonotonic = destinationMonotonic;
89
92
  this.currentNow = destinationNow;
90
- await Promise.resolve();
93
+ await this.drainMicrotasks();
94
+ }
95
+ /**
96
+ * 循环排空微任务队列直至稳定(一轮排空后无新的已到期 sleep 注册),
97
+ * 保证链式 sleep 在本次 advance 终点前全部触发。
98
+ *
99
+ * 仅排空微任务,不等待任何宏任务(定时器、IO):链式 sleep 依赖的
100
+ * async 回调链全部由微任务驱动,无需也无法跨越宏任务边界。
101
+ * 使用 setImmediate 兜底作为「微任务队列已排空」的观测哨兵:
102
+ * setImmediate 回调只能从事件循环检查点进入,它的执行必然意味着
103
+ * 在它之前排队的全部微任务(无论多少层 await 边界)都已完成,
104
+ * 以此获得不依赖拍数猜测的精确稳定性判定。
105
+ */
106
+ drainMicrotasks() {
107
+ return new Promise((resolve) => {
108
+ setImmediate(() => {
109
+ // setImmediate 触发时本轮微任务已全部排空;若排空期间注册了新的
110
+ // 已到期 sleep,再排一轮直至稳定。上限保护防御异常场景下的
111
+ // 自续注册(正常链式回调远达不到此深度)。
112
+ let rounds = 0;
113
+ const check = () => {
114
+ const pendingBefore = this.pendingSleeps.length;
115
+ setImmediate(() => {
116
+ if (this.pendingSleeps.length === pendingBefore) {
117
+ resolve();
118
+ return;
119
+ }
120
+ if (++rounds >= 10000) {
121
+ resolve();
122
+ return;
123
+ }
124
+ check();
125
+ });
126
+ };
127
+ check();
128
+ });
129
+ });
91
130
  }
92
131
  /**
93
132
  * 获取当前等待中的计时器数量。
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./clock.js";
2
2
  export * from "./process.js";
3
+ export * from "./process-driver.js";
3
4
  export * from "./storage.js";
4
5
  export * from "./runtime.js";
5
6
  export * from "./platform.js";
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./clock.js";
2
2
  export * from "./process.js";
3
+ export * from "./process-driver.js";
3
4
  export * from "./storage.js";
4
5
  export * from "./runtime.js";
5
6
  export * from "./platform.js";
@@ -1,6 +1,7 @@
1
- import { type EventSink, type FileSystem, type ModuleLoader, type RuntimePlatform, type RuntimeStorage, type StorageFactory } from "@actiondock/core";
1
+ import { type EventSink, type FileSystem, type ModuleLoader, type ProcessManager, type RuntimePlatform, type RuntimeStorage, type StorageFactory } from "@actiondock/core";
2
2
  import { FakeClock } from "./clock.js";
3
3
  import { MockProcessExecutor } from "./process.js";
4
+ import { FakeProcessDriver } from "./process-driver.js";
4
5
  /**
5
6
  * 测试平台构建配置选项。
6
7
  */
@@ -13,6 +14,10 @@ export interface TestPlatformOptions {
13
14
  globalStorage?: RuntimeStorage;
14
15
  /** 可选注入的模拟进程执行器 */
15
16
  process?: MockProcessExecutor;
17
+ /** 可选注入的底层进程驱动 */
18
+ processDriver?: FakeProcessDriver;
19
+ /** 可选注入的受管进程管理器 */
20
+ processManager?: ProcessManager;
16
21
  /** 可选注入的执行事件接收器 */
17
22
  eventSink?: EventSink;
18
23
  /** 可选注入的文件系统抽象驱动 */
package/dist/platform.js CHANGED
@@ -17,7 +17,11 @@ import { MemoryStorage } from "./storage.js";
17
17
  */
18
18
  export function createTestPlatform(options = {}) {
19
19
  const clock = options.clock ?? new FakeClock();
20
- const process = options.process ?? new MockProcessExecutor();
20
+ const process = options.process ??
21
+ new MockProcessExecutor({
22
+ driver: options.processDriver,
23
+ processManager: options.processManager,
24
+ });
21
25
  const eventSink = options.eventSink ?? new TestEventSink();
22
26
  const files = options.files ?? new NodeFileSystem();
23
27
  const modules = options.modules ?? new DefaultModuleLoader();
@@ -0,0 +1,248 @@
1
+ import type { Capabilities, LaunchSpec } from "@actiondock/sdk";
2
+ import type { ProcessDriver, ProcessDriverCallbacks, ProcessDriverHandle, ProcessHandle, ProcessObserver } from "@actiondock/core";
3
+ export type { ProcessDriver, ProcessObserver, ProcessHandle };
4
+ /**
5
+ * 记录的标准写入操作条目。
6
+ */
7
+ export interface RecordedWrite {
8
+ /** 目标进程句柄 */
9
+ handle: ProcessHandle;
10
+ /** 写入的二进制字节数据 */
11
+ data: Uint8Array;
12
+ /** 调用发生时间戳 */
13
+ timestamp: number;
14
+ }
15
+ /**
16
+ * 记录的标准输入 EOF 调用条目。
17
+ */
18
+ export interface RecordedEOF {
19
+ /** 目标进程句柄 */
20
+ handle: ProcessHandle;
21
+ /** 调用发生时间戳 */
22
+ timestamp: number;
23
+ }
24
+ /**
25
+ * 记录的中断前台作业调用条目。
26
+ */
27
+ export interface RecordedInterrupt {
28
+ /** 目标进程句柄 */
29
+ handle: ProcessHandle;
30
+ /** 调用发生时间戳 */
31
+ timestamp: number;
32
+ }
33
+ /**
34
+ * 记录的调整尺寸调用条目。
35
+ */
36
+ export interface RecordedResize {
37
+ /** 目标进程句柄 */
38
+ handle: ProcessHandle;
39
+ /** 列数 */
40
+ cols: number;
41
+ /** 行数 */
42
+ rows: number;
43
+ /** 调用发生时间戳 */
44
+ timestamp: number;
45
+ }
46
+ /**
47
+ * 记录的终止进程调用条目。
48
+ */
49
+ export interface RecordedTerminate {
50
+ /** 目标进程句柄 */
51
+ handle: ProcessHandle;
52
+ /** 宽限退出时限 */
53
+ graceMs: number;
54
+ /** 调用发生时间戳 */
55
+ timestamp: number;
56
+ }
57
+ /**
58
+ * 记录的销毁进程调用条目。
59
+ */
60
+ export interface RecordedDispose {
61
+ /** 目标进程句柄 */
62
+ handle: ProcessHandle;
63
+ /** 调用发生时间戳 */
64
+ timestamp: number;
65
+ }
66
+ /**
67
+ * 记录的进程派生启动调用条目。
68
+ */
69
+ export interface RecordedSpawn {
70
+ /** 生成的进程句柄 */
71
+ handle: ProcessHandle;
72
+ /** 进程启动规范 */
73
+ spec: LaunchSpec;
74
+ /** 进程观察者对象 */
75
+ observer: ProcessObserver;
76
+ /** 调用发生时间戳 */
77
+ timestamp: number;
78
+ }
79
+ /**
80
+ * 确定性测试专用进程驱动桩。
81
+ * 遵循《ActionDock Managed Process 设计 v2》第 11、12 节契约:
82
+ * - 支持确定性模拟输出(emitOutput)
83
+ * - 支持确定性模拟退出(emitExit)
84
+ * - 支持确定性模拟输出关闭(emitOutputClosed)
85
+ * - 支持确定性模拟故障注入(emitFault)
86
+ * - 记录完整写入(writes)、EOF 调用、resize 与 terminate 操作历史
87
+ * - 支持模拟各阶段操作失败注入用于健壮性测试
88
+ */
89
+ export declare class FakeProcessDriver implements ProcessDriver {
90
+ private capabilities;
91
+ private readonly handles;
92
+ /** 记录所有派生调用 */
93
+ readonly spawnCalls: RecordedSpawn[];
94
+ /** 记录所有写入调用 */
95
+ readonly writes: RecordedWrite[];
96
+ /** 记录所有 EOF 调用 */
97
+ readonly eofCalls: RecordedEOF[];
98
+ /** 记录所有前台中断调用 */
99
+ readonly interruptCalls: RecordedInterrupt[];
100
+ /** 记录所有调整尺寸调用 */
101
+ readonly resizeCalls: RecordedResize[];
102
+ /** 记录所有终止调用 */
103
+ readonly terminateCalls: RecordedTerminate[];
104
+ /** 记录所有销毁调用 */
105
+ readonly disposeCalls: RecordedDispose[];
106
+ /** 故障注入:下一次派生将抛出的异常 */
107
+ nextSpawnError?: Error;
108
+ /** 故障注入:写入时将抛出的异常 */
109
+ nextWriteError?: Error;
110
+ /** 故障注入:调整尺寸时将抛出的异常 */
111
+ nextResizeError?: Error;
112
+ /** 故障注入:终止时将抛出的异常 */
113
+ nextTerminateError?: Error;
114
+ /** 自动响应回调:在进程派生后触发 */
115
+ onSpawn?: (handle: ProcessHandle, spec: LaunchSpec, observer: ProcessObserver) => void;
116
+ constructor(capabilities?: Partial<Capabilities>);
117
+ /**
118
+ * 获取驱动能力集合。
119
+ */
120
+ getCapabilities(): Capabilities;
121
+ /**
122
+ * 覆盖驱动能力集合。
123
+ */
124
+ setCapabilities(caps: Partial<Capabilities>): void;
125
+ /**
126
+ * 派生启动新进程,支持标准与旧版重载签名。
127
+ */
128
+ spawn(spec: LaunchSpec, observer: ProcessObserver): Promise<ProcessHandle>;
129
+ spawn(processId: string, spec: LaunchSpec, callbacks: ProcessDriverCallbacks): Promise<ProcessDriverHandle>;
130
+ /**
131
+ * 标准接口实现派生新进程。
132
+ */
133
+ private spawnStandard;
134
+ /**
135
+ * 模拟写入数据并记录历史。
136
+ */
137
+ write(handle: ProcessHandle, data: Uint8Array): Promise<void>;
138
+ /**
139
+ * 模拟输入流关闭并记录历史。
140
+ */
141
+ inputEOF(handle: ProcessHandle): Promise<void>;
142
+ /**
143
+ * 模拟中断前台作业并记录历史。
144
+ */
145
+ interruptForeground(handle: ProcessHandle): Promise<void>;
146
+ /**
147
+ * 模拟调整终端尺寸并记录历史。
148
+ */
149
+ resize(handle: ProcessHandle, cols: number, rows: number): Promise<void>;
150
+ /**
151
+ * 模拟终止进程并记录历史。
152
+ *
153
+ * 终止语义与 MemoryProcessDriver 对齐:除非测试通过 nextTerminateError
154
+ * 注入故障,否则终止后进程必须退出(触发 exited 回调),否则
155
+ * ProcessManager.run 的超时/超限路径在 terminate 后永远收不到退出事件,
156
+ * 调用方会永久挂起。需要非退出语义的用例可先 setExitBehavior 或
157
+ * 直接使用 handle 上的确定性模拟接口自行控制退出时机。
158
+ */
159
+ terminate(handle: ProcessHandle, graceMs: number): Promise<void>;
160
+ terminate(processId: string, graceMs: number): Promise<void>;
161
+ /**
162
+ * 模拟销毁进程并记录历史。
163
+ */
164
+ dispose(handle: ProcessHandle): Promise<void>;
165
+ /**
166
+ * 确定性模拟向观察者发送输出。
167
+ */
168
+ emitOutput(handleOrId: ProcessHandle | string, stream: "stdout" | "stderr" | "pty", data: Uint8Array | string): void;
169
+ /**
170
+ * 确定性模拟进程退出事件。
171
+ */
172
+ emitExit(handleOrId: ProcessHandle | string, result?: {
173
+ code?: number | null;
174
+ signal?: string | null;
175
+ } | number): void;
176
+ /**
177
+ * 确定性模拟输出流彻底关闭事件。
178
+ */
179
+ emitOutputClosed(handleOrId: ProcessHandle | string, reason?: "natural" | "drain-timeout" | "host-lost"): void;
180
+ /**
181
+ * 确定性模拟驱动故障通知。
182
+ */
183
+ emitFault(handleOrId: ProcessHandle | string, error: Error): void;
184
+ /**
185
+ * 注入下一次 spawn 故障异常。
186
+ */
187
+ simulateSpawnFailure(error: Error): void;
188
+ /**
189
+ * 注入下一次 write 故障异常。
190
+ */
191
+ simulateWriteFailure(error: Error): void;
192
+ /**
193
+ * 注入下一次 resize 故障异常。
194
+ */
195
+ simulateResizeFailure(error: Error): void;
196
+ /**
197
+ * 注入下一次 terminate 故障异常。
198
+ */
199
+ simulateTerminateFailure(error: Error): void;
200
+ /**
201
+ * 获取最近创建的进程句柄。
202
+ */
203
+ getLastHandle(): ProcessHandle | undefined;
204
+ /**
205
+ * 查询指定句柄标识对应的句柄实例。
206
+ */
207
+ getHandle(handleOrId: ProcessHandle | string): ProcessHandle | undefined;
208
+ /**
209
+ * 查询指定句柄对应的观察者。
210
+ */
211
+ getObserver(handleOrId: ProcessHandle | string): ProcessObserver | undefined;
212
+ /**
213
+ * 获取针对指定句柄(或全部句柄)的写入字节切片列表。
214
+ */
215
+ getWrites(handleOrId?: ProcessHandle | string): Uint8Array[];
216
+ /**
217
+ * 获取针对指定句柄(或全部句柄)写入的 UTF-8 解码文本列表。
218
+ */
219
+ getWrittenStrings(handleOrId?: ProcessHandle | string): string[];
220
+ /**
221
+ * 判断指定句柄是否接收到了 EOF 输入结束通知。
222
+ */
223
+ hasInputEOF(handleOrId?: ProcessHandle | string): boolean;
224
+ /**
225
+ * 判断指定句柄是否接收到了前台作业中断通知。
226
+ */
227
+ hasInterrupted(handleOrId?: ProcessHandle | string): boolean;
228
+ /**
229
+ * 清除历史调用记录。
230
+ */
231
+ clearHistory(): void;
232
+ /**
233
+ * 重置全部句柄、模拟规则与调用历史。
234
+ */
235
+ reset(): void;
236
+ /**
237
+ * 兼容旧版基于 processId 派生新进程。
238
+ */
239
+ private spawnLegacy;
240
+ /**
241
+ * 内部解析指定句柄或标识。
242
+ */
243
+ private resolveInternal;
244
+ }
245
+ /**
246
+ * 兼容原有命名导出。
247
+ */
248
+ export { FakeProcessDriver as MockProcessDriver };