refactor(deadcode): privatize core test seams (#108325)

* refactor(entry): privatize compile cache internals

* refactor(logging): privatize diagnostic test seams

* refactor(plugin-state): privatize store test seams

* refactor(skills): privatize lifecycle test seams

* refactor(node-host): privatize invoke test seam

* refactor(system-agent): privatize turn test seam

* chore(deadcode): shrink core test seam baseline
This commit is contained in:
Peter Steinberger
2026-07-15 06:37:40 -07:00
committed by GitHub
parent 20bf422cc7
commit 674dd37cd0
49 changed files with 500 additions and 101 deletions

View File

@@ -100,32 +100,12 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [
"src/cron/service/timer.ts: executeJobCore",
"src/cron/service/timer.ts: onTimer",
"src/cron/session-reaper.ts: resetReaperThrottle",
"src/entry.compile-cache.ts: buildOpenClawCompileCacheRespawnPlan",
"src/entry.compile-cache.ts: isNodeVersionAffectedByCompileCacheDeadlock",
"src/entry.compile-cache.ts: isSourceCheckoutInstallRoot",
"src/entry.compile-cache.ts: resolveOpenClawCompileCacheDirectory",
"src/entry.compile-cache.ts: runOpenClawCompileCacheRespawnPlan",
"src/entry.compile-cache.ts: shouldEnableOpenClawCompileCache",
"src/flows/doctor-health-contributions.ts: createDoctorHealthContribution",
"src/flows/doctor-health-contributions.ts: resolveDoctorHealthContributions",
"src/logging/diagnostic-run-activity.ts: markDiagnosticModelStartedForTest",
"src/logging/diagnostic-run-activity.ts: markDiagnosticRunProgressForTest",
"src/logging/diagnostic-run-activity.ts: markDiagnosticToolStartedForTest",
"src/logging/redact-internal.ts: withFullContextToolPayloadRedaction",
"src/logging/secret-redaction-registry.ts: resetSecretRedactionRegistryForTest",
"src/node-host/invoke.ts: testing",
"src/plugin-state/plugin-state-store.sqlite.ts: probePluginStateStore",
"src/plugin-state/plugin-state-store.sqlite.ts: seedPluginStateDatabaseEntriesForTests",
"src/plugin-state/plugin-state-store.sqlite.ts: setMaxPluginStateEntriesPerPluginForTests",
"src/plugin-state/plugin-state-store.ts: clearPluginStateStoreForTests",
"src/sessions/session-lifecycle-admission.ts: runExclusiveSessionLifecycle",
"src/sessions/session-upstream-monitor.ts: runSessionUpstreamMonitorTick",
"src/sessions/user-turn-transcript.ts: persistUserTurnTranscript",
"src/skills/lifecycle/install.ts: testing",
"src/skills/lifecycle/upload-store.ts: createSkillUploadStore",
"src/skills/runtime/refresh.ts: resetSkillsRefreshForTest",
"src/skills/runtime/remote-skills.ts: resetRemoteNodeSkillsForTests",
"src/system-agent/agent-turn.ts: runSystemAgentTurnWithDeps",
"src/tasks/detached-task-runtime.ts: resetDetachedTaskLifecycleRuntimeForTests",
"src/tasks/detached-task-runtime.ts: setDetachedTaskLifecycleRuntime",
"src/tasks/generated-media-task-activity.ts: resetGeneratedMediaTaskActivityForTests",

View File

@@ -11,10 +11,8 @@ import {
isReplyRunActiveForSessionId,
} from "../../auto-reply/reply/reply-run-registry.js";
import { setDiagnosticsEnabledForProcess } from "../../infra/diagnostic-events.js";
import {
markDiagnosticToolStartedForTest,
resetDiagnosticRunActivityForTest,
} from "../../logging/diagnostic-run-activity.js";
import { resetDiagnosticRunActivityForTest } from "../../logging/diagnostic-run-activity.js";
import { markDiagnosticToolStartedForTest } from "../../logging/diagnostic-run-activity.test-support.js";
import {
getDiagnosticSessionState,
resetDiagnosticSessionStateForTest,

View File

@@ -63,8 +63,10 @@ describe("mcp connection resolver helpers", () => {
});
it("registers resolved header and signed-URL credentials for redaction", async () => {
const { isSecretValueRegisteredForRedaction, resetSecretRedactionRegistryForTest } =
const { isSecretValueRegisteredForRedaction } =
await import("../logging/secret-redaction-registry.js");
const { resetSecretRedactionRegistryForTest } =
await import("../logging/secret-redaction-registry.test-support.js");
resetSecretRedactionRegistryForTest();
testing.setMcpServerConnectionResolversForTest([
{

View File

@@ -3,7 +3,7 @@ import type { AddressInfo } from "node:net";
import type { Model } from "openclaw/plugin-sdk/llm";
import { afterEach, describe, expect, it } from "vitest";
import { redactSensitiveText } from "../logging/redact.js";
import { resetSecretRedactionRegistryForTest } from "../logging/secret-redaction-registry.js";
import { resetSecretRedactionRegistryForTest } from "../logging/secret-redaction-registry.test-support.js";
import { mintSecretSentinel } from "../secrets/sentinel.js";
import { buildGuardedModelFetch } from "./provider-transport-fetch.js";

View File

@@ -8,7 +8,7 @@ import { createEmptyPluginRegistry } from "../plugins/registry-empty.js";
import type { PluginRegistry } from "../plugins/registry.js";
import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../plugins/runtime.js";
import type { ProviderPlugin } from "../plugins/types.js";
import { resetSkillsRefreshForTest } from "../skills/runtime/refresh.js";
import { resetSkillsRefreshForTest } from "../skills/runtime/refresh.test-support.js";
import {
clearSessionAuthProfileOverrideMock,
compactEmbeddedAgentSessionMock,

View File

@@ -9,10 +9,10 @@ import {
} from "../../config/sessions/session-accessor.js";
import type { SessionEntry } from "../../config/sessions/types.js";
import {
markDiagnosticToolStartedForTest,
resetDiagnosticRunActivityForTest,
RUN_STALE_TAKEOVER_MS,
} from "../../logging/diagnostic-run-activity.js";
import { markDiagnosticToolStartedForTest } from "../../logging/diagnostic-run-activity.test-support.js";
import {
interruptSessionWorkAdmissions,
runExclusiveSessionLifecycleMutation,

View File

@@ -14,8 +14,10 @@ import {
createPluginStateKeyedStore,
resetPluginStateStoreForTests,
} from "../plugin-state/plugin-state-store.js";
import { setMaxPluginStateEntriesPerPluginForTests } from "../plugin-state/plugin-state-store.sqlite.js";
import { seedPluginStateEntriesForTests } from "../plugin-state/plugin-state-store.test-helpers.js";
import {
seedPluginStateEntriesForTests,
setMaxPluginStateEntriesPerPluginForTests,
} from "../plugin-state/plugin-state-store.test-helpers.js";
import {
readPersistedInstalledPluginIndex,
writePersistedInstalledPluginIndex,

View File

@@ -0,0 +1,80 @@
import type { ChildProcess } from "node:child_process";
import type { RespawnChildRuntime } from "./process/respawn-child-runner.js";
import "./entry.compile-cache.js";
type CompileCacheParams = {
env?: NodeJS.ProcessEnv;
installRoot: string;
nodeVersion?: string;
platform?: NodeJS.Platform;
};
type CompileCacheRespawnPlan = {
command: string;
args: string[];
env: NodeJS.ProcessEnv;
detachForProcessTree: boolean;
};
type CompileCacheTestApi = {
buildOpenClawCompileCacheRespawnPlan(params: {
currentFile: string;
env?: NodeJS.ProcessEnv;
execArgv?: string[];
execPath?: string;
installRoot: string;
argv?: string[];
compileCacheDir?: string;
nodeVersion?: string;
platform?: NodeJS.Platform;
}): CompileCacheRespawnPlan | undefined;
isNodeVersionAffectedByCompileCacheDeadlock(nodeVersion: string | undefined): boolean;
isSourceCheckoutInstallRoot(installRoot: string): boolean;
resolveOpenClawCompileCacheDirectory(params: {
env?: NodeJS.ProcessEnv;
installRoot: string;
}): string;
runOpenClawCompileCacheRespawnPlan(
plan: CompileCacheRespawnPlan,
runtime?: RespawnChildRuntime & { writeError(message: string): void },
): ChildProcess;
shouldEnableOpenClawCompileCache(params: CompileCacheParams): boolean;
};
function getTestApi(): CompileCacheTestApi {
return (globalThis as Record<PropertyKey, unknown>)[
Symbol.for("openclaw.entryCompileCacheTestApi")
] as CompileCacheTestApi;
}
export function buildOpenClawCompileCacheRespawnPlan(
params: Parameters<CompileCacheTestApi["buildOpenClawCompileCacheRespawnPlan"]>[0],
): CompileCacheRespawnPlan | undefined {
return getTestApi().buildOpenClawCompileCacheRespawnPlan(params);
}
export function isNodeVersionAffectedByCompileCacheDeadlock(
nodeVersion: string | undefined,
): boolean {
return getTestApi().isNodeVersionAffectedByCompileCacheDeadlock(nodeVersion);
}
export function isSourceCheckoutInstallRoot(installRoot: string): boolean {
return getTestApi().isSourceCheckoutInstallRoot(installRoot);
}
export function resolveOpenClawCompileCacheDirectory(
params: Parameters<CompileCacheTestApi["resolveOpenClawCompileCacheDirectory"]>[0],
): string {
return getTestApi().resolveOpenClawCompileCacheDirectory(params);
}
export function runOpenClawCompileCacheRespawnPlan(
...args: Parameters<CompileCacheTestApi["runOpenClawCompileCacheRespawnPlan"]>
): ChildProcess {
return getTestApi().runOpenClawCompileCacheRespawnPlan(...args);
}
export function shouldEnableOpenClawCompileCache(params: CompileCacheParams): boolean {
return getTestApi().shouldEnableOpenClawCompileCache(params);
}

View File

@@ -5,15 +5,15 @@ import fs from "node:fs/promises";
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { cleanupTempDirs, makeTempDir } from "../test/helpers/temp-dir.js";
import { resolveEntryInstallRoot } from "./entry.compile-cache.js";
import {
buildOpenClawCompileCacheRespawnPlan,
isNodeVersionAffectedByCompileCacheDeadlock,
isSourceCheckoutInstallRoot,
resolveOpenClawCompileCacheDirectory,
resolveEntryInstallRoot,
runOpenClawCompileCacheRespawnPlan,
shouldEnableOpenClawCompileCache,
} from "./entry.compile-cache.js";
} from "./entry.compile-cache.test-support.js";
function requireFirstMockCall(mock: { mock: { calls: unknown[][] } }, label: string): unknown[] {
const [call] = mock.mock.calls;

View File

@@ -24,7 +24,7 @@ export function resolveEntryInstallRoot(entryFile: string): string {
return entryParent === "dist" || entryParent === "src" ? path.dirname(entryDir) : entryDir;
}
export function isSourceCheckoutInstallRoot(installRoot: string): boolean {
function isSourceCheckoutInstallRoot(installRoot: string): boolean {
return (
existsSync(path.join(installRoot, ".git")) ||
existsSync(path.join(installRoot, "src", "entry.ts"))
@@ -39,9 +39,7 @@ function isNodeCompileCacheRequested(env: NodeJS.ProcessEnv | undefined): boolea
return env?.NODE_COMPILE_CACHE !== undefined && !isNodeCompileCacheDisabled(env);
}
export function isNodeVersionAffectedByCompileCacheDeadlock(
nodeVersion: string | undefined,
): boolean {
function isNodeVersionAffectedByCompileCacheDeadlock(nodeVersion: string | undefined): boolean {
if (!nodeVersion) {
return false;
}
@@ -57,7 +55,7 @@ export function isNodeVersionAffectedByCompileCacheDeadlock(
return minor < MIN_COMPILE_CACHE_NODE_24_MINOR;
}
export function shouldEnableOpenClawCompileCache(params: {
function shouldEnableOpenClawCompileCache(params: {
env?: NodeJS.ProcessEnv;
installRoot: string;
nodeVersion?: string;
@@ -98,7 +96,7 @@ function readPackageVersion(packageJsonPath: string): string {
return "unknown";
}
export function resolveOpenClawCompileCacheDirectory(params: {
function resolveOpenClawCompileCacheDirectory(params: {
env?: NodeJS.ProcessEnv;
installRoot: string;
}): string {
@@ -135,7 +133,7 @@ type OpenClawCompileCacheRespawnRuntime = RespawnChildRuntime & {
writeError: (message: string) => void;
};
export function buildOpenClawCompileCacheRespawnPlan(params: {
function buildOpenClawCompileCacheRespawnPlan(params: {
currentFile: string;
env?: NodeJS.ProcessEnv;
execArgv?: string[];
@@ -196,7 +194,7 @@ export function respawnWithoutOpenClawCompileCacheIfNeeded(params: {
return true;
}
export function runOpenClawCompileCacheRespawnPlan(
function runOpenClawCompileCacheRespawnPlan(
plan: OpenClawCompileCacheRespawnPlan,
runtime: OpenClawCompileCacheRespawnRuntime = {
spawn,
@@ -234,3 +232,14 @@ export function enableOpenClawCompileCache(params: {
// Best-effort only; never block startup.
}
}
if (process.env.VITEST || process.env.NODE_ENV === "test") {
(globalThis as Record<PropertyKey, unknown>)[Symbol.for("openclaw.entryCompileCacheTestApi")] = {
buildOpenClawCompileCacheRespawnPlan,
isNodeVersionAffectedByCompileCacheDeadlock,
isSourceCheckoutInstallRoot,
resolveOpenClawCompileCacheDirectory,
runOpenClawCompileCacheRespawnPlan,
shouldEnableOpenClawCompileCache,
};
}

View File

@@ -13,7 +13,7 @@ import {
type DiagnosticSecurityEvent,
} from "../../infra/diagnostic-events.js";
import { approveNodePairing, requestNodePairing } from "../../infra/node-pairing.js";
import { resetRemoteNodeSkillsForTests } from "../../skills/runtime/remote-skills.js";
import { resetRemoteNodeSkillsForTests } from "../../skills/runtime/remote-skills.test-support.js";
import {
createOpenClawTestState,
type OpenClawTestState,

View File

@@ -1,8 +1,6 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import {
isSecretValueRegisteredForRedaction,
resetSecretRedactionRegistryForTest,
} from "../../logging/secret-redaction-registry.js";
import { isSecretValueRegisteredForRedaction } from "../../logging/secret-redaction-registry.js";
import { resetSecretRedactionRegistryForTest } from "../../logging/secret-redaction-registry.test-support.js";
import { createWorkerCredentialMaterial, hashWorkerCredential } from "./credential.js";
afterEach(resetSecretRedactionRegistryForTest);

View File

@@ -0,0 +1,46 @@
import type { DiagnosticEventPayload } from "../infra/diagnostic-events.js";
import "./diagnostic-run-activity.js";
type DiagnosticModelStartedActivityEvent = Pick<
Extract<DiagnosticEventPayload, { type: "model.call.started" }>,
"runId" | "sessionId" | "sessionKey" | "provider" | "model"
> & { seq?: number };
type DiagnosticRunProgressActivityEvent = Pick<
Extract<DiagnosticEventPayload, { type: "run.progress" }>,
"runId" | "sessionId" | "sessionKey" | "reason"
>;
type DiagnosticRunActivityTestApi = {
markDiagnosticModelStartedForTest(params: DiagnosticModelStartedActivityEvent): void;
markDiagnosticRunProgressForTest(params: DiagnosticRunProgressActivityEvent): void;
markDiagnosticToolStartedForTest(params: {
sessionId?: string;
sessionKey?: string;
runId?: string;
toolName: string;
toolCallId?: string;
}): void;
};
function getTestApi(): DiagnosticRunActivityTestApi {
return (globalThis as Record<PropertyKey, unknown>)[
Symbol.for("openclaw.diagnosticRunActivityTestApi")
] as DiagnosticRunActivityTestApi;
}
export function markDiagnosticModelStartedForTest(
params: DiagnosticModelStartedActivityEvent,
): void {
getTestApi().markDiagnosticModelStartedForTest(params);
}
export function markDiagnosticRunProgressForTest(params: DiagnosticRunProgressActivityEvent): void {
getTestApi().markDiagnosticRunProgressForTest(params);
}
export function markDiagnosticToolStartedForTest(
params: Parameters<DiagnosticRunActivityTestApi["markDiagnosticToolStartedForTest"]>[0],
): void {
getTestApi().markDiagnosticToolStartedForTest(params);
}

View File

@@ -627,11 +627,11 @@ export function getDiagnosticEmbeddedRunActivitySequence(): number {
return embeddedRunSequence;
}
export function markDiagnosticRunProgressForTest(params: DiagnosticRunProgressActivityEvent): void {
function markDiagnosticRunProgressForTest(params: DiagnosticRunProgressActivityEvent): void {
markDiagnosticRunProgress(params);
}
export function markDiagnosticToolStartedForTest(params: {
function markDiagnosticToolStartedForTest(params: {
sessionId?: string;
sessionKey?: string;
runId?: string;
@@ -641,9 +641,7 @@ export function markDiagnosticToolStartedForTest(params: {
recordToolStarted(params);
}
export function markDiagnosticModelStartedForTest(
params: DiagnosticModelStartedActivityEvent,
): void {
function markDiagnosticModelStartedForTest(params: DiagnosticModelStartedActivityEvent): void {
recordModelStarted(params);
}
@@ -691,3 +689,13 @@ function registerDiagnosticRunActivityListener(): void {
}
registerDiagnosticRunActivityListener();
if (process.env.VITEST || process.env.NODE_ENV === "test") {
(globalThis as Record<PropertyKey, unknown>)[
Symbol.for("openclaw.diagnosticRunActivityTestApi")
] = {
markDiagnosticModelStartedForTest,
markDiagnosticRunProgressForTest,
markDiagnosticToolStartedForTest,
};
}

View File

@@ -14,11 +14,13 @@ import {
getDiagnosticSessionActivitySnapshot,
markDiagnosticEmbeddedRunEnded,
markDiagnosticEmbeddedRunStarted,
resetDiagnosticRunActivityForTest,
} from "./diagnostic-run-activity.js";
import {
markDiagnosticModelStartedForTest,
markDiagnosticRunProgressForTest,
markDiagnosticToolStartedForTest,
resetDiagnosticRunActivityForTest,
} from "./diagnostic-run-activity.js";
} from "./diagnostic-run-activity.test-support.js";
import type { SessionAttentionClassification } from "./diagnostic-session-attention.js";
import {
requestStuckSessionRecovery,

View File

@@ -15,10 +15,8 @@ import {
redactToolPayloadTextWithConfig,
resolveRedactOptions,
} from "./redact.js";
import {
registerSecretValueForRedaction,
resetSecretRedactionRegistryForTest,
} from "./secret-redaction-registry.js";
import { registerSecretValueForRedaction } from "./secret-redaction-registry.js";
import { resetSecretRedactionRegistryForTest } from "./secret-redaction-registry.test-support.js";
const defaults = getDefaultRedactPatterns();
let tempDirs: string[] = [];

View File

@@ -0,0 +1,15 @@
import "./secret-redaction-registry.js";
type SecretRedactionRegistryTestApi = {
resetSecretRedactionRegistryForTest(): void;
};
function getTestApi(): SecretRedactionRegistryTestApi {
return (globalThis as Record<PropertyKey, unknown>)[
Symbol.for("openclaw.secretRedactionRegistryTestApi")
] as SecretRedactionRegistryTestApi;
}
export function resetSecretRedactionRegistryForTest(): void {
getTestApi().resetSecretRedactionRegistryForTest();
}

View File

@@ -87,8 +87,13 @@ export function redactRegisteredSecretValues(
return text.replace(compiledMatcher, (value) => mask(value));
}
/** Test-only reset for process-global redaction state. */
export function resetSecretRedactionRegistryForTest(): void {
function resetSecretRedactionRegistryForTest(): void {
registeredValues.clear();
rebuildProbe();
}
if (process.env.VITEST || process.env.NODE_ENV === "test") {
(globalThis as Record<PropertyKey, unknown>)[
Symbol.for("openclaw.secretRedactionRegistryTestApi")
] = { resetSecretRedactionRegistryForTest };
}

View File

@@ -1,7 +1,8 @@
/** Tests the built-in node-host MCP invocation command. */
import { describe, expect, it, vi } from "vitest";
import type { GatewayClient } from "../gateway/client.js";
import { handleInvoke, testing } from "./invoke.js";
import { handleInvoke } from "./invoke.js";
import { testing } from "./invoke.test-support.js";
import { NodeHostMcpError, type NodeHostMcpManager } from "./mcp.js";
async function invokeMcp(manager: NodeHostMcpManager, params: unknown) {

View File

@@ -2,7 +2,7 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { testing } from "./invoke.js";
import { testing } from "./invoke.test-support.js";
describe("runCommand", () => {
afterEach(() => {

View File

@@ -0,0 +1,35 @@
import type { RunResult } from "./invoke-types.js";
import "./invoke.js";
type NodeHostInvokeTestApi = {
readonly MCP_TEXT_CONTENT_MAX_BYTES: number;
readonly MCP_INVOKE_PAYLOAD_MAX_BYTES: number;
clarifyNodeExecCwdSpawnError(error: NodeJS.ErrnoException, cwd: string | undefined): string;
runCommand(
argv: string[],
cwd: string | undefined,
env: Record<string, string> | undefined,
timeoutMs: number | undefined,
): Promise<RunResult>;
};
function getTestApi(): NodeHostInvokeTestApi {
return (globalThis as Record<PropertyKey, unknown>)[
Symbol.for("openclaw.nodeHostInvokeTestApi")
] as NodeHostInvokeTestApi;
}
export const testing: NodeHostInvokeTestApi = {
get MCP_TEXT_CONTENT_MAX_BYTES() {
return getTestApi().MCP_TEXT_CONTENT_MAX_BYTES;
},
get MCP_INVOKE_PAYLOAD_MAX_BYTES() {
return getTestApi().MCP_INVOKE_PAYLOAD_MAX_BYTES;
},
clarifyNodeExecCwdSpawnError(error, cwd) {
return getTestApi().clarifyNodeExecCwdSpawnError(error, cwd);
},
runCommand(argv, cwd, env, timeoutMs) {
return getTestApi().runCommand(argv, cwd, env, timeoutMs);
},
};

View File

@@ -1075,10 +1075,15 @@ async function sendNodeEvent(client: NodeHostClient, event: string, payload: unk
}
}
export const testing = {
const testing = {
MCP_TEXT_CONTENT_MAX_BYTES,
MCP_INVOKE_PAYLOAD_MAX_BYTES,
clarifyNodeExecCwdSpawnError,
runCommand,
} as const;
if (process.env.VITEST || process.env.NODE_ENV === "test") {
(globalThis as Record<PropertyKey, unknown>)[Symbol.for("openclaw.nodeHostInvokeTestApi")] =
testing;
}
/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */

View File

@@ -9,7 +9,7 @@ import {
resetPluginStateStoreForTests,
sweepExpiredPluginStateEntries,
} from "./plugin-state-store.js";
import { probePluginStateStore } from "./plugin-state-store.sqlite.js";
import { probePluginStateStore } from "./plugin-state-store.test-helpers.js";
afterEach(() => {
vi.useRealTimers();

View File

@@ -905,7 +905,7 @@ export function clearPluginStateDatabaseForTests(): void {
);
}
export function setMaxPluginStateEntriesPerPluginForTests(value?: number): void {
function setMaxPluginStateEntriesPerPluginForTests(value?: number): void {
maxPluginStateEntriesPerPluginForTests = value;
}
@@ -923,7 +923,7 @@ export function countPluginStateLiveEntries(pluginId: string): number {
}
}
export function seedPluginStateDatabaseEntriesForTests(
function seedPluginStateDatabaseEntriesForTests(
entries: readonly PluginStateSeedEntryForTests[],
): void {
if (entries.length === 0) {
@@ -948,7 +948,7 @@ export function seedPluginStateDatabaseEntriesForTests(
});
}
export function probePluginStateStore(): PluginStateStoreProbeResult {
function probePluginStateStore(): PluginStateStoreProbeResult {
const databasePath = resolveOpenClawStateSqlitePath(process.env);
const steps: PluginStateStoreProbeStep[] = [];
const wasOpen = cachedDatabase !== null;
@@ -1039,4 +1039,12 @@ export function closePluginStateDatabase(): void {
cachedDatabase = null;
closeOpenClawStateDatabase();
}
if (process.env.VITEST || process.env.NODE_ENV === "test") {
(globalThis as Record<PropertyKey, unknown>)[Symbol.for("openclaw.pluginStateSqliteTestApi")] = {
probePluginStateStore,
seedPluginStateDatabaseEntriesForTests,
setMaxPluginStateEntriesPerPluginForTests,
};
}
/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */

View File

@@ -1,5 +1,7 @@
// Plugin state test helpers seed SQLite plugin state fixtures.
import { seedPluginStateDatabaseEntriesForTests } from "./plugin-state-store.sqlite.js";
import type { PluginStateStoreProbeResult } from "./plugin-state-store.types.js";
import "./plugin-state-store.js";
import "./plugin-state-store.sqlite.js";
// Test-only seed helpers for plugin state. Values are serialized through the
// same JSON storage path used by the production sqlite store.
@@ -12,13 +14,56 @@ type PluginStateSeedEntry = {
expiresAt?: number | null;
};
type PluginStateDatabaseSeedEntry = {
pluginId: string;
namespace: string;
key: string;
valueJson: string;
createdAt?: number;
expiresAt?: number | null;
};
type PluginStateSqliteTestApi = {
probePluginStateStore(): PluginStateStoreProbeResult;
seedPluginStateDatabaseEntriesForTests(entries: readonly PluginStateDatabaseSeedEntry[]): void;
setMaxPluginStateEntriesPerPluginForTests(value?: number): void;
};
type PluginStateStoreTestApi = {
clearPluginStateStoreForTests(): void;
};
function getSqliteTestApi(): PluginStateSqliteTestApi {
return (globalThis as Record<PropertyKey, unknown>)[
Symbol.for("openclaw.pluginStateSqliteTestApi")
] as PluginStateSqliteTestApi;
}
function getStoreTestApi(): PluginStateStoreTestApi {
return (globalThis as Record<PropertyKey, unknown>)[
Symbol.for("openclaw.pluginStateStoreTestApi")
] as PluginStateStoreTestApi;
}
export function clearPluginStateStoreForTests(): void {
getStoreTestApi().clearPluginStateStoreForTests();
}
export function probePluginStateStore(): PluginStateStoreProbeResult {
return getSqliteTestApi().probePluginStateStore();
}
export function setMaxPluginStateEntriesPerPluginForTests(value?: number): void {
getSqliteTestApi().setMaxPluginStateEntriesPerPluginForTests(value);
}
/** Seeds plugin state entries for tests without opening public store handles. */
export function seedPluginStateEntriesForTests(entries: PluginStateSeedEntry[]): void {
if (entries.length === 0) {
return;
}
seedPluginStateDatabaseEntriesForTests(
getSqliteTestApi().seedPluginStateDatabaseEntriesForTests(
entries.map((entry) => {
const valueJson = JSON.stringify(entry.value);
if (valueJson == null) {

View File

@@ -11,7 +11,6 @@ import {
type OpenClawTestState,
} from "../test-utils/openclaw-test-state.js";
import {
clearPluginStateStoreForTests,
closePluginStateDatabase,
createCorePluginStateSyncKeyedStore,
createPluginStateKeyedStore,
@@ -20,10 +19,11 @@ import {
sweepExpiredPluginStateEntries,
} from "./plugin-state-store.js";
import {
clearPluginStateStoreForTests,
probePluginStateStore,
seedPluginStateEntriesForTests,
setMaxPluginStateEntriesPerPluginForTests,
} from "./plugin-state-store.sqlite.js";
import { seedPluginStateEntriesForTests } from "./plugin-state-store.test-helpers.js";
} from "./plugin-state-store.test-helpers.js";
import { PluginStateStoreError } from "./plugin-state-store.types.js";
let testState: OpenClawTestState | undefined;

View File

@@ -511,7 +511,7 @@ export function createCorePluginStateSyncKeyedStore<T>(
}
/** Clears plugin-state rows and option signatures for tests. */
export function clearPluginStateStoreForTests(): void {
function clearPluginStateStoreForTests(): void {
clearPluginStateDatabaseForTests();
namespaceOptionSignatures.clear();
}
@@ -524,3 +524,9 @@ export function resetPluginStateStoreForTests(options: { closeDatabase?: boolean
}
namespaceOptionSignatures.clear();
}
if (process.env.VITEST || process.env.NODE_ENV === "test") {
(globalThis as Record<PropertyKey, unknown>)[Symbol.for("openclaw.pluginStateStoreTestApi")] = {
clearPluginStateStoreForTests,
};
}

View File

@@ -1,9 +1,7 @@
// Proxy capture runtime tests cover session creation and capture lifecycle.
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
registerSecretValueForRedaction,
resetSecretRedactionRegistryForTest,
} from "../logging/secret-redaction-registry.js";
import { registerSecretValueForRedaction } from "../logging/secret-redaction-registry.js";
import { resetSecretRedactionRegistryForTest } from "../logging/secret-redaction-registry.test-support.js";
import type { DebugProxySettings } from "./env.js";
import {
captureHttpExchange,

View File

@@ -1,7 +1,7 @@
/** Tests runtime SecretRef resolution across core config and auth-profile surfaces. */
import { afterEach, describe, expect, it } from "vitest";
import { redactSensitiveText } from "../logging/redact.js";
import { resetSecretRedactionRegistryForTest } from "../logging/secret-redaction-registry.js";
import { resetSecretRedactionRegistryForTest } from "../logging/secret-redaction-registry.test-support.js";
import { asConfig, setupSecretsRuntimeSnapshotTestHooks } from "./runtime.test-support.ts";
const EMPTY_LOADABLE_PLUGIN_ORIGINS = new Map();

View File

@@ -1,6 +1,6 @@
import { afterEach, describe, expect, it } from "vitest";
import { redactSensitiveText } from "../logging/redact.js";
import { resetSecretRedactionRegistryForTest } from "../logging/secret-redaction-registry.js";
import { resetSecretRedactionRegistryForTest } from "../logging/secret-redaction-registry.test-support.js";
import {
looksLikeSecretSentinel,
mintSecretSentinel,

View File

@@ -25,14 +25,11 @@ vi.mock("../loading/workspace.js", () => ({
let installSkill: typeof import("./install.js").installSkill;
let resolveInstallerKindReadiness: typeof import("./install.js").resolveInstallerKindReadiness;
let skillsInstallTesting: typeof import("./install.js").testing;
let skillsInstallTesting: typeof import("./install.test-support.js").skillsInstallTesting;
async function loadSkillsInstallModulesForTest() {
({
installSkill,
resolveInstallerKindReadiness,
testing: skillsInstallTesting,
} = await import("./install.js"));
({ installSkill, resolveInstallerKindReadiness } = await import("./install.js"));
({ skillsInstallTesting } = await import("./install.test-support.js"));
}
function makeSkillEntry(

View File

@@ -0,0 +1,39 @@
import type { resolveSkillsInstallPreferences } from "../loading/config.js";
import type { loadWorkspaceSkillEntries } from "../loading/workspace.js";
import "./install.js";
type SkillsInstallDeps = {
hasBinary(bin: string): boolean;
loadWorkspaceSkillEntries: typeof loadWorkspaceSkillEntries;
resolveNodeInstallStateDir(): string;
resolveBrewExecutable(): string | undefined;
isContainerEnvironment(): boolean;
resolveSkillsInstallPreferences: typeof resolveSkillsInstallPreferences;
};
type ResolveDefaultNodeInstallStateDirParams = {
cwd?: string;
getuid?: () => number;
homedir?: () => string;
platform?: NodeJS.Platform;
};
type SkillsInstallTestApi = {
resolveDefaultNodeInstallStateDir(params?: ResolveDefaultNodeInstallStateDirParams): string;
setDepsForTest(overrides?: Partial<SkillsInstallDeps>): void;
};
function getTestApi(): SkillsInstallTestApi {
return (globalThis as Record<PropertyKey, unknown>)[
Symbol.for("openclaw.skillsInstallTestApi")
] as SkillsInstallTestApi;
}
export const skillsInstallTesting: SkillsInstallTestApi = {
resolveDefaultNodeInstallStateDir(params) {
return getTestApi().resolveDefaultNodeInstallStateDir(params);
},
setDepsForTest(overrides) {
getTestApi().setDepsForTest(overrides);
},
};

View File

@@ -13,7 +13,8 @@ import { resolveOpenClawMetadata, resolveSkillInvocationPolicy } from "../loadin
import { loadSkillsFromDirSafe, readSkillFrontmatterSafe } from "../loading/local-loader.js";
import { runCommandWithTimeoutMock } from "../test-support/install-test-mocks.js";
import type { SkillEntry } from "../types.js";
import { installSkill, testing as skillsInstallTesting } from "./install.js";
import { installSkill } from "./install.js";
import { skillsInstallTesting } from "./install.test-support.js";
vi.mock("../../process/exec.js", () => ({
runCommandWithTimeout: (...args: unknown[]) => runCommandWithTimeoutMock(...args),

View File

@@ -819,7 +819,7 @@ export async function installSkill(params: SkillInstallRequest): Promise<SkillIn
return withWarnings(normalizedResult, warnings);
}
export const testing = {
const testing = {
resolveDefaultNodeInstallStateDir,
setDepsForTest(overrides?: Partial<SkillsInstallDeps>): void {
skillsInstallDeps = {
@@ -828,4 +828,9 @@ export const testing = {
};
},
};
if (process.env.VITEST || process.env.NODE_ENV === "test") {
(globalThis as Record<PropertyKey, unknown>)[Symbol.for("openclaw.skillsInstallTestApi")] =
testing;
}
/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */

View File

@@ -0,0 +1,22 @@
import type { SkillUploadStore } from "./upload-store.js";
import "./upload-store.js";
type SkillUploadStoreTestApi = {
createSkillUploadStore(options?: {
rootDir?: string;
now?: () => number;
ttlMs?: number;
}): SkillUploadStore;
};
function getTestApi(): SkillUploadStoreTestApi {
return (globalThis as Record<PropertyKey, unknown>)[
Symbol.for("openclaw.skillUploadStoreTestApi")
] as SkillUploadStoreTestApi;
}
export function createSkillUploadStore(
options?: Parameters<SkillUploadStoreTestApi["createSkillUploadStore"]>[0],
): SkillUploadStore {
return getTestApi().createSkillUploadStore(options);
}

View File

@@ -5,7 +5,8 @@ import os from "node:os";
import path from "node:path";
import { MAX_DATE_TIMESTAMP_MS } from "@openclaw/normalization-core/number-coercion";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { createSkillUploadStore, SkillUploadRequestError } from "./upload-store.js";
import { SkillUploadRequestError } from "./upload-store.js";
import { createSkillUploadStore } from "./upload-store.test-support.js";
const ACTIVE_UPLOAD_LIMIT = 32;

View File

@@ -361,7 +361,7 @@ async function readCommittedRecord(
return record;
}
export function createSkillUploadStore(options?: {
function createSkillUploadStore(options?: {
rootDir?: string;
now?: () => number;
ttlMs?: number;
@@ -608,3 +608,9 @@ export function createSkillUploadStore(options?: {
}
export const defaultSkillUploadStore = createSkillUploadStore();
if (process.env.VITEST || process.env.NODE_ENV === "test") {
(globalThis as Record<PropertyKey, unknown>)[Symbol.for("openclaw.skillUploadStoreTestApi")] = {
createSkillUploadStore,
};
}

View File

@@ -0,0 +1,15 @@
import "./refresh.js";
type SkillsRefreshTestApi = {
resetSkillsRefreshForTest(): Promise<void>;
};
function getTestApi(): SkillsRefreshTestApi {
return (globalThis as Record<PropertyKey, unknown>)[
Symbol.for("openclaw.skillsRefreshTestApi")
] as SkillsRefreshTestApi;
}
export async function resetSkillsRefreshForTest(): Promise<void> {
await getTestApi().resetSkillsRefreshForTest();
}

View File

@@ -38,6 +38,7 @@ const watchMock = vi.fn(() => {
});
let refreshModule: typeof import("./refresh.js");
let refreshTestSupport: typeof import("./refresh.test-support.js");
vi.mock("chokidar", () => ({
default: { watch: watchMock },
@@ -50,6 +51,7 @@ vi.mock("../loading/plugin-skills.js", () => ({
describe("ensureSkillsWatcher", () => {
beforeAll(async () => {
refreshModule = await import("./refresh.js");
refreshTestSupport = await import("./refresh.test-support.js");
});
beforeEach(() => {
@@ -59,7 +61,7 @@ describe("ensureSkillsWatcher", () => {
afterEach(async () => {
vi.useRealTimers();
await refreshModule.resetSkillsRefreshForTest();
await refreshTestSupport.resetSkillsRefreshForTest();
});
it("watches skill roots and filters non-skill churn", async () => {

View File

@@ -712,7 +712,7 @@ export function ensureSkillsWatcher(params: { workspaceDir: string; config?: Ope
evictIdleWorkspaceWatchStates(now);
}
export async function resetSkillsRefreshForTest(): Promise<void> {
async function resetSkillsRefreshForTest(): Promise<void> {
resetSkillsRefreshStateForTest();
const active = Array.from(pathWatchers.values());
@@ -733,3 +733,9 @@ export async function resetSkillsRefreshForTest(): Promise<void> {
}),
);
}
if (process.env.VITEST || process.env.NODE_ENV === "test") {
(globalThis as Record<PropertyKey, unknown>)[Symbol.for("openclaw.skillsRefreshTestApi")] = {
resetSkillsRefreshForTest,
};
}

View File

@@ -0,0 +1,15 @@
import "./remote-skills.js";
type RemoteNodeSkillsTestApi = {
resetRemoteNodeSkillsForTests(): void;
};
function getTestApi(): RemoteNodeSkillsTestApi {
return (globalThis as Record<PropertyKey, unknown>)[
Symbol.for("openclaw.remoteNodeSkillsTestApi")
] as RemoteNodeSkillsTestApi;
}
export function resetRemoteNodeSkillsForTests(): void {
getTestApi().resetRemoteNodeSkillsForTests();
}

View File

@@ -10,8 +10,8 @@ import {
recordRemoteSkillNodeInfo,
removeRemoteNodeSkills,
replaceRemoteNodeSkills,
resetRemoteNodeSkillsForTests,
} from "./remote-skills.js";
import { resetRemoteNodeSkillsForTests } from "./remote-skills.test-support.js";
function content(name: string, description: string, body = "# Instructions"): string {
return `---\nname: ${name}\ndescription: ${description}\n---\n\n${body}\n`;

View File

@@ -246,6 +246,12 @@ export function mergeRemoteNodeSkillEntries(
);
}
export function resetRemoteNodeSkillsForTests(): void {
function resetRemoteNodeSkillsForTests(): void {
remoteSkillNodes.clear();
}
if (process.env.VITEST || process.env.NODE_ENV === "test") {
(globalThis as Record<PropertyKey, unknown>)[Symbol.for("openclaw.remoteNodeSkillsTestApi")] = {
resetRemoteNodeSkillsForTests,
};
}

View File

@@ -7,7 +7,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import type { NodeRegistry } from "../../gateway/node-registry.js";
import { getSkillsSnapshotVersion } from "./refresh-state.js";
import { resetSkillsRefreshForTest } from "./refresh.js";
import { resetSkillsRefreshForTest } from "./refresh.test-support.js";
import {
getRemoteSkillEligibility,
recordRemoteNodeBins,

View File

@@ -0,0 +1,41 @@
import type { SystemAgentTurnRunner } from "./agent-turn.js";
import "./agent-turn.js";
import type { SystemAgentVerifiedInferenceDeps } from "./verified-inference.js";
type SystemAgentRunEmbeddedAgent = (
params: Parameters<typeof import("../agents/embedded-agent.js").runEmbeddedAgent>[0] & {
systemAgentTool?: import("../agents/tools/system-agent-tool.js").SystemAgentToolOptions;
},
) => ReturnType<typeof import("../agents/embedded-agent.js").runEmbeddedAgent>;
type SystemAgentRunCliAgent = (
params: Parameters<typeof import("../agents/cli-runner.js").runCliAgent>[0] & {
systemAgentTool?: import("../agents/tools/system-agent-tool.js").SystemAgentToolOptions;
},
) => ReturnType<typeof import("../agents/cli-runner.js").runCliAgent>;
export type SystemAgentTurnDeps = SystemAgentVerifiedInferenceDeps & {
runEmbeddedAgent?: SystemAgentRunEmbeddedAgent;
runCliAgent?: SystemAgentRunCliAgent;
readConfigFileSnapshot?: typeof import("../config/config.js").readConfigFileSnapshot;
};
type SystemAgentTurnTestApi = {
runSystemAgentTurnWithDeps(
params: Parameters<SystemAgentTurnRunner>[0],
deps?: SystemAgentTurnDeps,
): ReturnType<SystemAgentTurnRunner>;
};
function getTestApi(): SystemAgentTurnTestApi {
return (globalThis as Record<PropertyKey, unknown>)[
Symbol.for("openclaw.systemAgentTurnTestApi")
] as SystemAgentTurnTestApi;
}
export function runSystemAgentTurnWithDeps(
params: Parameters<SystemAgentTurnRunner>[0],
deps: SystemAgentTurnDeps = {},
): ReturnType<SystemAgentTurnRunner> {
return getTestApi().runSystemAgentTurnWithDeps(params, deps);
}

View File

@@ -8,9 +8,9 @@ import type { CliBackendConfig, OpenClawConfig } from "../config/types.js";
import {
cleanupSystemAgentSession,
createSystemAgentSession,
runSystemAgentTurnWithDeps,
type SystemAgentSession,
} from "./agent-turn.js";
import { runSystemAgentTurnWithDeps, type SystemAgentTurnDeps } from "./agent-turn.test-support.js";
import { SystemAgentInferenceUnavailableError } from "./inference-error.js";
import { resolveSystemAgentConfiguredRouteFromConfig } from "./inference-route.js";
import { createSystemAgentVerifiedInferenceTestFixture } from "./system-agent.test-helpers.js";
@@ -29,7 +29,6 @@ vi.mock("../agents/harness/runtime-plugin.js", async (importOriginal) => ({
),
}));
type SystemAgentTurnDeps = NonNullable<Parameters<typeof runSystemAgentTurnWithDeps>[1]>;
type RunCliAgentParams = Parameters<NonNullable<SystemAgentTurnDeps["runCliAgent"]>>[0];
type RunEmbeddedAgentParams = Parameters<NonNullable<SystemAgentTurnDeps["runEmbeddedAgent"]>>[0];

View File

@@ -272,7 +272,7 @@ async function mirrorSystemAgentToolStateFromEvents(params: {
* output failures are typed so callers may try another inference path without
* mistaking the failure for deterministic setup authority.
*/
export async function runSystemAgentTurnWithDeps(
async function runSystemAgentTurnWithDeps(
params: SystemAgentTurnParams,
deps: SystemAgentTurnDeps = {},
): Promise<SystemAgentTurnReply | null> {
@@ -435,3 +435,9 @@ export async function runSystemAgentTurnWithDeps(
export const runSystemAgentTurn: SystemAgentTurnRunner = (params) =>
runSystemAgentTurnWithDeps(params);
if (process.env.VITEST || process.env.NODE_ENV === "test") {
(globalThis as Record<PropertyKey, unknown>)[Symbol.for("openclaw.systemAgentTurnTestApi")] = {
runSystemAgentTurnWithDeps,
};
}

View File

@@ -12,7 +12,7 @@ import {
import { hashSystemAgentOperation } from "../agents/tools/system-agent-tool.js";
import type { ConfigFileSnapshot, OpenClawConfig } from "../config/types.openclaw.js";
import type { WizardPrompter } from "../wizard/prompts.js";
import { runSystemAgentTurnWithDeps } from "./agent-turn.js";
import { runSystemAgentTurnWithDeps } from "./agent-turn.test-support.js";
import { classifySystemAgentApprovalText } from "./approval-intent.js";
import {
SystemAgentChatEngine as RuntimeSystemAgentChatEngine,

View File

@@ -32,11 +32,8 @@ import {
import { ensurePluginRegistryLoaded } from "../plugins/runtime/runtime-registry-loader.js";
import type { ProviderPlugin } from "../plugins/types.js";
import { disposeOpenClawAgentDatabaseByPath } from "../state/openclaw-agent-db.js";
import {
cleanupSystemAgentSession,
createSystemAgentSession,
runSystemAgentTurnWithDeps,
} from "./agent-turn.js";
import { cleanupSystemAgentSession, createSystemAgentSession } from "./agent-turn.js";
import { runSystemAgentTurnWithDeps } from "./agent-turn.test-support.js";
import { resolveSystemAgentConfiguredRouteFromConfig } from "./inference-route.js";
import { applySystemAgentModelSelection } from "./setup-apply.js";
import { resolveSetupInferenceProbeStreamParams } from "./setup-inference-probe.js";