@mzzsfy/dsh-bug-perf 0.0.0-stage → 0.2.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.
@@ -0,0 +1,186 @@
1
+ import test from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { readFileSync } from 'node:fs'
4
+
5
+ const source = readFileSync(new URL('../src/client.js', import.meta.url), 'utf8')
6
+
7
+ function loadPlugin() {
8
+ let registered
9
+ const window = { __ModuleLoader__: { load: (entry) => { registered = entry } } }
10
+ new Function('window', source)(window)
11
+ return registered.factory(() => { throw new Error('no modules') })
12
+ }
13
+
14
+ function fakeTimers() {
15
+ let now = 0
16
+ let nextId = 0
17
+ const intervals = new Map()
18
+ return {
19
+ now: () => now,
20
+ setInterval: (fn, ms) => { const id = ++nextId; intervals.set(id, { fn, every: ms, at: now + ms }); return id },
21
+ clearInterval: (id) => { intervals.delete(id) },
22
+ advance(ms) {
23
+ now += ms
24
+ for (const [id, timer] of [...intervals]) {
25
+ if (timer.at <= now) {
26
+ timer.at = now + timer.every
27
+ timer.fn()
28
+ }
29
+ }
30
+ },
31
+ intervalCount: () => intervals.size,
32
+ }
33
+ }
34
+
35
+ function fakeView({ writable = false, phase = 'connected', revision = 1, pendingRender, hasStream = true } = {}) {
36
+ const snapshot = { phase, writable }
37
+ return {
38
+ sessionId: 'session-a',
39
+ id: 'term-a',
40
+ revision,
41
+ pendingRender,
42
+ stream: hasStream ? {} : undefined,
43
+ connects: 0,
44
+ lifetime: { signal: { addEventListener() {} } },
45
+ state: { getSnapshot: () => snapshot },
46
+ snapshot,
47
+ connect() { this.connects += 1 },
48
+ }
49
+ }
50
+
51
+ const OPTIONS = { checkMs: 2000, stallMs: 8000, cooldownMs: 60000 }
52
+
53
+ function stateFor(view, now = 0) {
54
+ return { revision: view.revision, changedAt: now, armed: false, healedAt: -Infinity }
55
+ }
56
+
57
+ // Given 视图从未可写(旁观只读), When 长时间无输出, Then 不触发重连
58
+ test('看门狗_未武装_不触发', () => {
59
+ const plugin = loadPlugin()
60
+ const view = fakeView({ writable: false })
61
+ const state = stateFor(view)
62
+ const result = plugin.__test.watchdogTick({ view, state, options: OPTIONS, now: 30000, visible: true })
63
+ assert.equal(result, 'idle')
64
+ assert.equal(state.armed, false)
65
+ })
66
+
67
+ // Given 视图曾接管输入随后卡住, When 停滞超过阈值, Then 触发一次重连并释放武装
68
+ test('看门狗_已武装停滞_触发重连', () => {
69
+ const plugin = loadPlugin()
70
+ const view = fakeView({ writable: true })
71
+ const state = stateFor(view)
72
+ assert.equal(plugin.__test.watchdogTick({ view, state, options: OPTIONS, now: 1000, visible: true }), 'progress')
73
+ assert.equal(state.armed, true)
74
+ view.snapshot.writable = false
75
+ assert.equal(plugin.__test.watchdogTick({ view, state, options: OPTIONS, now: 5000, visible: true }), 'idle')
76
+ assert.equal(plugin.__test.watchdogTick({ view, state, options: OPTIONS, now: 10500, visible: true }), 'heal')
77
+ assert.equal(state.armed, false)
78
+ assert.equal(state.healedAt, 10500)
79
+ })
80
+
81
+ // Given 视图持续有新帧, When 每轮检查, Then 只报进度不触发
82
+ test('看门狗_持续有新帧_不触发', () => {
83
+ const plugin = loadPlugin()
84
+ const view = fakeView({ writable: true })
85
+ const state = stateFor(view)
86
+ for (let i = 1; i <= 6; i++) {
87
+ view.revision = i
88
+ const result = plugin.__test.watchdogTick({ view, state, options: OPTIONS, now: i * 10000, visible: true })
89
+ assert.equal(result, 'progress')
90
+ }
91
+ })
92
+
93
+ // Given 刚自愈过, When 再次停滞进入冷却期, Then 不重复重连
94
+ test('看门狗_冷却期_不重复', () => {
95
+ const plugin = loadPlugin()
96
+ const view = fakeView({ writable: true })
97
+ const state = stateFor(view)
98
+ plugin.__test.watchdogTick({ view, state, options: OPTIONS, now: 1000, visible: true })
99
+ view.snapshot.writable = false
100
+ assert.equal(plugin.__test.watchdogTick({ view, state, options: OPTIONS, now: 9500, visible: true }), 'heal')
101
+ state.armed = true
102
+ assert.equal(plugin.__test.watchdogTick({ view, state, options: OPTIONS, now: 20000, visible: true }), 'cooldown')
103
+ })
104
+
105
+ // Given 页面隐藏, When 停滞判定到期, Then 不触发(隐藏期的停顿不是故障)
106
+ test('看门狗_页面隐藏_不触发', () => {
107
+ const plugin = loadPlugin()
108
+ const view = fakeView({ writable: true })
109
+ const state = stateFor(view)
110
+ assert.equal(plugin.__test.watchdogTick({ view, state, options: OPTIONS, now: 1000, visible: true }), 'progress')
111
+ view.snapshot.writable = false
112
+ assert.equal(plugin.__test.watchdogTick({ view, state, options: OPTIONS, now: 20000, visible: false }), 'hidden')
113
+ })
114
+
115
+ // Given 视图正在等渲染或没有 attach 流, When 检查, Then 不当作冻结处理
116
+ test('看门狗_待渲染或无流_不触发', () => {
117
+ const plugin = loadPlugin()
118
+ const pending = fakeView({ writable: true, pendingRender: {} })
119
+ const pendingState = stateFor(pending)
120
+ plugin.__test.watchdogTick({ view: pending, state: pendingState, options: OPTIONS, now: 1000, visible: true })
121
+ pending.snapshot.writable = false
122
+ assert.equal(plugin.__test.watchdogTick({ view: pending, state: pendingState, options: OPTIONS, now: 20000, visible: true }), 'pending-render')
123
+
124
+ const detached = fakeView({ writable: true, hasStream: false })
125
+ const detachedState = stateFor(detached)
126
+ plugin.__test.watchdogTick({ view: detached, state: detachedState, options: OPTIONS, now: 1000, visible: true })
127
+ detached.snapshot.writable = false
128
+ assert.equal(plugin.__test.watchdogTick({ view: detached, state: detachedState, options: OPTIONS, now: 20000, visible: true }), 'no-stream')
129
+ })
130
+
131
+ // Given 视图已断开重连中, When 检查, Then 只报未连接, 不叠加重连
132
+ test('看门狗_未连接_不触发', () => {
133
+ const plugin = loadPlugin()
134
+ const view = fakeView({ writable: true, phase: 'connecting' })
135
+ const state = stateFor(view)
136
+ plugin.__test.watchdogTick({ view, state, options: OPTIONS, now: 1000, visible: true })
137
+ view.snapshot.writable = false
138
+ assert.equal(plugin.__test.watchdogTick({ view, state, options: OPTIONS, now: 20000, visible: true }), 'not-connected')
139
+ })
140
+
141
+ // Given 新视图由官方服务创建, When 看门狗安装后推进到停滞阈值, Then 自动调用 connect 恢复输出
142
+ test('看门狗_新建视图_自动重连', () => {
143
+ const plugin = loadPlugin()
144
+ const timers = fakeTimers()
145
+ const holder = { view() { return this.created }, created: fakeView({ writable: true }), views: new Map() }
146
+ const result = plugin.__test.installTerminalWatchdog({ holder, options: OPTIONS, timers, isVisible: () => true })
147
+ assert.equal(result.installed, true)
148
+ const view = holder.view('session-a', 'tab-1', 'content-1')
149
+ timers.advance(2000)
150
+ view.snapshot.writable = false
151
+ timers.advance(8000)
152
+ assert.equal(view.connects, 1)
153
+ })
154
+
155
+ // Given 已存在的视图, When 安装时遍历 views, Then 同样进入监控
156
+ test('看门狗_既有视图_进入监控', () => {
157
+ const plugin = loadPlugin()
158
+ const timers = fakeTimers()
159
+ const existing = fakeView({ writable: true })
160
+ const holder = { view: () => existing, views: new Map([['session-a', new Map([['tab-1', existing]])]]) }
161
+ plugin.__test.installTerminalWatchdog({ holder, options: OPTIONS, timers, isVisible: () => true })
162
+ timers.advance(2000)
163
+ existing.snapshot.writable = false
164
+ timers.advance(8000)
165
+ assert.equal(existing.connects, 1)
166
+ })
167
+
168
+ // Given 宿主版本没有 webTerminals.view, When 安装, Then 跳过并不抛错
169
+ test('看门狗_服务结构缺失_跳过不抛', () => {
170
+ const plugin = loadPlugin()
171
+ const missing = plugin.__test.installTerminalWatchdog({ holder: {}, options: OPTIONS, timers: fakeTimers(), isVisible: () => true })
172
+ assert.equal(missing.installed, false)
173
+ const alsoMissing = plugin.__test.installTerminalWatchdog({ holder: undefined, options: OPTIONS, timers: fakeTimers(), isVisible: () => true })
174
+ assert.equal(alsoMissing.installed, false)
175
+ })
176
+
177
+ // Given 已安装, When 再次安装, Then 幂等不重复挂定时器
178
+ test('看门狗_重复安装_幂等', () => {
179
+ const plugin = loadPlugin()
180
+ const timers = fakeTimers()
181
+ const holder = { view: () => fakeView({ writable: true }), views: new Map() }
182
+ plugin.__test.installTerminalWatchdog({ holder, options: OPTIONS, timers, isVisible: () => true })
183
+ const again = plugin.__test.installTerminalWatchdog({ holder, options: OPTIONS, timers, isVisible: () => true })
184
+ assert.equal(again.installed, false)
185
+ assert.equal(timers.intervalCount() <= 1, true)
186
+ })
@@ -0,0 +1,145 @@
1
+ import test from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { DEFAULT_CONFIG, normalizeConfig } from '../src/config.mjs'
4
+
5
+ // Given 无配置, When 归一化, Then 各补丁默认开启(诊断上报默认关闭)且阈值取默认
6
+ test('配置_缺省_功能默认值与阈值', () => {
7
+ const config = normalizeConfig(undefined)
8
+ assert.deepEqual(config, DEFAULT_CONFIG)
9
+ assert.equal(config.restartCoalesce.enabled, true)
10
+ assert.equal(config.restartCoalesce.windowMs, 100)
11
+ assert.equal(config.terminalWatchdog.enabled, true)
12
+ assert.equal(config.terminalWatchdog.checkMs, 2000)
13
+ assert.equal(config.terminalWatchdog.stallMs, 8000)
14
+ assert.equal(config.terminalWatchdog.cooldownMs, 60000)
15
+ })
16
+
17
+ // Given 只覆盖一个字段, When 归一化, Then 其余字段保持默认
18
+ test('配置_部分覆盖_其余保持默认', () => {
19
+ const config = normalizeConfig({ restartCoalesce: { windowMs: 250 } })
20
+ assert.equal(config.restartCoalesce.windowMs, 250)
21
+ assert.equal(config.restartCoalesce.enabled, true)
22
+ assert.equal(config.terminalWatchdog.stallMs, 8000)
23
+ })
24
+
25
+ // Given 功能关闭, When 归一化, Then 关闭语义原样保留而阈值仍可取
26
+ test('配置_功能关闭_保留阈值', () => {
27
+ const config = normalizeConfig({ terminalWatchdog: { enabled: false, stallMs: 4000 } })
28
+ assert.equal(config.terminalWatchdog.enabled, false)
29
+ assert.equal(config.terminalWatchdog.stallMs, 4000)
30
+ })
31
+
32
+ // Given 非法类型与越界值, When 归一化, Then 回退默认而不是把坏值带进运行时
33
+ test('配置_非法值_回退默认', () => {
34
+ const config = normalizeConfig({
35
+ restartCoalesce: { enabled: 'yes', windowMs: -1 },
36
+ terminalWatchdog: { checkMs: 0, stallMs: 'soon', cooldownMs: Number.NaN },
37
+ })
38
+ assert.equal(config.restartCoalesce.enabled, true)
39
+ assert.equal(config.restartCoalesce.windowMs, 100)
40
+ assert.equal(config.terminalWatchdog.checkMs, 2000)
41
+ assert.equal(config.terminalWatchdog.stallMs, 8000)
42
+ assert.equal(config.terminalWatchdog.cooldownMs, 60000)
43
+ })
44
+
45
+ // Given 超上限的阈值, When 归一化, Then 收敛到上限(防止错误配置把功能变成滥用)
46
+ test('配置_越界上限_收敛', () => {
47
+ const config = normalizeConfig({
48
+ restartCoalesce: { windowMs: 60000 },
49
+ terminalWatchdog: { checkMs: 600000, stallMs: 999999999, cooldownMs: 999999999 },
50
+ })
51
+ assert.equal(config.restartCoalesce.windowMs, 2000)
52
+ assert.equal(config.terminalWatchdog.checkMs, 60000)
53
+ assert.equal(config.terminalWatchdog.stallMs, 600000)
54
+ assert.equal(config.terminalWatchdog.cooldownMs, 3600000)
55
+ })
56
+
57
+ // Given 未知字段, When 归一化, Then 被丢弃(配置面只认已声明字段)
58
+ test('配置_未知字段_丢弃', () => {
59
+ const config = normalizeConfig({ unknown: 1, restartCoalesce: { windowMs: 150 } })
60
+ assert.equal(config.unknown, undefined)
61
+ assert.equal(config.restartCoalesce.windowMs, 150)
62
+ })
63
+
64
+ // Given 无配置, When 归一化, Then 保活与停滞兜底默认开启且阈值取默认
65
+ test('配置_保活与停滞_默认值', () => {
66
+ const config = normalizeConfig(undefined)
67
+ assert.deepEqual(config.socketKeepAlive, { enabled: true, intervalMs: 1500 })
68
+ assert.deepEqual(config.stuckSession, {
69
+ enabled: true,
70
+ checkMs: 2000,
71
+ stallMs: 8000,
72
+ reconnect: true,
73
+ maxReconnectAttempts: 5,
74
+ reconnectIntervalMs: 15000,
75
+ })
76
+ assert.deepEqual(config.jobStreamLimit, { enabled: true, base: 32, max: 48 })
77
+ })
78
+
79
+ // Given 新增补丁的非法值, When 归一化, Then 回退默认而不是把坏值带进运行时
80
+ test('配置_保活与停滞_非法值回退', () => {
81
+ const config = normalizeConfig({
82
+ socketKeepAlive: { enabled: 'on', intervalMs: 0 },
83
+ stuckSession: { checkMs: -5, stallMs: 'slow', reconnect: 1, maxReconnectAttempts: Number.NaN, reconnectIntervalMs: 0 },
84
+ })
85
+ assert.equal(config.socketKeepAlive.enabled, true)
86
+ assert.equal(config.socketKeepAlive.intervalMs, 1500)
87
+ assert.equal(config.stuckSession.checkMs, 2000)
88
+ assert.equal(config.stuckSession.stallMs, 8000)
89
+ assert.equal(config.stuckSession.reconnect, true)
90
+ assert.equal(config.stuckSession.maxReconnectAttempts, 5)
91
+ assert.equal(config.stuckSession.reconnectIntervalMs, 15000)
92
+ })
93
+
94
+ // Given 超上限的阈值, When 归一化, Then 收敛到上限
95
+ test('配置_保活与停滞_越界收敛', () => {
96
+ const config = normalizeConfig({
97
+ socketKeepAlive: { intervalMs: 999999 },
98
+ stuckSession: { checkMs: 999999, stallMs: 999999999, maxReconnectAttempts: 999999, reconnectIntervalMs: 999999999 },
99
+ })
100
+ assert.equal(config.socketKeepAlive.intervalMs, 60000)
101
+ assert.equal(config.stuckSession.checkMs, 60000)
102
+ assert.equal(config.stuckSession.stallMs, 600000)
103
+ assert.equal(config.stuckSession.maxReconnectAttempts, 20)
104
+ assert.equal(config.stuckSession.reconnectIntervalMs, 600000)
105
+ })
106
+
107
+ // Given 任务流上限被关掉或给了非法值, When 归一化, Then 关闭生效、非法值回退默认
108
+ test('配置_任务流上限_开关与回退', () => {
109
+ const off = normalizeConfig({ jobStreamLimit: { enabled: false } })
110
+ assert.equal(off.jobStreamLimit.enabled, false)
111
+ const bad = normalizeConfig({ jobStreamLimit: { base: 0, max: 0 } })
112
+ assert.equal(bad.jobStreamLimit.base, 32)
113
+ assert.equal(bad.jobStreamLimit.max, 48)
114
+ const capped = normalizeConfig({ jobStreamLimit: { max: 99999 } })
115
+ assert.equal(capped.jobStreamLimit.max, 512)
116
+ const inverted = normalizeConfig({ jobStreamLimit: { base: 200, max: 10 } })
117
+ assert.equal(inverted.jobStreamLimit.base, 10)
118
+ })
119
+
120
+ // Given 列表瘦身缺省或关闭, When 归一化, Then 默认开启、显式关闭保留、非法类型回退默认
121
+ test('配置_列表瘦身_开关与回退', () => {
122
+ assert.deepEqual(normalizeConfig(undefined).listSlim, { enabled: true })
123
+ assert.equal(normalizeConfig({ listSlim: { enabled: false } }).listSlim.enabled, false)
124
+ assert.equal(normalizeConfig({ listSlim: { enabled: 'off' } }).listSlim.enabled, true)
125
+ })
126
+
127
+ // Given 诊断上报缺省, When 归一化, Then 默认关闭且上报间隔取默认
128
+ test('配置_诊断上报_默认关闭', () => {
129
+ assert.deepEqual(normalizeConfig(undefined).diag, { enabled: false, reportEvery: 12 })
130
+ })
131
+
132
+ // Given 诊断上报显式开关与非法值, When 归一化, Then 开关保留、非法类型回退、间隔越界收敛、未知字段丢弃
133
+ test('配置_诊断上报_开关边界', () => {
134
+ const off = normalizeConfig({ diag: { enabled: false, reportEvery: 5 } })
135
+ assert.equal(off.diag.enabled, false)
136
+ assert.equal(off.diag.reportEvery, 5)
137
+ const on = normalizeConfig({ diag: { enabled: true } })
138
+ assert.equal(on.diag.enabled, true)
139
+ const bad = normalizeConfig({ diag: { enabled: 'yes', reportEvery: 'often' } })
140
+ assert.equal(bad.diag.enabled, false)
141
+ assert.equal(bad.diag.reportEvery, 12)
142
+ assert.equal(normalizeConfig({ diag: { reportEvery: 0 } }).diag.reportEvery, 1)
143
+ assert.equal(normalizeConfig({ diag: { reportEvery: 99999 } }).diag.reportEvery, 720)
144
+ assert.deepEqual(normalizeConfig({ diag: { reportEvery: 3, unknown: 1 } }).diag, { enabled: false, reportEvery: 3 })
145
+ })
@@ -0,0 +1,340 @@
1
+ import test from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { DEFAULT_CONFIG } from '../src/config.mjs'
4
+ import { apply, Config, name } from '../src/index.js'
5
+
6
+ function makeRes() {
7
+ return {
8
+ status: undefined,
9
+ headers: undefined,
10
+ body: undefined,
11
+ writeHead(status, headers) { this.status = status; this.headers = headers },
12
+ end(body) { this.body = body },
13
+ }
14
+ }
15
+
16
+ function makeCtx({ server, registerError, sessionController, settings, configEditor, fiberEntry } = {}) {
17
+ const registrations = []
18
+ const disposed = []
19
+ const ctx = {
20
+ fiber: { entry: fiberEntry },
21
+ get: (service) => {
22
+ if (service === 'webServer') return server
23
+ if (service === 'sessionController') return sessionController
24
+ if (service === 'settings') return settings
25
+ if (service === 'configEditor') return configEditor
26
+ return undefined
27
+ },
28
+ effect: (factory) => {
29
+ const dispose = factory()
30
+ ctx.effects.push(dispose)
31
+ return dispose
32
+ },
33
+ inject: (services, callback) => {
34
+ const scope = {
35
+ get: ctx.get,
36
+ get sessionController() { return sessionController },
37
+ get settings() { return settings },
38
+ on: () => {},
39
+ effect: (factory) => {
40
+ ctx.effects.push(factory())
41
+ },
42
+ }
43
+ if (services.includes('sessionController') && sessionController !== undefined) callback(scope)
44
+ if (services.includes('settings') && settings !== undefined) callback(scope)
45
+ },
46
+ effects: [],
47
+ registrations,
48
+ disposed,
49
+ }
50
+ if (server !== undefined) {
51
+ server.register = (route) => {
52
+ if (registerError !== undefined) throw registerError
53
+ registrations.push(route)
54
+ return () => disposed.push(route.path)
55
+ }
56
+ }
57
+ return ctx
58
+ }
59
+
60
+ async function request(route, config) {
61
+ const res = makeRes()
62
+ await route.handler({ url: '/api/dsh-bug-perf/config', method: 'GET' }, res)
63
+ return res
64
+ }
65
+
66
+ async function requestDiag(route, query) {
67
+ const res = makeRes()
68
+ await route.handler({ url: `/api/dsh-bug-perf/diag${query}`, method: 'GET' }, res)
69
+ return res
70
+ }
71
+
72
+ // Given 插件声明注入, When 装载器解析, Then 等待 webServer 就绪后再激活(不是同步探测)
73
+ test('宿主_注入声明_等待webServer', async () => {
74
+ const index = await import('../src/index.js')
75
+ assert.deepEqual(index.inject, ['webServer'])
76
+ })
77
+
78
+ // Given 宿主提供 webServer, When 插件激活, Then 注册配置读写路由且路径固定
79
+ test('宿主_激活_注册配置路由', () => {
80
+ const ctx = makeCtx({ server: {} })
81
+ apply(ctx, undefined)
82
+ assert.equal(name, 'dsh-bug-perf')
83
+ assert.equal(ctx.registrations.length, 2)
84
+ assert.equal(ctx.registrations[0].kind, 'exact')
85
+ assert.equal(ctx.registrations[0].path, '/api/dsh-bug-perf/config')
86
+ assert.equal(ctx.registrations[1].path, '/api/dsh-bug-perf/diag')
87
+ })
88
+
89
+ // Given 行配置为缺省, When 客户端读取配置, Then 返回默认配置 JSON
90
+ test('宿主_缺省配置_返回默认值', async () => {
91
+ const ctx = makeCtx({ server: {} })
92
+ apply(ctx, undefined)
93
+ const res = await request(ctx.registrations[0])
94
+ assert.equal(res.status, 200)
95
+ const json = JSON.parse(res.body)
96
+ assert.deepEqual(json.config, DEFAULT_CONFIG)
97
+ })
98
+
99
+ // Given 行配置部分覆盖, When 客户端读取, Then 覆盖项生效且其余为默认
100
+ test('宿主_部分覆盖_返回归一化结果', async () => {
101
+ const ctx = makeCtx({ server: {} })
102
+ apply(ctx, { restartCoalesce: { enabled: false }, terminalWatchdog: { stallMs: 12000 } })
103
+ const json = JSON.parse((await request(ctx.registrations[0])).body)
104
+ assert.equal(json.config.restartCoalesce.enabled, false)
105
+ assert.equal(json.config.restartCoalesce.windowMs, 100)
106
+ assert.equal(json.config.terminalWatchdog.stallMs, 12000)
107
+ })
108
+
109
+ async function postConfig(route, body) {
110
+ const res = makeRes()
111
+ const payload = typeof body === 'string' ? body : JSON.stringify(body)
112
+ const req = {
113
+ url: '/api/dsh-bug-perf/config',
114
+ method: 'POST',
115
+ on(event, callback) {
116
+ if (event === 'data') callback(Buffer.from(payload))
117
+ else if (event === 'end') callback()
118
+ },
119
+ destroy() {},
120
+ }
121
+ await route.handler(req, res)
122
+ return res
123
+ }
124
+
125
+ // Given 旧宿主 settings 命名空间可用, When POST 配置, Then 归一化后持久化并可读回
126
+ test('宿主_配置保存_settings持久化', async () => {
127
+ const settings = {
128
+ stored: undefined,
129
+ registered: undefined,
130
+ register(namespace, schema, options) { this.registered = { namespace, schema, options } },
131
+ get() { return this.stored },
132
+ async update(_namespace, patch) { this.stored = { ...(this.stored ?? {}), ...patch } },
133
+ }
134
+ const ctx = makeCtx({ server: {}, settings })
135
+ apply(ctx, undefined)
136
+ assert.equal(settings.registered.namespace, 'bug-perf')
137
+ const res = await postConfig(ctx.registrations[0], { config: { listSlim: { enabled: false } } })
138
+ assert.equal(res.status, 200)
139
+ const saved = JSON.parse(res.body).config
140
+ assert.equal(saved.listSlim.enabled, false)
141
+ assert.equal(settings.stored.listSlim.enabled, false)
142
+ const read = JSON.parse((await request(ctx.registrations[0])).body)
143
+ assert.equal(read.config.listSlim.enabled, false)
144
+ })
145
+
146
+ // Given legacy settings 只存 diag, When 读取配置, Then diag 节也参与已存判断
147
+ test('宿主_配置读取_settings仅diag_生效', async () => {
148
+ const settings = {
149
+ stored: { diag: { enabled: true, reportEvery: 2 } },
150
+ register() {},
151
+ get() { return this.stored },
152
+ async update() {},
153
+ }
154
+ const ctx = makeCtx({ server: {}, settings })
155
+ apply(ctx, undefined)
156
+ const read = JSON.parse((await request(ctx.registrations[0])).body)
157
+ assert.deepEqual(read.config.diag, { enabled: true, reportEvery: 2 })
158
+ })
159
+
160
+ // Given 保存值越界, When POST, Then 上限收敛后再落盘
161
+ test('宿主_配置保存_数值收敛', async () => {
162
+ const settings = {
163
+ stored: undefined,
164
+ register() {},
165
+ get() { return this.stored },
166
+ async update(_namespace, patch) { this.stored = patch },
167
+ }
168
+ const ctx = makeCtx({ server: {}, settings })
169
+ apply(ctx, undefined)
170
+ const res = await postConfig(ctx.registrations[0], { config: { terminalWatchdog: { stallMs: 99999999 } } })
171
+ assert.equal(JSON.parse(res.body).config.terminalWatchdog.stallMs, 600000)
172
+ assert.equal(settings.stored.terminalWatchdog.stallMs, 600000)
173
+ })
174
+
175
+ // Given 0.1.7 行配置编辑器可用, When POST, Then 通过 editor.edit 写当前 fiber 条目
176
+ test('宿主_配置保存_行配置编辑器', async () => {
177
+ const edits = []
178
+ const configEditor = {
179
+ async edit(entry, update) {
180
+ const next = update({ restartCoalesce: { enabled: true, windowMs: 100 } })
181
+ edits.push({ entry, next })
182
+ },
183
+ }
184
+ const entry = { id: 'dsh-bug-perf' }
185
+ const ctx = makeCtx({ server: {}, configEditor, fiberEntry: entry })
186
+ apply(ctx, undefined)
187
+ const res = await postConfig(ctx.registrations[0], { config: { restartCoalesce: { windowMs: 250 } } })
188
+ assert.equal(res.status, 200)
189
+ assert.equal(edits.length, 1)
190
+ assert.equal(edits[0].entry, entry)
191
+ assert.equal(edits[0].next.restartCoalesce.windowMs, 250)
192
+ })
193
+
194
+ // Given 坏 JSON 或非法 config, When POST, Then 400 且不写
195
+ test('宿主_配置保存_拒绝坏载荷', async () => {
196
+ let updates = 0
197
+ const settings = {
198
+ register() {},
199
+ get() { return undefined },
200
+ async update() { updates += 1 },
201
+ }
202
+ const ctx = makeCtx({ server: {}, settings })
203
+ apply(ctx, undefined)
204
+ assert.equal((await postConfig(ctx.registrations[0], 'not-json')).status, 400)
205
+ assert.equal((await postConfig(ctx.registrations[0], { config: null })).status, 400)
206
+ assert.equal((await postConfig(ctx.registrations[0], { config: [] })).status, 400)
207
+ assert.equal(updates, 0)
208
+ })
209
+
210
+ // Given 宿主没有 settings 与 configEditor, When POST, Then 503 且不抛
211
+ test('宿主_配置保存_服务缺失返回503', async () => {
212
+ const ctx = makeCtx({ server: {} })
213
+ apply(ctx, undefined)
214
+ const res = await postConfig(ctx.registrations[0], { config: { listSlim: { enabled: false } } })
215
+ assert.equal(res.status, 503)
216
+ })
217
+
218
+ // Given 非 GET/POST 方法, When 请求配置路由, Then 405 且不读不写
219
+ test('宿主_配置路由_拒绝未知方法', async () => {
220
+ const ctx = makeCtx({ server: {} })
221
+ apply(ctx, undefined)
222
+ const res = makeRes()
223
+ await ctx.registrations[0].handler({ url: '/api/dsh-bug-perf/config', method: 'DELETE' }, res)
224
+ assert.equal(res.status, 405)
225
+ })
226
+
227
+ // Given 0.1.7 宿主把整节配置包成 volatile ref, When 读取, Then 解箱后返回
228
+ test('宿主_volatile配置_解箱', async () => {
229
+ const ctx = makeCtx({ server: {} })
230
+ apply(ctx, { get: () => ({ terminalWatchdog: { enabled: false } }) })
231
+ const json = JSON.parse((await request(ctx.registrations[0])).body)
232
+ assert.equal(json.config.terminalWatchdog.enabled, false)
233
+ })
234
+
235
+ // Given 宿主没有 webServer 服务, When 插件激活, Then 不注册不抛错(旧宿主/无 web 场景)
236
+ test('宿主_无webServer_干净跳过', () => {
237
+ const ctx = makeCtx()
238
+ assert.doesNotThrow(() => apply(ctx, undefined))
239
+ assert.equal(ctx.registrations.length, 0)
240
+ assert.equal(ctx.effects.length, 0)
241
+ })
242
+
243
+ // Given 路由注册抛错, When 插件激活, Then 错误被记录而不是拖垮页面
244
+ test('宿主_注册失败_不抛', () => {
245
+ const ctx = makeCtx({ server: {}, registerError: new Error('route table frozen') })
246
+ assert.doesNotThrow(() => apply(ctx, undefined))
247
+ assert.equal(ctx.registrations.length, 0)
248
+ })
249
+
250
+ // Given 插件导出 Config, When 取各节默认值, Then 与共享默认值一致(设置页与运行时同源)
251
+ test('宿主_Config默认值_与共享默认一致', () => {
252
+ const restart = Config.dict.restartCoalesce({})
253
+ const watchdog = Config.dict.terminalWatchdog({})
254
+ assert.equal(restart.enabled, DEFAULT_CONFIG.restartCoalesce.enabled)
255
+ assert.equal(restart.windowMs, DEFAULT_CONFIG.restartCoalesce.windowMs)
256
+ assert.equal(watchdog.enabled, DEFAULT_CONFIG.terminalWatchdog.enabled)
257
+ assert.equal(watchdog.checkMs, DEFAULT_CONFIG.terminalWatchdog.checkMs)
258
+ assert.equal(watchdog.stallMs, DEFAULT_CONFIG.terminalWatchdog.stallMs)
259
+ assert.equal(watchdog.cooldownMs, DEFAULT_CONFIG.terminalWatchdog.cooldownMs)
260
+ assert.deepEqual(Config.dict.listSlim({}), DEFAULT_CONFIG.listSlim)
261
+ assert.deepEqual(Config.dict.diag({}), DEFAULT_CONFIG.diag)
262
+ assert.equal(Config.dict.diag({}).enabled, false)
263
+ })
264
+
265
+ // Given sessionController 就绪, When 插件激活并调用列表接口, Then 投影按白名单裁剪且第二次命中缓存
266
+ test('宿主_列表瘦身_按配置接管列表接口', async () => {
267
+ let built = 0
268
+ const controller = {
269
+ list: async () => {
270
+ built += 1
271
+ return { items: [{ sessionId: 'a', projections: { kind: 'cached', values: { title: 'A', contextHeaders: [1] } } }] }
272
+ },
273
+ }
274
+ const ctx = makeCtx({ server: {}, sessionController: controller })
275
+ apply(ctx, undefined)
276
+ const first = await controller.list({}, undefined)
277
+ const second = await controller.list({}, undefined)
278
+ assert.equal(built, 1)
279
+ assert.deepEqual(Object.keys(first.items[0].projections.values), ['title'])
280
+ assert.equal(second, first)
281
+ })
282
+
283
+ // Given 行配置关闭列表瘦身, When 调用列表接口, Then 原样透传且不缓存
284
+ test('宿主_列表瘦身_关闭时原样透传', async () => {
285
+ let built = 0
286
+ const controller = {
287
+ list: async () => {
288
+ built += 1
289
+ return { items: [{ sessionId: 'a', projections: { values: { contextHeaders: [1] } } }] }
290
+ },
291
+ }
292
+ const ctx = makeCtx({ server: {}, sessionController: controller })
293
+ apply(ctx, { listSlim: { enabled: false } })
294
+ await controller.list({}, undefined)
295
+ const value = await controller.list({}, undefined)
296
+ assert.equal(built, 2)
297
+ assert.deepEqual(value.items[0].projections.values, { contextHeaders: [1] })
298
+ })
299
+
300
+ // Given 0.1.7 live ref 热更新行配置, When 连续切换 listSlim 开关, Then 每次调用按当前值决定裁剪或透传
301
+ test('宿主_列表瘦身_热切换开关即时生效', async () => {
302
+ let current = { listSlim: { enabled: false } }
303
+ let built = 0
304
+ const controller = {
305
+ list: async () => {
306
+ built += 1
307
+ return { items: [{ sessionId: 'a', projections: { values: { title: 'A', contextHeaders: [1] } } }] }
308
+ },
309
+ }
310
+ const ctx = makeCtx({ server: {}, sessionController: controller })
311
+ apply(ctx, { get: () => current })
312
+ const off = await controller.list({}, undefined)
313
+ assert.deepEqual(off.items[0].projections.values.contextHeaders, [1], '关闭时透传完整投影')
314
+ current = { listSlim: { enabled: true } }
315
+ const on = await controller.list({}, undefined)
316
+ assert.deepEqual(Object.keys(on.items[0].projections.values), ['title'], '开启后裁剪')
317
+ current = { listSlim: { enabled: false } }
318
+ const offAgain = await controller.list({}, undefined)
319
+ assert.deepEqual(offAgain.items[0].projections.values.contextHeaders, [1], '再次关闭恢复完整投影')
320
+ assert.equal(built, 3)
321
+ })
322
+
323
+ // Given diag 开关默认关闭, When 浏览器仍上报合法载荷, Then 宿主不按开关拒绝, 返回 ok 并落盘
324
+ test('宿主_采样上报_接受合法载荷', async () => {
325
+ const ctx = makeCtx({ server: {} })
326
+ apply(ctx, undefined)
327
+ const res = await requestDiag(ctx.registrations[1], `?d=${encodeURIComponent(JSON.stringify({ at: 1, totals: { opened: 1 } }))}`)
328
+ assert.equal(res.status, 200)
329
+ assert.deepEqual(JSON.parse(res.body), { ok: true })
330
+ })
331
+
332
+ // Given 缺少载荷或载荷不是 JSON, When 上报, Then 返回 400 且不抛出
333
+ test('宿主_采样上报_拒绝坏载荷', async () => {
334
+ const ctx = makeCtx({ server: {} })
335
+ apply(ctx, undefined)
336
+ const missing = await requestDiag(ctx.registrations[1], '')
337
+ assert.equal(missing.status, 400)
338
+ const broken = await requestDiag(ctx.registrations[1], '?d=not-json')
339
+ assert.equal(broken.status, 400)
340
+ })