整合求解器活动监控与步长回归证据
同步远端 PNL0003 诊断和大采样网格能力,语义合并活动感知的 60 秒真停滞判定与旧后端 15 分钟兼容兜底。 纳管热路径优化、15 单元运行证据、浏览器与 API 报告,并补充北京时间更新日志和遗留问题。
This commit is contained in:
1 parent
c19cf77aee
commit
e18399c022
46 files changed
+181589
-170
No files matched your search
@@ -0,0 +1,252 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import {
|
||||
createSimulationActivityWatchdog,
|
||||
isSimulationSolverActivityPhase,
|
||||
observeSimulationActivityWatchdog,
|
||||
} from "../../src/App";
|
||||
|
||||
const TEST_STALL_TIMEOUT_MS = 1_000;
|
||||
|
||||
test("首个含 activitySequence 的慢步心跳只建立活动基线", () => {
|
||||
const state = createSimulationActivityWatchdog(0);
|
||||
const watched = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{ heartbeat: true, activitySequence: 42 },
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
);
|
||||
|
||||
expect(watched.observation.status).toBe("slow-active");
|
||||
expect(watched.observation.acceptedProgressIdleMs).toBe(TEST_STALL_TIMEOUT_MS);
|
||||
expect(watched.observation.activityIdleMs).toBe(0);
|
||||
});
|
||||
|
||||
test("accepted time 停滞但 activitySequence 持续增长时保持慢步运行", () => {
|
||||
let state = createSimulationActivityWatchdog(0);
|
||||
|
||||
({ state } = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{
|
||||
heartbeat: true,
|
||||
activitySequence: 1,
|
||||
acceptedStepSequence: 10,
|
||||
acceptedTime: 0.048,
|
||||
},
|
||||
100,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
));
|
||||
let watched = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{
|
||||
heartbeat: true,
|
||||
activitySequence: 2,
|
||||
acceptedStepSequence: 10,
|
||||
acceptedTime: 0.048,
|
||||
},
|
||||
1_000,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
);
|
||||
state = watched.state;
|
||||
expect(watched.observation.status).toBe("slow-active");
|
||||
|
||||
watched = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{
|
||||
heartbeat: true,
|
||||
activitySequence: 3,
|
||||
acceptedStepSequence: 10,
|
||||
acceptedTime: 0.048,
|
||||
},
|
||||
2_500,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
);
|
||||
expect(watched.observation.status).toBe("slow-active");
|
||||
expect(watched.observation.acceptedProgressIdleMs).toBe(2_500);
|
||||
expect(watched.observation.activityIdleMs).toBe(0);
|
||||
});
|
||||
|
||||
test("旧后端缺少活动字段时只报告遥测不可用且不误判 stalled", () => {
|
||||
const state = createSimulationActivityWatchdog(0);
|
||||
const watched = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{ heartbeat: true },
|
||||
20_000,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
);
|
||||
|
||||
expect(watched.observation.status).toBe("slow-telemetry-unavailable");
|
||||
expect(watched.observation.activityIdleMs).toBeNull();
|
||||
expect(watched.observation.activityTelemetryAvailable).toBe(false);
|
||||
});
|
||||
|
||||
test("旧后端缺少活动字段时使用 15 分钟兼容兜底", () => {
|
||||
const state = createSimulationActivityWatchdog(0);
|
||||
const watched = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{ heartbeat: true },
|
||||
900_000,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
);
|
||||
|
||||
expect(watched.observation.status).toBe("stalled");
|
||||
expect(watched.observation.activityTelemetryAvailable).toBe(false);
|
||||
});
|
||||
|
||||
test("接受步和明确可用的活动序号都长期不变时才报告 stalled", () => {
|
||||
let state = createSimulationActivityWatchdog(0);
|
||||
({ state } = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{ heartbeat: true, activitySequence: 7 },
|
||||
0,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
));
|
||||
|
||||
const beforeDeadline = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{ heartbeat: true, activitySequence: 7 },
|
||||
999,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
);
|
||||
expect(beforeDeadline.observation.status).toBe("progressing");
|
||||
|
||||
const atDeadline = observeSimulationActivityWatchdog(
|
||||
beforeDeadline.state,
|
||||
{ heartbeat: true, activitySequence: 7 },
|
||||
1_000,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
);
|
||||
expect(atDeadline.observation.status).toBe("stalled");
|
||||
});
|
||||
|
||||
test("heartbeat 报告新的 acceptedStepSequence 时重置接受步计时", () => {
|
||||
let state = createSimulationActivityWatchdog(0);
|
||||
({ state } = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{
|
||||
heartbeat: true,
|
||||
activitySequence: 1,
|
||||
acceptedStepSequence: 10,
|
||||
acceptedTime: 0.048,
|
||||
},
|
||||
0,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
));
|
||||
|
||||
const watched = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{
|
||||
heartbeat: true,
|
||||
activitySequence: 2,
|
||||
acceptedStepSequence: 11,
|
||||
acceptedTime: 0.049,
|
||||
},
|
||||
5_000,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
);
|
||||
expect(watched.observation.status).toBe("progressing");
|
||||
expect(watched.observation.acceptedProgressIdleMs).toBe(0);
|
||||
expect(watched.state.lastAcceptedProgressAt).toBe(5_000);
|
||||
});
|
||||
|
||||
test("validation 和 compilation 阶段不参与求解器停滞判定", () => {
|
||||
let state = createSimulationActivityWatchdog(0);
|
||||
|
||||
let watched = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{ phase: "validation", heartbeat: true },
|
||||
90_000,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
);
|
||||
state = watched.state;
|
||||
expect(watched.observation.status).toBe("progressing");
|
||||
expect(watched.observation.acceptedProgressIdleMs).toBe(0);
|
||||
expect(watched.state.integratingPhaseActive).toBe(false);
|
||||
|
||||
watched = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{ phase: "compilation", heartbeat: true, activitySequence: 7 },
|
||||
180_000,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
);
|
||||
expect(watched.observation.status).toBe("progressing");
|
||||
expect(watched.observation.activityIdleMs).toBeNull();
|
||||
expect(isSimulationSolverActivityPhase("compilation")).toBe(false);
|
||||
});
|
||||
|
||||
test("进入 integrating 时重新建立计时基线", () => {
|
||||
let state = createSimulationActivityWatchdog(0);
|
||||
({ state } = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{ phase: "initialization", heartbeat: true },
|
||||
120_000,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
));
|
||||
|
||||
let watched = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{
|
||||
phase: "integrating",
|
||||
heartbeat: true,
|
||||
activitySequence: 11,
|
||||
acceptedStepSequence: 3,
|
||||
acceptedTime: 0.048,
|
||||
},
|
||||
180_000,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
);
|
||||
state = watched.state;
|
||||
expect(watched.observation.status).toBe("progressing");
|
||||
expect(watched.observation.acceptedProgressIdleMs).toBe(0);
|
||||
expect(watched.observation.activityIdleMs).toBe(0);
|
||||
expect(watched.state.integratingPhaseActive).toBe(true);
|
||||
expect(isSimulationSolverActivityPhase("integrating")).toBe(true);
|
||||
|
||||
watched = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{
|
||||
phase: "integrating",
|
||||
heartbeat: true,
|
||||
activitySequence: 11,
|
||||
acceptedStepSequence: 3,
|
||||
acceptedTime: 0.048,
|
||||
},
|
||||
181_000,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
);
|
||||
expect(watched.observation.status).toBe("stalled");
|
||||
});
|
||||
|
||||
test("离开 integrating 后清除旧积分阶段的停滞状态", () => {
|
||||
let state = createSimulationActivityWatchdog(0);
|
||||
({ state } = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{
|
||||
phase: "integrating",
|
||||
heartbeat: true,
|
||||
activitySequence: 5,
|
||||
},
|
||||
0,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
));
|
||||
({ state } = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{
|
||||
phase: "integrating",
|
||||
heartbeat: true,
|
||||
activitySequence: 5,
|
||||
},
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
));
|
||||
|
||||
const watched = observeSimulationActivityWatchdog(
|
||||
state,
|
||||
{ phase: "postprocessing", heartbeat: true },
|
||||
60_000,
|
||||
TEST_STALL_TIMEOUT_MS,
|
||||
);
|
||||
expect(watched.observation.status).toBe("progressing");
|
||||
expect(watched.observation.acceptedProgressIdleMs).toBe(0);
|
||||
expect(watched.state.integratingPhaseActive).toBe(false);
|
||||
expect(watched.state.lastActivitySequence).toBeNull();
|
||||
});
|
||||
Reference in new issue
Block a user