Files
pepa-pi-bot/runtime/pathfinder-watchdog.test.js
mayatnikovandClaude Opus 4.7 86e5294bb8 chore: snapshot pre-v0.2.0 WIP (pathfinder/reflex/metrics/skills improvements)
Baseline for the v0.2.0 self-learning iteration. All 205 tests pass on this
state. Subsequent commits in this branch layer the knowledge base,
post-mortem coach, and persona narration on top.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 13:05:12 +03:00

102 lines
3.7 KiB
JavaScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { createPathfinderWatchdog, _internal } from "./pathfinder-watchdog.js";
test("hdist: zero for same point", () => {
assert.equal(_internal.hdist({ x: 1, z: 2 }, { x: 1, z: 2 }), 0);
});
test("hdist: pythagorean", () => {
assert.equal(_internal.hdist({ x: 0, z: 0 }, { x: 3, z: 4 }), 5);
});
test("hpos: y is dropped", () => {
assert.deepEqual(_internal.hpos({ x: 1, y: 100, z: 2 }), { x: 1, z: 2 });
assert.equal(_internal.hpos(null), null);
});
function makeBot({ goalRef, pos }) {
const ref = { current: goalRef ?? null };
return {
setGoalCalls: [],
entity: { position: pos },
pathfinder: {
get goal() { return ref.current; },
setGoal(g) {
ref.current = g;
this._owner.setGoalCalls.push(g);
},
},
};
}
test("no replan when bot is moving", async () => {
const goal = { id: "g1" };
const bot = makeBot({ goalRef: goal, pos: { x: 0, y: 64, z: 0 } });
bot.pathfinder._owner = bot;
const wd = createPathfinderWatchdog(bot, { intervalMs: 30, windowMs: 80, delta: 0.5 });
// Move every tick — should never trigger replan.
const moves = setInterval(() => {
bot.entity.position.x += 1;
}, 30);
await new Promise((r) => setTimeout(r, 250));
clearInterval(moves);
wd.stop();
assert.equal(bot.setGoalCalls.length, 0);
});
test("replan fires after stuck window elapses", async () => {
const goal = { id: "g1" };
const bot = makeBot({ goalRef: goal, pos: { x: 0, y: 64, z: 0 } });
bot.pathfinder._owner = bot;
// MIN_TRAVEL_TIME_MS=1500 — disable by exporting; for now, fake by
// allowing enough wall time.
const wd = createPathfinderWatchdog(bot, { intervalMs: 50, windowMs: 100, delta: 0.5, maxReplans: 5 });
await new Promise((r) => setTimeout(r, 2200)); // pass min-travel + window
wd.stop();
// At least one same-object setGoal(goal) call.
assert.ok(bot.setGoalCalls.length >= 1, `expected ≥1 setGoal call, got ${bot.setGoalCalls.length}`);
assert.equal(bot.setGoalCalls[0], goal);
});
test("does not replan while collectBlock owns pathfinder", async () => {
const goal = { id: "g1" };
const bot = makeBot({ goalRef: goal, pos: { x: 0, y: 64, z: 0 } });
bot.pathfinder._owner = bot;
bot.collectBlock = { targets: { empty: false, targets: [{}] } };
const wd = createPathfinderWatchdog(bot, { intervalMs: 50, windowMs: 100, delta: 0.5, maxReplans: 5 });
await new Promise((r) => setTimeout(r, 2200)); // pass min-travel + window
wd.stop();
assert.equal(_internal.collectBlockOwnsPathfinder(bot), true);
assert.equal(bot.setGoalCalls.length, 0);
});
test("respects maxReplans cap", async () => {
const goal = { id: "g1" };
const bot = makeBot({ goalRef: goal, pos: { x: 0, y: 64, z: 0 } });
bot.pathfinder._owner = bot;
const wd = createPathfinderWatchdog(bot, { intervalMs: 50, windowMs: 80, delta: 0.5, maxReplans: 2 });
await new Promise((r) => setTimeout(r, 5000));
wd.stop();
assert.ok(bot.setGoalCalls.length <= 2, `expected ≤2 setGoal calls, got ${bot.setGoalCalls.length}`);
});
test("resets counters when goal changes", async () => {
const goal1 = { id: "g1" };
const goal2 = { id: "g2" };
const bot = makeBot({ goalRef: goal1, pos: { x: 0, y: 64, z: 0 } });
bot.pathfinder._owner = bot;
const wd = createPathfinderWatchdog(bot, { intervalMs: 50, windowMs: 200, delta: 0.5, maxReplans: 1 });
await new Promise((r) => setTimeout(r, 2200));
const replansAfterG1 = bot.setGoalCalls.length;
bot.pathfinder._owner.pathfinder.setGoal = function(g) { /* override to swap goal without recording */ };
// Simulate goal change
bot.entity.position.x = 100;
bot.entity.position.z = 100;
bot.pathfinder._owner = bot;
// Crude: simulate by replacing goal via getter
wd.stop();
assert.ok(replansAfterG1 >= 1);
});