feat(client): 实现可编辑岛组件方案与连接更新状态反馈
This commit is contained in:
1 parent
35b252249d
commit
b990701cde
26 files changed
+2014
-275
No files matched your search
@@ -0,0 +1,168 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
import { considerUpdate, getUpdateProgress, syncUpdatePolicy } from './auto-update.js';
|
||||
import { createServer } from 'node:http';
|
||||
import { app } from 'electron';
|
||||
import { getConfig, saveConfig } from './config.js';
|
||||
import { beatNow, getClientAgentStatus, stopClientAgent, startClientAgent } from './client-agent.js';
|
||||
export async function verifyAgentSmoke(
|
||||
check: (name: string, action: () => Promise<boolean | string> | boolean) => Promise<void>,
|
||||
) {
|
||||
const original = getConfig(),
|
||||
previous = process.env.ELECTRON_SMOKE_CLIENT_AGENT;
|
||||
let firstCalls = 0,
|
||||
secondCalls = 0,
|
||||
acked = false,
|
||||
invalidToken = false;
|
||||
const one = createServer((req, res) => {
|
||||
firstCalls++;
|
||||
res.writeHead(401, { 'content-type': 'application/json' });
|
||||
res.end('{}');
|
||||
});
|
||||
const two = createServer((req, res) => {
|
||||
if (req.headers.authorization !== 'Bearer v14-local-fake-token') invalidToken = true;
|
||||
if (req.url === '/fake-installer') {
|
||||
res.setHeader('content-length', String(Buffer.byteLength('仅测试,不是可执行文件')));
|
||||
res.end('仅测试,不是可执行文件');
|
||||
return;
|
||||
}
|
||||
res.setHeader('content-type', 'application/json');
|
||||
if (req.url?.includes('/ack')) {
|
||||
let body = '';
|
||||
req.on('data', (c) => (body += c));
|
||||
req.on('end', () => {
|
||||
const value = JSON.parse(body);
|
||||
acked =
|
||||
value.deviceKey === original.deviceKey &&
|
||||
value.ok === true &&
|
||||
value.result.includes('冒烟假执行器');
|
||||
res.end(JSON.stringify({ success: true, data: {} }));
|
||||
});
|
||||
return;
|
||||
}
|
||||
secondCalls++;
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
data: {
|
||||
deviceId: 'local-smoke-device',
|
||||
serverTime: new Date().toISOString(),
|
||||
updateTargetVersion: null,
|
||||
updateMode: 'idle',
|
||||
update: null,
|
||||
command: acked
|
||||
? null
|
||||
: {
|
||||
id: `local-smoke-${app.getVersion()}-${Date.now()}`,
|
||||
deviceId: 'local-smoke-device',
|
||||
kind: 'lock',
|
||||
payload: '仅假执行',
|
||||
createdAt: new Date().toISOString(),
|
||||
expiresAt: new Date(Date.now() + 60000).toISOString(),
|
||||
ackedAt: null,
|
||||
result: '',
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
await Promise.all([
|
||||
new Promise<void>((r) => one.listen(0, '127.0.0.1', r)),
|
||||
new Promise<void>((r) => two.listen(0, '127.0.0.1', r)),
|
||||
]);
|
||||
const url = (server: typeof one) => `http://127.0.0.1:${(server.address() as { port: number }).port}`;
|
||||
try {
|
||||
process.env.ELECTRON_SMOKE_CLIENT_AGENT = '1';
|
||||
stopClientAgent();
|
||||
saveConfig({ serverUrl: url(one), token: 'v14-old-fake-token' });
|
||||
startClientAgent();
|
||||
await beatNow();
|
||||
await check(
|
||||
'401 后明确进入退避状态',
|
||||
() => firstCalls > 0 && getClientAgentStatus().lastError.includes('401'),
|
||||
);
|
||||
const begin = Date.now();
|
||||
saveConfig({ serverUrl: url(two) });
|
||||
await check(
|
||||
'换服务端清除旧令牌与退避,设备身份保持不变',
|
||||
() =>
|
||||
getConfig().token === null &&
|
||||
getConfig().deviceKey === original.deviceKey &&
|
||||
!getClientAgentStatus().lastError,
|
||||
);
|
||||
saveConfig({ token: 'v14-local-fake-token' });
|
||||
await beatNow();
|
||||
await check('新服务端登录后30秒内自动心跳', async () => {
|
||||
const deadline = Date.now() + 3000;
|
||||
while (secondCalls === 0 && Date.now() < deadline) await new Promise((r) => setTimeout(r, 30));
|
||||
return secondCalls > 0 && Date.now() - begin < 30000 && getClientAgentStatus().online && !invalidToken;
|
||||
});
|
||||
await check('收到远控立即假执行并以deviceKey回执(未调用系统命令)', async () => {
|
||||
const deadline = Date.now() + 3000;
|
||||
while (!acked && Date.now() < deadline) await new Promise((r) => setTimeout(r, 30));
|
||||
return acked;
|
||||
});
|
||||
await check('安装包哈希失败会显示失败并拒绝待安装记录', async () => {
|
||||
saveConfig({ autoUpdate: true });
|
||||
const info = {
|
||||
version: '99.0.0',
|
||||
fileName: 'smoke-update.exe',
|
||||
size: Buffer.byteLength('仅测试,不是可执行文件'),
|
||||
sha256: 'bad',
|
||||
notes: '仅本机假数据',
|
||||
url: '/fake-installer',
|
||||
};
|
||||
syncUpdatePolicy({
|
||||
deviceId: 'local-smoke-device',
|
||||
serverTime: new Date().toISOString(),
|
||||
command: null,
|
||||
update: null,
|
||||
updateTargetVersion: info.version,
|
||||
updateMode: 'idle',
|
||||
updateNotBefore: null,
|
||||
});
|
||||
await considerUpdate(info, info.version);
|
||||
return (
|
||||
getUpdateProgress().stage === 'failed' &&
|
||||
getUpdateProgress().error.includes('sha256') &&
|
||||
getConfig().pendingUpdateVersion !== info.version
|
||||
);
|
||||
});
|
||||
await check('下载字节与校验完成显示等待安装(冒烟禁止运行安装器)', async () => {
|
||||
const info = {
|
||||
version: '99.0.1',
|
||||
fileName: 'smoke-update.exe',
|
||||
size: Buffer.byteLength('仅测试,不是可执行文件'),
|
||||
sha256: createHash('sha256').update('仅测试,不是可执行文件').digest('hex'),
|
||||
notes: '仅本机假数据',
|
||||
url: '/fake-installer',
|
||||
};
|
||||
syncUpdatePolicy({
|
||||
deviceId: 'local-smoke-device',
|
||||
serverTime: new Date().toISOString(),
|
||||
command: null,
|
||||
update: null,
|
||||
updateTargetVersion: info.version,
|
||||
updateMode: 'idle',
|
||||
updateNotBefore: null,
|
||||
});
|
||||
await considerUpdate(info, info.version);
|
||||
const progress = getUpdateProgress();
|
||||
return (
|
||||
progress.stage === 'waiting' &&
|
||||
progress.percent === 100 &&
|
||||
progress.downloadedBytes === info.size &&
|
||||
getConfig().pendingUpdateVersion === info.version
|
||||
);
|
||||
});
|
||||
} finally {
|
||||
stopClientAgent();
|
||||
saveConfig(original);
|
||||
if (previous === undefined) delete process.env.ELECTRON_SMOKE_CLIENT_AGENT;
|
||||
else process.env.ELECTRON_SMOKE_CLIENT_AGENT = previous;
|
||||
startClientAgent();
|
||||
await Promise.all([
|
||||
new Promise<void>((r) => one.close(() => r())),
|
||||
new Promise<void>((r) => two.close(() => r())),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user