按 TODO.md 的优先级把待办全部落地为代码,详见 CHANGELOG 的 [1.7.0]。 - 「走不动」攻关:地面状态修正(syncGroundToPhysics / trustSetbackOnGround)、 出站包类型分布、备选执行器 manualMover(只借 A* 规划、自己按键执行) - 原点自救分两轮并统计成败;回城前评估「原地是否更差」并记录前后坐标 - 新增目标区块租约表(targetLease)避免多机器人撞同一块 - 定时消息支持 cron 与「期望回复」确认;新增人工介入 Webhook 通知(notifier) - 大地图后台预热与视野裁剪;控制台日志按帧批量写入、配置面板支持搜索 顺带修掉两个影响部署的细节: - 部署脚本(deploy.js / ssh-helper.js)改为也能从 .env 读 SSH 凭据 —— 在命令行传密码会留在 shell 历史与进程列表里,写进已被忽略的 .env 更安全; 显式环境变量仍然优先。.env.example 补上占位符并说明这些只在本机用。 - package.json / package-lock.json 版本对齐到 1.7.0(此前停留在 1.1.0, 与 CHANGELOG 的语义化版本声明脱节)。 测试:368 → 436 个用例,`npm test` 与 `npm run check` 全绿。
83 行
3.7 KiB
JavaScript
83 行
3.7 KiB
JavaScript
'use strict';
|
|
|
|
const test = require('node:test');
|
|
const assert = require('node:assert');
|
|
|
|
const { TargetLeaseTable, DEFAULT_TTL_MS } = require('../src/core/targetLease');
|
|
|
|
test('tryAcquire:先抢到的机器人独占,其他机器人拿不到', () => {
|
|
const table = new TargetLeaseTable();
|
|
const chunk = { x: 10, z: 20 };
|
|
assert.strictEqual(table.tryAcquire(chunk, 'A'), true);
|
|
assert.strictEqual(table.tryAcquire(chunk, 'B'), false, '被别人占用时应失败');
|
|
assert.strictEqual(table.isLeasedByOther(chunk, 'B'), true);
|
|
assert.strictEqual(table.isLeasedByOther(chunk, 'A'), false, '自己占用不算「被别人占用」');
|
|
});
|
|
|
|
test('tryAcquire:同一机器人可重复占用(视为续租)', () => {
|
|
const table = new TargetLeaseTable({ ttlMs: 1000 });
|
|
const chunk = { x: 1, z: 1 };
|
|
assert.strictEqual(table.tryAcquire(chunk, 'A'), true);
|
|
assert.strictEqual(table.tryAcquire(chunk, 'A'), true, '自己再抢一次不应失败');
|
|
assert.strictEqual(table.size, 1, '不应产生第二条记录');
|
|
});
|
|
|
|
test('租约到期后自动失效,其他机器人可接手(不会永久锁死区块)', async () => {
|
|
const table = new TargetLeaseTable({ ttlMs: 30 });
|
|
const chunk = { x: 5, z: 5 };
|
|
assert.strictEqual(table.tryAcquire(chunk, 'A'), true);
|
|
assert.strictEqual(table.tryAcquire(chunk, 'B'), false);
|
|
await new Promise((resolve) => setTimeout(resolve, 45));
|
|
assert.strictEqual(table.isLeasedByOther(chunk, 'B'), false, '过期后应视为空闲');
|
|
assert.strictEqual(table.tryAcquire(chunk, 'B'), true, '过期后其他机器人可接手');
|
|
});
|
|
|
|
test('release:只有持有者能释放,释放后别人立刻可用', () => {
|
|
const table = new TargetLeaseTable();
|
|
const chunk = { x: 2, z: 3 };
|
|
table.tryAcquire(chunk, 'A');
|
|
assert.strictEqual(table.release(chunk, 'B'), false, '非持有者不能释放');
|
|
assert.strictEqual(table.tryAcquire(chunk, 'B'), false);
|
|
assert.strictEqual(table.release(chunk, 'A'), true);
|
|
assert.strictEqual(table.tryAcquire(chunk, 'B'), true);
|
|
});
|
|
|
|
test('releaseAll 释放某机器人的全部占用(停止任务 / 断线时用)', () => {
|
|
const table = new TargetLeaseTable();
|
|
table.tryAcquire({ x: 0, z: 0 }, 'A');
|
|
table.tryAcquire({ x: 1, z: 0 }, 'A');
|
|
table.tryAcquire({ x: 0, z: 1 }, 'B');
|
|
assert.strictEqual(table.releaseAll('A'), 2);
|
|
assert.strictEqual(table.size, 1, '只应剩下 B 的占用');
|
|
assert.strictEqual(table.isLeasedByOther({ x: 0, z: 0 }, 'B'), false);
|
|
});
|
|
|
|
test('renew 延长租约,防止长距离寻路中途被抢', async () => {
|
|
const table = new TargetLeaseTable({ ttlMs: 40 });
|
|
const chunk = { x: 7, z: 7 };
|
|
table.tryAcquire(chunk, 'A');
|
|
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
assert.strictEqual(table.renew(chunk, 'A'), true);
|
|
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
// 已超过原始 TTL,但续租过 → 仍是 A 的
|
|
assert.strictEqual(table.tryAcquire(chunk, 'B'), false, '续租后不应被抢走');
|
|
});
|
|
|
|
test('renew 对非持有者无效', () => {
|
|
const table = new TargetLeaseTable();
|
|
table.tryAcquire({ x: 9, z: 9 }, 'A');
|
|
assert.strictEqual(table.renew({ x: 9, z: 9 }, 'B'), false);
|
|
});
|
|
|
|
test('sweep 清理过期租约,snapshot 只列出有效占用', async () => {
|
|
const table = new TargetLeaseTable({ ttlMs: 20 });
|
|
table.tryAcquire({ x: 1, z: 1 }, 'A');
|
|
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
assert.strictEqual(table.sweep(), 1);
|
|
assert.deepStrictEqual(table.snapshot(), []);
|
|
});
|
|
|
|
test('默认租约时长与配置常量一致', () => {
|
|
assert.strictEqual(new TargetLeaseTable().ttlMs, DEFAULT_TTL_MS);
|
|
assert.ok(DEFAULT_TTL_MS >= 60000, '默认不应太短,否则长距离寻路会被抢');
|
|
}); |