test: move spawn and doctor coverage to owners

This commit is contained in:
Peter Steinberger
2026-04-11 07:54:19 +01:00
parent 32b252cabf
commit 7273cae36b
4 changed files with 93 additions and 109 deletions

View File

@@ -96,6 +96,47 @@ describe("discord doctor", () => {
).toEqual(["Moved channels.discord.streamMode → channels.discord.streaming.mode (block)."]);
});
it("moves account voice.tts.edge into providers.microsoft", () => {
const normalize = discordDoctor.normalizeCompatibilityConfig;
expect(normalize).toBeDefined();
if (!normalize) {
return;
}
const result = normalize({
cfg: {
channels: {
discord: {
accounts: {
main: {
voice: {
tts: {
edge: {
voice: "en-US-JennyNeural",
},
},
},
},
},
},
},
} as never,
});
expect(result.changes).toContain(
"Moved channels.discord.accounts.main.voice.tts.edge → channels.discord.accounts.main.voice.tts.providers.microsoft.",
);
const mainTts = result.config.channels?.discord?.accounts?.main?.voice?.tts as
| Record<string, unknown>
| undefined;
expect(mainTts?.providers).toEqual({
microsoft: {
voice: "en-US-JennyNeural",
},
});
expect(mainTts?.edge).toBeUndefined();
});
it("finds numeric id entries across discord scopes", () => {
const cfg = {
channels: {

View File

@@ -1,71 +0,0 @@
import { beforeEach, describe, expect, it } from "vitest";
import "./test-helpers/fast-core-tools.js";
import {
getCallGatewayMock,
getSessionsSpawnTool,
setSessionsSpawnConfigOverride,
} from "./openclaw-tools.subagents.sessions-spawn.test-harness.js";
import { subagentRuns } from "./subagent-registry-memory.js";
import { listRunsForRequesterFromRuns } from "./subagent-registry-queries.js";
function listSubagentRunsForRequester(requesterSessionKey: string) {
return listRunsForRequesterFromRuns(subagentRuns, requesterSessionKey);
}
describe("sessions_spawn requesterOrigin threading", () => {
const spawnAndReadRequesterRun = async (opts?: { agentThreadId?: number }) => {
const tool = await getSessionsSpawnTool({
agentSessionKey: "main",
agentChannel: "telegram",
agentTo: "telegram:123",
...(opts?.agentThreadId === undefined ? {} : { agentThreadId: opts.agentThreadId }),
});
const result = await tool.execute("call", {
task: "do thing",
runTimeoutSeconds: 1,
});
expect(result.details).toMatchObject({ status: "accepted", runId: "run-1" });
const runs = listSubagentRunsForRequester("main");
expect(runs).toHaveLength(1);
return runs[0];
};
beforeEach(() => {
const callGatewayMock = getCallGatewayMock();
subagentRuns.clear();
callGatewayMock.mockClear();
setSessionsSpawnConfigOverride({
session: {
mainKey: "main",
scope: "per-sender",
},
});
callGatewayMock.mockImplementation(async (opts: unknown) => {
const req = opts as { method?: string };
if (req.method === "agent") {
return { runId: "run-1", status: "accepted", acceptedAt: 1 };
}
// Prevent background announce flow by returning a non-terminal status.
if (req.method === "agent.wait") {
return { runId: "run-1", status: "running" };
}
return {};
});
});
it("captures threadId in requesterOrigin", async () => {
const run = await spawnAndReadRequesterRun({ agentThreadId: 42 });
expect(run?.requesterOrigin).toMatchObject({
channel: "telegram",
to: "telegram:123",
threadId: 42,
});
});
it("stores requesterOrigin without threadId when none is provided", async () => {
const run = await spawnAndReadRequesterRun();
expect(run?.requesterOrigin?.threadId).toBeUndefined();
});
});

View File

@@ -107,6 +107,7 @@ describe("spawnSubagentDirect seam flow", () => {
agentChannel: "discord",
agentAccountId: "acct-1",
agentTo: "user-1",
agentThreadId: 42,
workspaceDir: "/tmp/requester-workspace",
},
);
@@ -132,7 +133,7 @@ describe("spawnSubagentDirect seam flow", () => {
channel: "discord",
accountId: "acct-1",
to: "user-1",
threadId: undefined,
threadId: 42,
},
task: "inspect the spawn seam",
cleanup: "keep",
@@ -162,6 +163,44 @@ describe("spawnSubagentDirect seam flow", () => {
expect(operations.indexOf("gateway:agent")).toBeGreaterThan(operations.indexOf("store:update"));
});
it("omits requesterOrigin threadId when no requester thread is provided", async () => {
hoisted.callGatewayMock.mockImplementation(async (request: { method?: string }) => {
if (request.method === "agent") {
return { runId: "run-1" };
}
if (request.method?.startsWith("sessions.")) {
return { ok: true };
}
return {};
});
installSessionStoreCaptureMock(hoisted.updateSessionStoreMock);
const result = await spawnSubagentDirect(
{
task: "inspect unthreaded spawn",
model: "openai-codex/gpt-5.4",
},
{
agentSessionKey: "agent:main:main",
agentChannel: "discord",
agentAccountId: "acct-1",
agentTo: "user-1",
},
);
expect(result.status).toBe("accepted");
expect(hoisted.registerSubagentRunMock).toHaveBeenCalledWith(
expect.objectContaining({
requesterOrigin: expect.objectContaining({
channel: "discord",
accountId: "acct-1",
to: "user-1",
threadId: undefined,
}),
}),
);
});
it("pins admin-only methods to operator.admin and preserves least-privilege for others (#59428)", async () => {
const capturedCalls: Array<{ method?: string; scopes?: string[] }> = [];

View File

@@ -1,12 +1,22 @@
import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../../../config/types.js";
import { applyLegacyDoctorMigrations } from "./legacy-config-compat.js";
import { LEGACY_CONFIG_MIGRATIONS_RUNTIME_TTS } from "./legacy-config-migrations.runtime.tts.js";
function migrateLegacyConfig(raw: unknown): {
config: OpenClawConfig | null;
changes: string[];
} {
const { next, changes } = applyLegacyDoctorMigrations(raw);
if (!raw || typeof raw !== "object") {
return { config: null, changes: [] };
}
const next = structuredClone(raw) as Record<string, unknown>;
const changes: string[] = [];
for (const migration of LEGACY_CONFIG_MIGRATIONS_RUNTIME_TTS) {
migration.apply(next, changes);
}
if (changes.length === 0) {
return { config: null, changes };
}
return { config: next as OpenClawConfig | null, changes };
}
@@ -38,41 +48,6 @@ describe("legacy migrate provider-shaped config", () => {
});
});
it("moves channels.discord.accounts.<id>.voice.tts.edge into providers.microsoft", () => {
const res = migrateLegacyConfig({
channels: {
discord: {
accounts: {
main: {
voice: {
tts: {
edge: {
voice: "en-US-JennyNeural",
},
},
},
},
},
},
},
});
expect(res.changes).toContain(
"Moved channels.discord.accounts.main.voice.tts.edge → channels.discord.accounts.main.voice.tts.providers.microsoft.",
);
const mainTts = (
res.config?.channels?.discord?.accounts as
| Record<string, { voice?: { tts?: Record<string, unknown> } }>
| undefined
)?.main?.voice?.tts;
expect(mainTts?.providers).toEqual({
microsoft: {
voice: "en-US-JennyNeural",
},
});
expect(mainTts?.edge).toBeUndefined();
});
it("moves plugins.entries.voice-call.config.tts.<provider> keys into providers", () => {
const res = migrateLegacyConfig({
plugins: {