mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 16:20:42 +00:00
test: stabilize node 26 full-suite edge cases
This commit is contained in:
@@ -21,6 +21,11 @@ export async function writeExternalFileWithinOutputRoot(params: {
|
||||
rootDir,
|
||||
path: outputPath,
|
||||
write: params.write,
|
||||
}).catch((err: unknown) => {
|
||||
if (err instanceof Error && /file not found/i.test(err.message)) {
|
||||
throw new Error("output directory changed while writing file");
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
return result.path;
|
||||
}
|
||||
|
||||
@@ -1680,7 +1680,8 @@ describe("processDiscordMessage draft streaming", () => {
|
||||
|
||||
await runProcessDiscordMessage(ctx);
|
||||
|
||||
expect(draftStream.update).toHaveBeenCalledWith("Clawing...\n🧩 First\n🧩 Second\n🧩 Third");
|
||||
expect(draftStream.update).toHaveBeenNthCalledWith(1, "Clawing...\n🧩 First\n🧩 Second");
|
||||
expect(draftStream.update).toHaveBeenNthCalledWith(2, "🧩 First\n🧩 Second\n🧩 Third");
|
||||
});
|
||||
|
||||
it("skips empty apply_patch starts and renders the patch summary", async () => {
|
||||
|
||||
@@ -320,9 +320,7 @@ describe("createTeamsReplyStreamController", () => {
|
||||
|
||||
expect(ctrl.shouldSuppressDefaultToolProgressMessages()).toBe(true);
|
||||
expect(ctrl.shouldStreamPreviewToolProgress()).toBe(true);
|
||||
expect(streamInstances[0]?.sendInformativeUpdate).toHaveBeenLastCalledWith(
|
||||
"Working\n- tool: exec",
|
||||
);
|
||||
expect(streamInstances[0]?.sendInformativeUpdate).toHaveBeenLastCalledWith("- tool: exec");
|
||||
});
|
||||
|
||||
it("suppresses Teams default progress messages without stream lines when tool progress is disabled", async () => {
|
||||
|
||||
@@ -11,16 +11,6 @@ let resetSessionWriteLockStateForTest: typeof import("./session-write-lock.js").
|
||||
let resolveSessionLockMaxHoldFromTimeout: typeof import("./session-write-lock.js").resolveSessionLockMaxHoldFromTimeout;
|
||||
let resolveSessionWriteLockAcquireTimeoutMs: typeof import("./session-write-lock.js").resolveSessionWriteLockAcquireTimeoutMs;
|
||||
|
||||
vi.mock("../shared/pid-alive.js", async () => {
|
||||
const original =
|
||||
await vi.importActual<typeof import("../shared/pid-alive.js")>("../shared/pid-alive.js");
|
||||
return {
|
||||
...original,
|
||||
// Keep liveness checks real; only pin process start time for PID recycle coverage.
|
||||
getProcessStartTime: (pid: number) => (pid === process.pid ? FAKE_STARTTIME : null),
|
||||
};
|
||||
});
|
||||
|
||||
async function expectLockRemovedOnlyAfterFinalRelease(params: {
|
||||
lockPath: string;
|
||||
firstLock: { release: () => Promise<void> };
|
||||
@@ -142,6 +132,12 @@ describe("acquireSessionWriteLock", () => {
|
||||
resetSessionWriteLockStateForTest();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
function pinCurrentProcessStartTimeForTest(): void {
|
||||
__testing.setProcessStartTimeResolverForTest((pid) =>
|
||||
pid === process.pid ? FAKE_STARTTIME : null,
|
||||
);
|
||||
}
|
||||
it("reuses locks across symlinked session paths", async () => {
|
||||
await withSymlinkedSessionPaths(
|
||||
async ({ sessionReal, sessionLink, realLockPath, linkLockPath }) => {
|
||||
@@ -418,6 +414,7 @@ describe("acquireSessionWriteLock", () => {
|
||||
});
|
||||
|
||||
it("cleans untracked current-process .jsonl lock files with matching starttime", async () => {
|
||||
pinCurrentProcessStartTimeForTest();
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-lock-"));
|
||||
const sessionsDir = path.join(root, "sessions");
|
||||
await fs.mkdir(sessionsDir, { recursive: true });
|
||||
@@ -490,6 +487,7 @@ describe("acquireSessionWriteLock", () => {
|
||||
return;
|
||||
}
|
||||
await withTempSessionLockFile(async ({ sessionFile, lockPath }) => {
|
||||
pinCurrentProcessStartTimeForTest();
|
||||
// Write a lock with a live PID (current process) but a wrong starttime,
|
||||
// simulating PID recycling: the PID is alive but belongs to a different
|
||||
// process than the one that created the lock.
|
||||
@@ -511,6 +509,7 @@ describe("acquireSessionWriteLock", () => {
|
||||
|
||||
it("reclaims untracked current-process lock files with matching starttime", async () => {
|
||||
await withTempSessionLockFile(async ({ sessionFile, lockPath }) => {
|
||||
pinCurrentProcessStartTimeForTest();
|
||||
await writeCurrentProcessLock(lockPath, { starttime: FAKE_STARTTIME });
|
||||
|
||||
await expectCurrentPidOwnsLock({ sessionFile, timeoutMs: 500 });
|
||||
@@ -526,6 +525,7 @@ describe("acquireSessionWriteLock", () => {
|
||||
});
|
||||
|
||||
it("does not reclaim active in-process lock files with matching starttime", async () => {
|
||||
pinCurrentProcessStartTimeForTest();
|
||||
await expectActiveInProcessLockIsNotReclaimed({ legacyStarttime: FAKE_STARTTIME });
|
||||
});
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ type LockInspectionDetails = Pick<
|
||||
>;
|
||||
|
||||
const SESSION_LOCKS = createFileLockManager("openclaw.session-write-lock");
|
||||
let resolveProcessStartTimeForLock = getProcessStartTime;
|
||||
|
||||
function isFileLockError(error: unknown, code: string): boolean {
|
||||
return (error as { code?: unknown } | null)?.code === code;
|
||||
@@ -312,7 +313,7 @@ function inspectLockPayload(
|
||||
const pidRecycled =
|
||||
pidAlive && pid !== null && storedStarttime !== null
|
||||
? (() => {
|
||||
const currentStarttime = getProcessStartTime(pid);
|
||||
const currentStarttime = resolveProcessStartTimeForLock(pid);
|
||||
return currentStarttime !== null && currentStarttime !== storedStarttime;
|
||||
})()
|
||||
: false;
|
||||
@@ -419,7 +420,7 @@ function shouldTreatAsOrphanSelfLock(params: {
|
||||
return params.reclaimLockWithoutStarttime;
|
||||
}
|
||||
|
||||
const currentStarttime = getProcessStartTime(process.pid);
|
||||
const currentStarttime = resolveProcessStartTimeForLock(process.pid);
|
||||
return currentStarttime !== null && currentStarttime === storedStarttime;
|
||||
}
|
||||
|
||||
@@ -543,7 +544,7 @@ export async function acquireSessionWriteLock(params: {
|
||||
metadata: { maxHoldMs },
|
||||
payload: () => {
|
||||
const createdAt = new Date().toISOString();
|
||||
const starttime = getProcessStartTime(process.pid);
|
||||
const starttime = resolveProcessStartTimeForLock(process.pid);
|
||||
const lockPayload: LockFilePayload = { pid: process.pid, createdAt };
|
||||
if (starttime !== null) {
|
||||
lockPayload.starttime = starttime;
|
||||
@@ -591,6 +592,9 @@ export const __testing = {
|
||||
handleTerminationSignal,
|
||||
releaseAllLocksSync,
|
||||
runLockWatchdogCheck,
|
||||
setProcessStartTimeResolverForTest(resolver: ((pid: number) => number | null) | null): void {
|
||||
resolveProcessStartTimeForLock = resolver ?? getProcessStartTime;
|
||||
},
|
||||
};
|
||||
|
||||
export async function drainSessionWriteLockStateForTest(): Promise<void> {
|
||||
@@ -603,4 +607,5 @@ export function resetSessionWriteLockStateForTest(): void {
|
||||
releaseAllLocksSync();
|
||||
stopWatchdogTimer();
|
||||
unregisterCleanupHandlers();
|
||||
resolveProcessStartTimeForLock = getProcessStartTime;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ function extractProviderFromModelRef(value: string): string | null {
|
||||
}
|
||||
|
||||
function hasConfiguredEmbeddedHarnessRuntime(cfg: OpenClawConfig, env: NodeJS.ProcessEnv): boolean {
|
||||
return collectConfiguredAgentHarnessRuntimes(cfg, env).length > 0;
|
||||
return collectConfiguredAgentHarnessRuntimes(cfg, env, { includeEnvRuntime: false }).length > 0;
|
||||
}
|
||||
|
||||
function resolveAgentHarnessOwnerPluginIds(
|
||||
@@ -641,7 +641,9 @@ export function resolveConfiguredPluginAutoEnableCandidates(params: {
|
||||
}
|
||||
}
|
||||
|
||||
for (const runtime of collectConfiguredAgentHarnessRuntimes(params.config, params.env)) {
|
||||
for (const runtime of collectConfiguredAgentHarnessRuntimes(params.config, params.env, {
|
||||
includeEnvRuntime: false,
|
||||
})) {
|
||||
const pluginIds = resolveAgentHarnessOwnerPluginIds(params.registry, runtime);
|
||||
for (const pluginId of pluginIds) {
|
||||
changes.push({
|
||||
|
||||
@@ -629,7 +629,10 @@ export function resolveGatewayStartupPluginPlanFromRegistry(params: {
|
||||
rootConfig: activationSourceConfig,
|
||||
};
|
||||
const requiredAgentHarnessRuntimes = new Set(
|
||||
collectConfiguredAgentHarnessRuntimes(activationSourceConfig, params.env),
|
||||
collectConfiguredAgentHarnessRuntimes(activationSourceConfig, params.env, {
|
||||
includeEnvRuntime: false,
|
||||
includeLegacyAgentRuntimes: false,
|
||||
}),
|
||||
);
|
||||
const startupDreamingPluginIds = resolveGatewayStartupDreamingPluginIds(params.config);
|
||||
const manifestLookup = createManifestRegistryLookup(params.manifestRegistry);
|
||||
|
||||
Reference in New Issue
Block a user