#!/usr/bin/env node /** * 手机接入线 S5 · **运维急停**夹具(独立进程,2026-09-27)。 * * ## 为什么必须是独立进程 * 路由侧读的是 `process.env.DSHS_DEVICE_WEB`,而 ESM 模块**只求值一次** —— * 在同一个进程里改 `process.env` 之后再 import,改动拿不到(实测假绿过一次)。 * 独立进程 + 环境变量与生产 systemd 的形态**完全同构**(e2e 脚本就是往 drop-in 里写这个键)。 * * ## 判据(两条,缺一条就不算通过) * ① `DSHS_DEVICE_WEB=0` 且**用户开关是开的** ⇒ 设备入口必须被拒,且码 = `switch-off` * (⛔ 不是 `user-switch-off` —— 糊了就分不清"该运维动"还是"该用户动") * ② 同一份夹具在**未设**该键时必须**不通过**(由调用方断言)⇒ 证明本脚本真的读了 env, * 而不是恒真。 * * ⚠️ **HTTP 状态 = 404**(既有 S3 口径:`switch-off` 本来就走 404,本棒⛔ 没动它): * 判据在**响应体里的具名码**,不在状态码 —— 这点必须写清,否则下一个人会以为 404 是写漏了。 * * 用法:`DSHS_DEVICE_WEB=0 node test/fixtures/device-switch-forceoff.mjs` * 退出码:0 = 急停生效(期望);1 = 没生效(不合期望)。 */ import Fastify from 'fastify' import { deviceWebRoutes } from '../../lib/web/routes/device-web.js' import { hashSessionToken } from '../../lib/web/auth.js' import { indexRelayStatus, resolveDeviceReachability } from '../../lib/net/relay-endpoints.js' const USER = 'u-1' const NET = `u:${USER}` const HOST = 'd-u-1-aaaaaaaa' const SID = 'sid-token' const COOKIE_HASH = hashSessionToken(SID) const app = Fastify() app.decorate('db', { findSessionWithUser: async (hash) => hash === COOKIE_HASH ? { expiresAt: Date.now() + 60_000, user: { id: USER, role: 'active' } } : undefined, findOverlayDevice: async () => ({ status: 'active', kind: 'desktop' }), // 🔴 用户开关**开着** —— 这正是"急停要压过用户开关"的判据面 getDeviceWebEnabled: async () => true, }) app.decorate('imInstanceTokens', undefined) await app.register(deviceWebRoutes, { resolve: ({ hostId }) => resolveDeviceReachability( indexRelayStatus({ endpoints: [{ hostId, network: NET, port: 20090, localPort: 42000, online: true }] }), { network: NET, hostId }, ), }) await app.ready() const res = await app.inject({ method: 'GET', url: `/u/${USER}/desk/${HOST}/`, headers: { cookie: `sid=${SID}` } }) await app.close() const body = res.payload === '' ? {} : JSON.parse(res.payload) if (res.statusCode === 404 && body.error === 'switch-off') { console.log(`FORCE-OFF-OK status=404 error=switch-off env=${JSON.stringify(process.env.DSHS_DEVICE_WEB)}`) process.exit(0) } console.error(`FORCE-OFF-FAILED status=${res.statusCode} body=${res.payload}`) process.exit(1)