tt-help-cli-ycl 1.4.169 → 1.4.172
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/package.json +1 -1
- package/scripts/test-subscription-allocator.mjs +220 -0
- package/scripts/test-subscription-auto-alloc-ui.mjs +208 -0
- package/scripts/test-subscription-auto-alloc.mjs +259 -0
- package/scripts/test-subscription-center-e2e.mjs +134 -0
- package/scripts/test-subscription-center-smoke.mjs +154 -0
- package/scripts/test-subscription-center-ui.mjs +111 -0
- package/scripts/vnc-login.sh +68 -0
- package/src/cli/info.js +6 -1
- package/src/lib/browser/cdp.js +7 -1
- package/src/watch/public/app.js +713 -39
- package/src/watch/public/index.html +74 -3
- package/src/watch/subscription-allocator.js +255 -0
- package/src/watch/subscription-center.js +590 -0
- package/src/watch/watchdog-server.js +210 -0
- package/src/watchdog/agent.js +104 -10
- package/scripts/test-api-interceptor-429.mjs +0 -122
- package/scripts/test-api-interceptor-captcha.mjs +0 -176
- package/scripts/test-claim-fix.mjs +0 -216
- package/scripts/test-clash-discover.mjs +0 -126
- package/scripts/test-clash-hysteresis.mjs +0 -131
- package/scripts/test-clash-multichannel.mjs +0 -120
- package/scripts/test-client-error-monitor.mjs +0 -261
- package/scripts/test-country-fix.mjs +0 -341
- package/scripts/test-daemon-failover.mjs +0 -301
- package/scripts/test-daemon-switch-history.mjs +0 -141
- package/scripts/test-daemon-whitelist.mjs +0 -308
- package/scripts/test-explore-captcha-wait.mjs +0 -170
- package/scripts/test-photo-post.mjs +0 -156
- package/scripts/test-precheck-perf.mjs +0 -270
- package/scripts/test-precise-index.mjs +0 -114
- package/scripts/test-probe-live.mjs +0 -83
- package/scripts/test-probe.mjs +0 -315
- package/scripts/test-product-fill-cdp.mjs +0 -336
- package/scripts/test-product-fill-db.mjs +0 -216
- package/scripts/test-product-fill-http.mjs +0 -117
- package/scripts/test-proxy-switch-restart.mjs +0 -198
- package/scripts/test-refill-order.mjs +0 -218
- package/scripts/test-sample-all-fail.mjs +0 -142
- package/scripts/test-seller-any-country.mjs +0 -167
- package/scripts/test-stats-smoke.mjs +0 -43
- package/scripts/test-upgrade-spawn.mjs +0 -179
- package/scripts/test-webserver-error-classify.mjs +0 -409
- package/scripts/test-wmi-free-upgrade.mjs +0 -98
- package/src/cli/test-real-attach.js +0 -0
- package/src/results/user-videos-bar.lar.lar.moeta.json +0 -37
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* clash 多控制端多通道 — 验证脚本
|
|
4
|
-
* 用法: node scripts/test-clash-multichannel.mjs
|
|
5
|
-
*/
|
|
6
|
-
import { discoverAll, getSystemProxyPorts } from "../src/clash/discover.js";
|
|
7
|
-
import * as ipc from "../src/clash/ipc.js";
|
|
8
|
-
|
|
9
|
-
let failed = 0;
|
|
10
|
-
function check(name, cond, extra = "") {
|
|
11
|
-
console.log(`${cond ? "✓" : "✗"} ${name}${extra ? ` — ${extra}` : ""}`);
|
|
12
|
-
if (!cond) failed++;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// ── 1. discoverAll:多控制端发现 ──
|
|
16
|
-
const all = await discoverAll();
|
|
17
|
-
console.log(` 发现 ${all.length} 个控制端:`);
|
|
18
|
-
for (const c of all) {
|
|
19
|
-
console.log(
|
|
20
|
-
` - [${c.proxyPort}] ${c.clientName} @${c.port}(${c.source}${c.fallback ? ",回退默认值" : ""})`,
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
check("discoverAll 返回数组", Array.isArray(all) && all.length > 0);
|
|
24
|
-
check(
|
|
25
|
-
"每个控制端含 proxyPort/proxyUrl",
|
|
26
|
-
all.every((c) => c.proxyPort > 0 && c.proxyUrl?.startsWith("http://")),
|
|
27
|
-
);
|
|
28
|
-
const sysPorts = getSystemProxyPorts();
|
|
29
|
-
if (sysPorts.length && all.length > 1) {
|
|
30
|
-
check(
|
|
31
|
-
"system-proxy 命中者排第一(主控制端)",
|
|
32
|
-
sysPorts.includes(all[0].proxyPort),
|
|
33
|
-
`主控制端 proxyPort=${all[0].proxyPort}, 系统代理=[${sysPorts.join(",")}]`,
|
|
34
|
-
);
|
|
35
|
-
} else {
|
|
36
|
-
console.log(
|
|
37
|
-
`○ 跳过主控制端排序断言(系统代理=[${sysPorts.join(",")}] 或单控制端)`,
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// ── 2. IPC 多通道读写 ──
|
|
42
|
-
const TEST_PORT = 7890;
|
|
43
|
-
ipc.writeCommand(
|
|
44
|
-
{ action: "failover", reason: "multichannel-test", proxyPort: TEST_PORT },
|
|
45
|
-
TEST_PORT,
|
|
46
|
-
);
|
|
47
|
-
const readBack = ipc.readCommand(TEST_PORT);
|
|
48
|
-
check(
|
|
49
|
-
"按端口写读命令通道",
|
|
50
|
-
Array.isArray(readBack) &&
|
|
51
|
-
readBack[0]?.action === "failover" &&
|
|
52
|
-
readBack[0]?.proxyPort === TEST_PORT,
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
ipc.writeHealth({ node: "test", healthy: true }, TEST_PORT);
|
|
56
|
-
const hBack = ipc.readHealth(TEST_PORT);
|
|
57
|
-
check(
|
|
58
|
-
"按端口写读健康通道",
|
|
59
|
-
!!hBack && hBack.node === "test" && hBack.healthy === true,
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
// 默认通道与端口通道互不影响
|
|
63
|
-
const legacyCmd = ipc.readCommand(0);
|
|
64
|
-
check(
|
|
65
|
-
"默认通道独立(未被端口通道污染)",
|
|
66
|
-
!legacyCmd || legacyCmd[0]?.action !== "failover" || !legacyCmd[0]?.proxyPort,
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
const channels = ipc.listCommandChannels();
|
|
70
|
-
check(
|
|
71
|
-
"listCommandChannels 含测试通道",
|
|
72
|
-
channels.includes(TEST_PORT),
|
|
73
|
-
`channels=[${channels.join(",")}]`,
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
ipc.clearCommand(TEST_PORT);
|
|
77
|
-
check("clearCommand 清空端口通道", ipc.readCommand(TEST_PORT) === null);
|
|
78
|
-
|
|
79
|
-
// ── 3. 路由逻辑(用 daemon 实例验证,不启动守护) ──
|
|
80
|
-
import { ClashDaemon } from "../src/clash/daemon.js";
|
|
81
|
-
const d = new ClashDaemon({ dryRun: true });
|
|
82
|
-
d._controllers = all;
|
|
83
|
-
d._primary = all[0];
|
|
84
|
-
const routed = d._routeController({
|
|
85
|
-
action: "failover",
|
|
86
|
-
proxyPort: all[0].proxyPort,
|
|
87
|
-
});
|
|
88
|
-
check(
|
|
89
|
-
"命令按 proxyPort 路由到对应控制端",
|
|
90
|
-
!!routed && routed.proxyPort === all[0].proxyPort,
|
|
91
|
-
);
|
|
92
|
-
const routedMiss = d._routeController({ action: "failover", proxyPort: 59999 });
|
|
93
|
-
check("未知端口回退主控制端", !!routedMiss && routedMiss === d._primary);
|
|
94
|
-
|
|
95
|
-
// 多控制端时:第二个控制端也能被路由命中
|
|
96
|
-
if (all.length > 1) {
|
|
97
|
-
const routed2 = d._routeController({
|
|
98
|
-
action: "switchNode",
|
|
99
|
-
node: "x",
|
|
100
|
-
proxyPort: all[1].proxyPort,
|
|
101
|
-
});
|
|
102
|
-
check(
|
|
103
|
-
"第二个控制端按端口路由命中",
|
|
104
|
-
!!routed2 && routed2.proxyPort === all[1].proxyPort,
|
|
105
|
-
`${routed2?.clientName} @${routed2?.port}`,
|
|
106
|
-
);
|
|
107
|
-
} else {
|
|
108
|
-
console.log("○ 单控制端,跳过第二控制端路由断言");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// 清理测试产生的健康文件
|
|
112
|
-
try {
|
|
113
|
-
const { rmSync } = await import("fs");
|
|
114
|
-
const { tmpdir } = await import("os");
|
|
115
|
-
const { join } = await import("path");
|
|
116
|
-
rmSync(join(tmpdir(), `clash-health-${TEST_PORT}.json`), { force: true });
|
|
117
|
-
} catch {}
|
|
118
|
-
|
|
119
|
-
console.log(failed ? `\n✗ ${failed} 项失败` : "\n✓ 全部通过");
|
|
120
|
-
process.exit(failed ? 1 : 0);
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 客户端异常监控单测(2026-08-28,2026-09-28 增补验证码路径,
|
|
3
|
-
* 2026-09-29 改为 clientId 本机关联 —— 克隆机 hostname 撞名修复)
|
|
4
|
-
*
|
|
5
|
-
* 验证:
|
|
6
|
-
* - detectClientError 检测函数:
|
|
7
|
-
* A. reportCount > 8 且最近一次上报在 2 分钟内
|
|
8
|
-
* B. captchaCount ≥ 3(webserver 验证码上报,10 分钟过期窗口内累计)
|
|
9
|
-
* - 按 clientId 过滤本机条目(每台唯一);hostname 撞名的兄弟机条目、
|
|
10
|
-
* 无 clientId 的旧条目均不参与关联
|
|
11
|
-
* - 命中 → onAlert 收到本机全部条目与命中列表;未命中 → 不回调
|
|
12
|
-
* - removeErrors 逐条 DELETE 并计数
|
|
13
|
-
*
|
|
14
|
-
* 运行: node --test scripts/test-client-error-monitor.mjs
|
|
15
|
-
*/
|
|
16
|
-
import { test, mock } from "node:test";
|
|
17
|
-
import assert from "node:assert/strict";
|
|
18
|
-
import {
|
|
19
|
-
detectClientError,
|
|
20
|
-
ClientErrorMonitor,
|
|
21
|
-
} from "../src/watchdog/client-error-monitor.js";
|
|
22
|
-
|
|
23
|
-
// 模拟克隆机群:hostname 全部相同(事故现场),clientId 每台唯一
|
|
24
|
-
const CLONED_HOST = "WIN-AADJ1HTCCAQ";
|
|
25
|
-
const CLIENT_ID = `F-9@${CLONED_HOST}`;
|
|
26
|
-
const BROTHER_CLIENT_ID = `F-10@${CLONED_HOST}`;
|
|
27
|
-
|
|
28
|
-
function makeErr(overrides = {}) {
|
|
29
|
-
return {
|
|
30
|
-
userId: "u1",
|
|
31
|
-
hostname: CLONED_HOST,
|
|
32
|
-
clientId: CLIENT_ID,
|
|
33
|
-
errorType: "consecutive_blocked",
|
|
34
|
-
reportCount: 10,
|
|
35
|
-
timestamp: Date.now() - 30_000,
|
|
36
|
-
...overrides,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function makeMonitor(opts = {}) {
|
|
41
|
-
let received = null;
|
|
42
|
-
let alerted = false;
|
|
43
|
-
const m = new ClientErrorMonitor({
|
|
44
|
-
serverUrl: "http://127.0.0.1:3000",
|
|
45
|
-
hostname: CLONED_HOST, // 仅展示用;关联只认 clientId
|
|
46
|
-
clientId: CLIENT_ID,
|
|
47
|
-
onAlert: async (mine, hit) => {
|
|
48
|
-
alerted = true;
|
|
49
|
-
received = { mine, hit };
|
|
50
|
-
},
|
|
51
|
-
...opts,
|
|
52
|
-
});
|
|
53
|
-
return { m, isAlerted: () => alerted, getReceived: () => received };
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// ── 检测函数 ──
|
|
57
|
-
|
|
58
|
-
test("D1 detectClientError:累计>8 且 2 分钟内 → 命中", () => {
|
|
59
|
-
assert.equal(
|
|
60
|
-
detectClientError(makeErr({ reportCount: 9 }), Date.now()),
|
|
61
|
-
true,
|
|
62
|
-
);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test("D2 detectClientError:reportCount=8 不命中(阈值是 >8)", () => {
|
|
66
|
-
assert.equal(
|
|
67
|
-
detectClientError(makeErr({ reportCount: 8 }), Date.now()),
|
|
68
|
-
false,
|
|
69
|
-
);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("D3 detectClientError:最近上报超过 2 分钟 → 不命中", () => {
|
|
73
|
-
assert.equal(
|
|
74
|
-
detectClientError(
|
|
75
|
-
makeErr({ timestamp: Date.now() - 2 * 60_000 - 1 }),
|
|
76
|
-
Date.now(),
|
|
77
|
-
),
|
|
78
|
-
false,
|
|
79
|
-
);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test("D4 detectClientError:缺 reportCount → 不命中", () => {
|
|
83
|
-
assert.equal(detectClientError({ timestamp: Date.now() }, Date.now()), false);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// ── 验证码路径(captchaCount ≥ 3) ──
|
|
87
|
-
|
|
88
|
-
test("C1 detectClientError:captchaCount=3 → 命中", () => {
|
|
89
|
-
assert.equal(
|
|
90
|
-
detectClientError(makeErr({ reportCount: 0, captchaCount: 3 }), Date.now()),
|
|
91
|
-
true,
|
|
92
|
-
);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test("C2 detectClientError:captchaCount=2 不命中(阈值是 ≥3)", () => {
|
|
96
|
-
assert.equal(
|
|
97
|
-
detectClientError(makeErr({ reportCount: 0, captchaCount: 2 }), Date.now()),
|
|
98
|
-
false,
|
|
99
|
-
);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("C3 detectClientError:captchaCount=3 但最近上报超 2 分钟 → 不命中", () => {
|
|
103
|
-
assert.equal(
|
|
104
|
-
detectClientError(
|
|
105
|
-
makeErr({
|
|
106
|
-
reportCount: 0,
|
|
107
|
-
captchaCount: 3,
|
|
108
|
-
timestamp: Date.now() - 2 * 60_000 - 1,
|
|
109
|
-
}),
|
|
110
|
-
Date.now(),
|
|
111
|
-
),
|
|
112
|
-
false,
|
|
113
|
-
);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
test("C4 detectClientError:reportCount 未达标 + captchaCount 达标 → 命中", () => {
|
|
117
|
-
assert.equal(
|
|
118
|
-
detectClientError(makeErr({ reportCount: 1, captchaCount: 5 }), Date.now()),
|
|
119
|
-
true,
|
|
120
|
-
);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test("C5 监控器:验证码条目命中 → onAlert 收到命中列表", async () => {
|
|
124
|
-
const captchaHit = makeErr({
|
|
125
|
-
userId: "webserver-host",
|
|
126
|
-
reportCount: 0,
|
|
127
|
-
captchaCount: 3,
|
|
128
|
-
});
|
|
129
|
-
const businessMiss = makeErr({ userId: "u-task", reportCount: 2 });
|
|
130
|
-
mock.method(globalThis, "fetch", async () => ({
|
|
131
|
-
ok: true,
|
|
132
|
-
json: async () => ({ clients: [captchaHit, businessMiss] }),
|
|
133
|
-
}));
|
|
134
|
-
const { m, getReceived } = makeMonitor();
|
|
135
|
-
const hit = await m._poll();
|
|
136
|
-
assert.equal(hit.length, 1, "仅验证码条目命中");
|
|
137
|
-
assert.equal(hit[0].userId, "webserver-host");
|
|
138
|
-
assert.equal(getReceived().hit.length, 1);
|
|
139
|
-
assert.equal(getReceived().mine.length, 2, "回调同时携带本机未命中条目");
|
|
140
|
-
mock.restoreAll();
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// ── 监控器:本机关联(clientId) ──
|
|
144
|
-
|
|
145
|
-
test("M1 按 clientId 过滤:撞名兄弟机条目不触发本机回调", async () => {
|
|
146
|
-
// 克隆机事故回归:F-10 与 F-9 hostname 完全相同,仅 clientId 不同
|
|
147
|
-
mock.method(globalThis, "fetch", async () => ({
|
|
148
|
-
ok: true,
|
|
149
|
-
json: async () => ({
|
|
150
|
-
clients: [
|
|
151
|
-
makeErr({
|
|
152
|
-
userId: "F-10-8222-1",
|
|
153
|
-
clientId: BROTHER_CLIENT_ID,
|
|
154
|
-
captchaCount: 3,
|
|
155
|
-
reportCount: 0,
|
|
156
|
-
}),
|
|
157
|
-
],
|
|
158
|
-
}),
|
|
159
|
-
}));
|
|
160
|
-
const { m, isAlerted } = makeMonitor();
|
|
161
|
-
const hit = await m._poll();
|
|
162
|
-
assert.equal(isAlerted(), false, "兄弟机异常不应触发本机回调/强切");
|
|
163
|
-
assert.equal(hit, null, "无本机条目应返回 null");
|
|
164
|
-
mock.restoreAll();
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
test("M2 命中:onAlert 收到本机全部条目(含未命中条目)", async () => {
|
|
168
|
-
const mineHit = makeErr({ userId: "u-hit", reportCount: 10 });
|
|
169
|
-
const mineMiss = makeErr({ userId: "u-miss", reportCount: 2 });
|
|
170
|
-
const other = makeErr({
|
|
171
|
-
userId: "u-other",
|
|
172
|
-
clientId: BROTHER_CLIENT_ID, // hostname 相同,clientId 不同
|
|
173
|
-
});
|
|
174
|
-
mock.method(globalThis, "fetch", async () => ({
|
|
175
|
-
ok: true,
|
|
176
|
-
json: async () => ({ clients: [mineHit, mineMiss, other] }),
|
|
177
|
-
}));
|
|
178
|
-
const { m, getReceived } = makeMonitor();
|
|
179
|
-
const hit = await m._poll();
|
|
180
|
-
assert.equal(hit.length, 1, "命中 1 条");
|
|
181
|
-
assert.equal(hit[0].userId, "u-hit");
|
|
182
|
-
assert.equal(getReceived().mine.length, 2, "回调收到本机全部 2 条");
|
|
183
|
-
assert.deepEqual(
|
|
184
|
-
getReceived()
|
|
185
|
-
.mine.map((e) => e.userId)
|
|
186
|
-
.sort(),
|
|
187
|
-
["u-hit", "u-miss"],
|
|
188
|
-
"回调含命中与未命中的本机条目",
|
|
189
|
-
);
|
|
190
|
-
mock.restoreAll();
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
test("M3 无 clientId 的旧条目不参与本机关联(宁漏不误)", async () => {
|
|
194
|
-
mock.method(globalThis, "fetch", async () => ({
|
|
195
|
-
ok: true,
|
|
196
|
-
json: async () => ({
|
|
197
|
-
clients: [makeErr({ clientId: "", reportCount: 99 })],
|
|
198
|
-
}),
|
|
199
|
-
}));
|
|
200
|
-
const { m, isAlerted } = makeMonitor();
|
|
201
|
-
const hit = await m._poll();
|
|
202
|
-
assert.equal(isAlerted(), false, "旧版上报条目不应触发强切");
|
|
203
|
-
assert.equal(hit, null);
|
|
204
|
-
mock.restoreAll();
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
test("M4 未配置 clientId 的监控器:不匹配任何条目", async () => {
|
|
208
|
-
mock.method(globalThis, "fetch", async () => ({
|
|
209
|
-
ok: true,
|
|
210
|
-
json: async () => ({ clients: [makeErr({ reportCount: 99 })] }),
|
|
211
|
-
}));
|
|
212
|
-
let alerted = false;
|
|
213
|
-
const m = new ClientErrorMonitor({
|
|
214
|
-
serverUrl: "http://127.0.0.1:3000",
|
|
215
|
-
onAlert: async () => {
|
|
216
|
-
alerted = true;
|
|
217
|
-
},
|
|
218
|
-
});
|
|
219
|
-
const hit = await m._poll();
|
|
220
|
-
assert.equal(alerted, false, "clientId 未配置时监控不生效");
|
|
221
|
-
assert.equal(hit, null);
|
|
222
|
-
mock.restoreAll();
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
test("M5 removeErrors:逐条 DELETE 并计数", async () => {
|
|
226
|
-
const deleted = [];
|
|
227
|
-
mock.method(globalThis, "fetch", async (url, opts) => {
|
|
228
|
-
if (opts && opts.method === "DELETE") {
|
|
229
|
-
deleted.push(String(url));
|
|
230
|
-
return { ok: true };
|
|
231
|
-
}
|
|
232
|
-
return { ok: true, json: async () => ({ clients: [] }) };
|
|
233
|
-
});
|
|
234
|
-
const m = new ClientErrorMonitor({ serverUrl: "http://127.0.0.1:3000" });
|
|
235
|
-
const removed = await m.removeErrors([
|
|
236
|
-
{ userId: "a" },
|
|
237
|
-
{ userId: "b" },
|
|
238
|
-
{ userId: "" },
|
|
239
|
-
null,
|
|
240
|
-
]);
|
|
241
|
-
assert.equal(removed, 2, "有效 userId 各删一条");
|
|
242
|
-
assert.equal(deleted.length, 2, "共 2 次 DELETE");
|
|
243
|
-
assert.ok(
|
|
244
|
-
deleted.every((u) =>
|
|
245
|
-
["a", "b"].some((id) => u.endsWith(`/api/client-error/${id}`)),
|
|
246
|
-
),
|
|
247
|
-
"DELETE 路径正确",
|
|
248
|
-
);
|
|
249
|
-
mock.restoreAll();
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
test("M6 拉取失败静默:fetch 抛错不回调不崩溃", async () => {
|
|
253
|
-
mock.method(globalThis, "fetch", async () => {
|
|
254
|
-
throw new Error("ECONNREFUSED");
|
|
255
|
-
});
|
|
256
|
-
const { m, isAlerted } = makeMonitor();
|
|
257
|
-
const hit = await m._poll();
|
|
258
|
-
assert.equal(isAlerted(), false);
|
|
259
|
-
assert.equal(hit, null);
|
|
260
|
-
mock.restoreAll();
|
|
261
|
-
});
|