Files
nodeagent/tests/unit/net-scripts.test.mjs
T
jkooe e955b6a2f7 test(v16): 网络脚本回归断言 8 条(把事故根因变成防线)
针对本次真机事故(机器掉 APIPA 失联)逐条设防:
- apply 必须先 New-NetIPAddress 再 Remove-NetIPAddress(顺序颠倒 → already exists → 无地址)
- 建默认路由前必须先 Remove-NetRoute(否则 New-NetRoute 报实例已存在)
- 断言脚本里**不得出现** netsh set address / set dnsservers(Win11 上会挂起)
- 自检片段必须校验目标地址并在失败时执行回滚(自愈)
- 回滚脚本同样受「先加后清 / 先清路由」约束,且必须恢复原地址/网关/DNS
- DHCP 快照的回滚走 -Dhcp Enabled,不得写静态地址
- 掩码→前缀换算边界(含非法输入回退)

单测 116 -> 124
2026-09-30 10:20:02 +08:00

92 lines
4.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import {
buildApplyCommand,
buildRollbackScript,
buildSelfCheck,
maskToPrefix,
} from '../../apps/agent/dist/capabilities/network.js';
const snap = {
alias: 'Ethernet',
dhcp: false,
ipv4: [{ ip: '192.168.1.100', prefix: 24 }],
gateway: ['192.168.0.1'],
dns: ['223.5.5.5'],
};
const args = { mode: 'static', ip: '192.168.1.100', mask: '255.255.255.0', gateway: '192.168.0.1' };
test('掩码换算:常见掩码与前缀长度互转', () => {
assert.equal(maskToPrefix('255.255.255.0'), 24);
assert.equal(maskToPrefix('255.255.0.0'), 16);
assert.equal(maskToPrefix('255.255.255.192'), 26);
assert.equal(maskToPrefix('255.255.255.255'), 32);
assert.equal(maskToPrefix('乱码'), 24, '非法输入回退 24');
});
/**
* 真机事故回归:曾经「先删旧地址 → 再加新地址」,Windows 残留的默认路由让
* New-NetIPAddress 报 already exists → 新地址没加上 → 机器掉到 APIPA 彻底失联。
* 下面几条断言就是那道防线。
*/
test('apply(static):必须先加新地址、再清旧地址(顺序不可颠倒)', () => {
const script = buildApplyCommand('static', 'Ethernet', args, snap);
const iAdd = script.indexOf('New-NetIPAddress');
const iRemove = script.indexOf('Remove-NetIPAddress');
assert.ok(iAdd > 0 && iRemove > 0, '既要加也要清');
assert.ok(iAdd < iRemove, '必须先 New-NetIPAddress 后 Remove-NetIPAddress(事故根因)');
});
test('apply(static):建路由之前必须先清掉旧默认路由(否则 already exists)', () => {
const script = buildApplyCommand('static', 'Ethernet', args, snap);
const iRmRoute = script.indexOf('Remove-NetRoute');
const iAddRoute = script.indexOf('New-NetRoute');
assert.ok(iRmRoute > 0, '必须显式清理默认路由');
assert.ok(iAddRoute > 0, '必须重建默认路由');
assert.ok(iRmRoute < iAddRoute, 'Remove-NetRoute 必须在 New-NetRoute 之前');
});
test('apply:不得使用会在 Win11 挂起的 netsh set address', () => {
const staticScript = buildApplyCommand('static', 'Ethernet', args, snap);
const dhcpScript = buildApplyCommand('dhcp', 'Ethernet', { mode: 'dhcp' }, snap);
for (const s of [staticScript, dhcpScript]) {
assert.ok(!/netsh interface ipv4 set address/.test(s), 'netsh set address 在 Win11 会挂起(真机实测)');
assert.ok(!/netsh interface ipv4 set dnsservers/.test(s), 'netsh set dnsservers 同类问题');
}
assert.match(staticScript, /Set-NetIPInterface|New-NetIPAddress/, '应使用原生 cmdlet');
});
test('apply(dhcp):切 DHCP 用原生 cmdlet', () => {
const s = buildApplyCommand('dhcp', 'Ethernet', { mode: 'dhcp' }, snap);
assert.match(s, /Set-NetIPInterface -InterfaceAlias "Ethernet" -Dhcp Enabled/);
assert.match(s, /Set-DnsClientServerAddress/);
});
test('自检:失败时自动执行回滚脚本(自愈)', () => {
const chk = buildSelfCheck('Ethernet', '192.168.1.100', 'C:/rb.ps1');
assert.match(chk, /Get-NetIPAddress/);
assert.match(chk, /192\.168\.0\.88/, '应校验目标地址是否真的生效');
assert.match(chk, /selfcheck FAILED -> auto rollback/);
assert.match(chk, /& "C:\/rb\.ps1"/, '失败必须回滚,而不是把机器留在无地址状态');
});
test('rollback:同样先加后清 + 先清路由(否则回滚自己也会失败)', () => {
const rb = buildRollbackScript(snap, 'C:/backup.txt', 'C:/rb.log');
const iAdd = rb.indexOf('New-NetIPAddress');
const iRemove = rb.indexOf('Remove-NetIPAddress');
const iRmRoute = rb.indexOf('Remove-NetRoute');
const iAddRoute = rb.indexOf('New-NetRoute');
assert.ok(iAdd < iRemove, '回滚也必须先加后清');
assert.ok(iRmRoute < iAddRoute, '回滚也必须先清路由');
assert.match(rb, /192\.168\.0\.159/, '必须恢复快照里的原地址');
assert.match(rb, /192\.168\.0\.1/, '必须恢复原网关');
assert.match(rb, /223\.5\.5\.5/, '必须恢复原 DNS');
assert.ok(!/netsh -f/.test(rb), '不得用 netsh -f 回滚(备份编码 + set address 双重问题)');
});
test('rollback:DHCP 快照走「切回 DHCP」而非设静态', () => {
const rb = buildRollbackScript({ ...snap, dhcp: true, ipv4: [] }, 'C:/backup.txt', 'C:/rb.log');
assert.match(rb, /-Dhcp Enabled/);
assert.ok(!/New-NetIPAddress/.test(rb), 'DHCP 快照不应写静态地址');
});