mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 01:29:56 +00:00
test: align roster fixtures with Doctor migrations
This commit is contained in:
parent
cae2f937ce
commit
0c1714d2ae
8 changed files with 308 additions and 201 deletions
|
|
@ -8,7 +8,7 @@ import { performance } from "node:perf_hooks";
|
|||
import { setTimeout as delay } from "node:timers/promises";
|
||||
import { GatewayClient } from "openclaw/plugin-sdk/gateway-runtime";
|
||||
import type { OpenClawPluginNodeHostCommand } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { stopChildProcess } from "openclaw/plugin-sdk/test-env";
|
||||
import { createCanonicalAgentConfigFixture, stopChildProcess } from "openclaw/plugin-sdk/test-env";
|
||||
import { createOpenClawTestState } from "openclaw/plugin-sdk/test-state";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { createOllamaNodeHostCommands } from "./node-inference.js";
|
||||
|
|
@ -60,28 +60,30 @@ describe("Ollama paired-node Gateway inference", () => {
|
|||
let node: GatewayClient | undefined;
|
||||
|
||||
try {
|
||||
await state.writeConfig({
|
||||
gateway: {
|
||||
mode: "local",
|
||||
port: gatewayPort,
|
||||
bind: "loopback",
|
||||
auth: { mode: "token", token: gatewayToken },
|
||||
controlUi: { enabled: false },
|
||||
nodes: { commands: { allow: ["ollama.models", "ollama.chat"] } },
|
||||
},
|
||||
plugins: {
|
||||
allow: ["ollama"],
|
||||
},
|
||||
agents: {
|
||||
defaults: { heartbeat: { every: "0m" }, skipBootstrap: true },
|
||||
entries: { main: { default: true, tools: { allow: ["node_inference"] } } },
|
||||
},
|
||||
models: {
|
||||
providers: {
|
||||
ollama: { api: "ollama", baseUrl: gatewayOllama.baseUrl, models: [] },
|
||||
await state.writeConfig(
|
||||
createCanonicalAgentConfigFixture({
|
||||
gateway: {
|
||||
mode: "local",
|
||||
port: gatewayPort,
|
||||
bind: "loopback",
|
||||
auth: { mode: "token", token: gatewayToken },
|
||||
controlUi: { enabled: false },
|
||||
nodes: { commands: { allow: ["ollama.models", "ollama.chat"] } },
|
||||
},
|
||||
},
|
||||
});
|
||||
plugins: {
|
||||
allow: ["ollama"],
|
||||
},
|
||||
agents: {
|
||||
defaults: { heartbeat: { every: "0m" }, skipBootstrap: true },
|
||||
entries: { main: { default: true, tools: { allow: ["node_inference"] } } },
|
||||
},
|
||||
models: {
|
||||
providers: {
|
||||
ollama: { api: "ollama", baseUrl: gatewayOllama.baseUrl, models: [] },
|
||||
},
|
||||
},
|
||||
}).config,
|
||||
);
|
||||
|
||||
const gatewayEntryArgs =
|
||||
process.env.OPENCLAW_E2E_USE_PREBUILT_DIST === "1"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import fs from "node:fs/promises";
|
|||
import path from "node:path";
|
||||
import { readConfigFileSnapshot, resolveAgentWorkspaceDir } from "openclaw/plugin-sdk/health";
|
||||
import { resolveAgentRoute } from "openclaw/plugin-sdk/routing";
|
||||
import { withTempHome } from "openclaw/plugin-sdk/test-env";
|
||||
import { createCanonicalAgentConfigFixture, withTempHome } from "openclaw/plugin-sdk/test-env";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildQaGatewayConfig } from "../../qa-gateway-config.js";
|
||||
import { buildWhatsAppQaConfig } from "./whatsapp-live.config.js";
|
||||
|
|
@ -53,7 +53,25 @@ describe("WhatsApp QA broadcast config", () => {
|
|||
sutAccountId: "sut",
|
||||
});
|
||||
|
||||
await fs.writeFile(path.join(home, ".openclaw", "openclaw.json"), JSON.stringify(cfg));
|
||||
const configPath = path.join(home, ".openclaw", "openclaw.json");
|
||||
const authored = JSON.stringify(cfg);
|
||||
await fs.writeFile(configPath, authored);
|
||||
if (roster === "legacy-default") {
|
||||
const before = await readConfigFileSnapshot({
|
||||
pluginValidation: "core-only",
|
||||
observe: false,
|
||||
});
|
||||
expect(before.valid).toBe(false);
|
||||
expect(before.sourceConfig.agents?.entries?.main?.default).toBe(true);
|
||||
expect(await fs.readFile(configPath, "utf8")).toBe(authored);
|
||||
}
|
||||
const runtimeConfig = createCanonicalAgentConfigFixture(cfg, {
|
||||
homedir: () => home,
|
||||
}).config;
|
||||
const originalRuntimeConfig = createCanonicalAgentConfigFixture(original, {
|
||||
homedir: () => home,
|
||||
}).config;
|
||||
await fs.writeFile(configPath, JSON.stringify(runtimeConfig));
|
||||
const snapshot = await readConfigFileSnapshot({
|
||||
pluginValidation: "core-only",
|
||||
observe: false,
|
||||
|
|
@ -61,12 +79,12 @@ describe("WhatsApp QA broadcast config", () => {
|
|||
expect(snapshot.valid, JSON.stringify(snapshot.issues)).toBe(true);
|
||||
expect(cfg.agents?.defaults).toMatchObject(original.agents?.defaults ?? {});
|
||||
const route = { channel: "whatsapp", accountId: "sut" };
|
||||
expect(resolveAgentRoute({ cfg, ...route }).agentId).toBe(
|
||||
resolveAgentRoute({ cfg: original, ...route }).agentId,
|
||||
expect(resolveAgentRoute({ cfg: runtimeConfig, ...route }).agentId).toBe(
|
||||
resolveAgentRoute({ cfg: originalRuntimeConfig, ...route }).agentId,
|
||||
);
|
||||
for (const agentId of Object.keys(original.agents?.entries ?? {})) {
|
||||
expect(resolveAgentWorkspaceDir(cfg, agentId)).toBe(
|
||||
resolveAgentWorkspaceDir(original, agentId),
|
||||
expect(resolveAgentWorkspaceDir(runtimeConfig, agentId)).toBe(
|
||||
resolveAgentWorkspaceDir(originalRuntimeConfig, agentId),
|
||||
);
|
||||
}
|
||||
expect(cfg.agents?.entries).toMatchObject(original.agents?.entries ?? {});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,11 @@ import path from "node:path";
|
|||
import { createPluginRuntimeMock } from "openclaw/plugin-sdk/channel-test-helpers";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import { readConfigFileSnapshotForWrite } from "openclaw/plugin-sdk/config-mutation";
|
||||
import { withEnv, withTempHome } from "openclaw/plugin-sdk/test-env";
|
||||
import {
|
||||
createCanonicalAgentConfigFixture,
|
||||
withEnv,
|
||||
withTempHome,
|
||||
} from "openclaw/plugin-sdk/test-env";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
listEnabledTelegramAccounts,
|
||||
|
|
@ -183,7 +187,7 @@ describe("resolveDefaultTelegramAccountId", () => {
|
|||
expect(resolveDefaultTelegramAccountId(cfg)).toBe("work");
|
||||
});
|
||||
|
||||
it("preserves a loaded legacy owner's account until explicit fleet ownership is applied", async () => {
|
||||
it("uses explicit channel defaults after Doctor repairs a loaded legacy roster", async () => {
|
||||
await withTempHome(
|
||||
async (home) => {
|
||||
const config: OpenClawConfig = {
|
||||
|
|
@ -196,13 +200,25 @@ describe("resolveDefaultTelegramAccountId", () => {
|
|||
},
|
||||
bindings: [{ agentId: "main", match: { channel: "telegram", accountId: "work" } }],
|
||||
};
|
||||
await fs.writeFile(path.join(home, ".openclaw", "openclaw.json"), JSON.stringify(config));
|
||||
const configPath = path.join(home, ".openclaw", "openclaw.json");
|
||||
const original = JSON.stringify(config);
|
||||
await fs.writeFile(configPath, original);
|
||||
const { snapshot } = await readConfigFileSnapshotForWrite();
|
||||
|
||||
expect(snapshot.valid).toBe(true);
|
||||
expect(resolveDefaultTelegramAccountId(snapshot.config)).toBe("work");
|
||||
snapshot.config.agents!.ownership = "explicit";
|
||||
expect(resolveDefaultTelegramAccountId(snapshot.config)).toBe("alerts");
|
||||
expect(snapshot.valid).toBe(false);
|
||||
expect(snapshot.sourceConfig.agents?.entries?.main?.default).toBe(true);
|
||||
expect(snapshot.sourceConfig.bindings).toEqual(config.bindings);
|
||||
expect(await fs.readFile(configPath, "utf8")).toBe(original);
|
||||
|
||||
const { config: repaired } = createCanonicalAgentConfigFixture(snapshot.sourceConfig, {
|
||||
homedir: () => home,
|
||||
});
|
||||
expect(repaired.agents?.ownership).toBe("explicit");
|
||||
expect(repaired.agents?.entries?.main).not.toHaveProperty("default");
|
||||
expect(repaired.agents?.defaults?.systemAgent).toEqual({ agentId: "main" });
|
||||
expect(repaired.bindings).toContainEqual(config.bindings![0]);
|
||||
expect(resolveDefaultTelegramAccountId(repaired)).toBe("alerts");
|
||||
expect(await fs.readFile(configPath, "utf8")).toBe(original);
|
||||
},
|
||||
{
|
||||
env: {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
|||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import { closeOpenClawAgentDatabasesForTest } from "../../state/openclaw-agent-db.js";
|
||||
import { closeOpenClawStateDatabaseForTest } from "../../state/openclaw-state-db.js";
|
||||
import { runDoctorConfigPreflight } from "../doctor-config-preflight.js";
|
||||
import {
|
||||
applyOnboardingPrimaryModel,
|
||||
type OnboardingAgentTarget,
|
||||
|
|
@ -45,162 +46,227 @@ describe("local setup fleet owner persistence", () => {
|
|||
{ legacy: true, include: true },
|
||||
{ legacy: false, include: false },
|
||||
{ legacy: false, include: true },
|
||||
])("preserves a fleet through two real setup writes: %j", async ({ legacy, include }) => {
|
||||
const { ensureOnboardingAgent } =
|
||||
await vi.importActual<typeof import("../onboard-agent.js")>("../onboard-agent.js");
|
||||
mocks.ensureOnboardingAgent.mockImplementation(ensureOnboardingAgent);
|
||||
await withTempHome(async (rawHome) => {
|
||||
const home = await fs.realpath(rawHome);
|
||||
const stateDir = path.join(home, ".openclaw");
|
||||
const configPath = path.join(stateDir, "openclaw.json");
|
||||
const includePath = path.join(stateDir, "roster.json");
|
||||
const workspace = path.join(home, "existing-workspace");
|
||||
const siblingWorkspace = path.join(home, "alpha-workspace");
|
||||
const agentDir = path.join(stateDir, "agents", "beta", "agent");
|
||||
const entries = {
|
||||
alpha: { model: "fixture/alpha", workspace: siblingWorkspace },
|
||||
beta: { model: "fixture/beta", ...(!legacy ? { workspace } : {}), agentDir },
|
||||
};
|
||||
// The legacy default is deliberately neither the first nor lexical agent.
|
||||
const list = [
|
||||
{ id: "alpha", ...entries.alpha },
|
||||
{ id: "beta", default: true, ...entries.beta },
|
||||
];
|
||||
const roster = legacy ? { list } : { entries };
|
||||
const includeRaw = JSON.stringify(roster);
|
||||
if (include) {
|
||||
await fs.writeFile(includePath, includeRaw);
|
||||
}
|
||||
const bindings = [
|
||||
{ type: "route", agentId: "alpha", match: { channel: "discord", accountId: "*" } },
|
||||
];
|
||||
const raw = JSON.stringify({
|
||||
agents: {
|
||||
...(include ? { $include: "./roster.json" } : roster),
|
||||
...(!legacy ? { ownership: "explicit" } : {}),
|
||||
defaults: {
|
||||
workspace,
|
||||
model: "fixture/shared",
|
||||
...(!legacy ? { systemAgent: { agentId: "beta" } } : {}),
|
||||
])(
|
||||
"preserves a fleet through Doctor and two real setup writes: %j",
|
||||
async ({ legacy, include }) => {
|
||||
const { ensureOnboardingAgent } =
|
||||
await vi.importActual<typeof import("../onboard-agent.js")>("../onboard-agent.js");
|
||||
mocks.ensureOnboardingAgent.mockImplementation(ensureOnboardingAgent);
|
||||
await withTempHome(async (rawHome) => {
|
||||
const home = await fs.realpath(rawHome);
|
||||
const stateDir = path.join(home, ".openclaw");
|
||||
const configPath = path.join(stateDir, "openclaw.json");
|
||||
const includePath = path.join(stateDir, "roster.json");
|
||||
const workspace = path.join(home, "existing-workspace");
|
||||
const siblingWorkspace = path.join(home, "alpha-workspace");
|
||||
const agentDir = path.join(stateDir, "agents", "beta", "agent");
|
||||
const entries = {
|
||||
alpha: { model: "fixture/alpha", workspace: siblingWorkspace },
|
||||
beta: { model: "fixture/beta", ...(!legacy ? { workspace } : {}), agentDir },
|
||||
};
|
||||
// The legacy default is deliberately neither the first nor lexical agent.
|
||||
const list = [
|
||||
{ id: "alpha", ...entries.alpha },
|
||||
{ id: "beta", default: true, ...entries.beta },
|
||||
];
|
||||
const roster = legacy ? { list } : { entries };
|
||||
const includeRaw = JSON.stringify(roster);
|
||||
if (include) {
|
||||
await fs.writeFile(includePath, includeRaw);
|
||||
}
|
||||
const bindings = [
|
||||
{ type: "route", agentId: "alpha", match: { channel: "discord", accountId: "*" } },
|
||||
];
|
||||
const raw = JSON.stringify({
|
||||
agents: {
|
||||
...(include ? { $include: "./roster.json" } : roster),
|
||||
...(!legacy ? { ownership: "explicit" } : {}),
|
||||
defaults: {
|
||||
workspace,
|
||||
model: "fixture/shared",
|
||||
...(!legacy ? { systemAgent: { agentId: "beta" } } : {}),
|
||||
},
|
||||
},
|
||||
},
|
||||
bindings,
|
||||
gateway: { mode: "local", port: 24680 },
|
||||
});
|
||||
await fs.writeFile(configPath, raw);
|
||||
resetConfigRuntimeState();
|
||||
const before = await readConfigFileSnapshot();
|
||||
expect(before.valid, JSON.stringify(before.issues)).toBe(true);
|
||||
expect(before.sourceConfigBeforeMigrations?.agents).toMatchObject(roster);
|
||||
expect(before.sourceConfig.agents?.entries).toEqual(entries);
|
||||
expect(before.sourceConfig.agents?.list).toBeUndefined();
|
||||
expect(await fs.readFile(configPath, "utf8")).toBe(raw);
|
||||
const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() } as unknown as RuntimeEnv;
|
||||
const profileId = "fixture:setup";
|
||||
mocks.applyAuthChoice.mockImplementation(
|
||||
async ({
|
||||
nextConfig,
|
||||
target,
|
||||
}: {
|
||||
nextConfig: OpenClawConfig;
|
||||
target: OnboardingAgentTarget;
|
||||
}) => {
|
||||
expect(target).toEqual({ agentId: "beta", agentDir, workspaceDir: workspace });
|
||||
upsertAuthProfile({
|
||||
agentDir: target.agentDir,
|
||||
profileId,
|
||||
credential: { type: "api_key", provider: "fixture", key: "synthetic-setup-key" },
|
||||
});
|
||||
// Include-owned entries remain authored in their own file. Root-owned
|
||||
// model edits exercise the writer's existing ownership projection.
|
||||
return include
|
||||
? { ...nextConfig }
|
||||
: applyOnboardingPrimaryModel(nextConfig, target, "fixture/selected");
|
||||
},
|
||||
);
|
||||
|
||||
for (let rerun = 0; rerun < 2; rerun += 1) {
|
||||
await runNonInteractiveSetup(
|
||||
{
|
||||
nonInteractive: true,
|
||||
mode: "local",
|
||||
agentName: "ignored",
|
||||
workspace: path.join(home, "requested-workspace"),
|
||||
authChoice: "fixture-api-key",
|
||||
skipBootstrap: true,
|
||||
skipSkills: true,
|
||||
skipHealth: true,
|
||||
installDaemon: false,
|
||||
},
|
||||
runtime,
|
||||
);
|
||||
await expect(mocks.ensureOnboardingAgent.mock.results[rerun]?.value).resolves.toMatchObject(
|
||||
{
|
||||
agentId: "beta",
|
||||
createdAgent: false,
|
||||
},
|
||||
);
|
||||
expect(mocks.ensureWorkspaceAndSessions).toHaveBeenLastCalledWith(
|
||||
workspace,
|
||||
runtime,
|
||||
expect.objectContaining({ agentId: "beta", skipBootstrap: true }),
|
||||
);
|
||||
closeOpenClawAgentDatabasesForTest();
|
||||
closeOpenClawStateDatabaseForTest();
|
||||
resetConfigRuntimeState();
|
||||
const reopened = await readConfigFileSnapshot();
|
||||
expect(reopened.valid, JSON.stringify(reopened.issues)).toBe(true);
|
||||
const expectedEntries = include
|
||||
? entries
|
||||
: {
|
||||
...entries,
|
||||
beta: {
|
||||
...entries.beta,
|
||||
workspace,
|
||||
model: { primary: "fixture/selected" },
|
||||
models: { "fixture/selected": {} },
|
||||
bindings,
|
||||
gateway: { mode: "local", port: 24680 },
|
||||
models: {
|
||||
providers: {
|
||||
fixture: {
|
||||
api: "openai-completions",
|
||||
baseUrl: "https://fixture.example/v1",
|
||||
models: ["alpha", "beta", "shared", "selected"].map((id) => ({
|
||||
id,
|
||||
name: id,
|
||||
contextWindow: 8192,
|
||||
maxTokens: 1024,
|
||||
})),
|
||||
},
|
||||
};
|
||||
expect(reopened.sourceConfig.agents?.entries).toEqual(expectedEntries);
|
||||
expect(reopened.sourceConfig.agents?.defaults).toMatchObject({
|
||||
workspace,
|
||||
model: "fixture/shared",
|
||||
skipBootstrap: true,
|
||||
systemAgent: { agentId: "beta" },
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(reopened.sourceConfig.bindings).toEqual(bindings);
|
||||
expect(reopened.sourceConfig.gateway?.port).toBe(24680);
|
||||
expect(reopened.sourceConfig.hooks?.internal?.entries?.["session-memory"]?.enabled).toBe(
|
||||
true,
|
||||
);
|
||||
await fs.writeFile(configPath, raw);
|
||||
resetConfigRuntimeState();
|
||||
const before = await readConfigFileSnapshot();
|
||||
expect(before.valid, JSON.stringify(before.issues)).toBe(!legacy);
|
||||
expect(before.sourceConfig.agents).toMatchObject(roster);
|
||||
expect(await fs.readFile(configPath, "utf8")).toBe(raw);
|
||||
if (include) {
|
||||
expect(await fs.readFile(includePath, "utf8")).toBe(includeRaw);
|
||||
expect(JSON.parse(await fs.readFile(configPath, "utf8")).agents.$include).toBe(
|
||||
"./roster.json",
|
||||
}
|
||||
const canonicalEntries = {
|
||||
...entries,
|
||||
beta: { ...entries.beta, workspace },
|
||||
};
|
||||
let setupIncludeRaw = includeRaw;
|
||||
if (legacy) {
|
||||
if (include) {
|
||||
// Doctor needs the original list and owner marker when repair spans
|
||||
// an included roster and root-owned roles. Re-split only after repair.
|
||||
await fs.writeFile(configPath, JSON.stringify(before.sourceConfig));
|
||||
resetConfigRuntimeState();
|
||||
}
|
||||
await runDoctorConfigPreflight({
|
||||
observe: false,
|
||||
invocationPurpose: "doctor",
|
||||
repairPrefixedConfig: true,
|
||||
doctorOnlyStateMigrations: true,
|
||||
preparePluginMetadataSnapshot: true,
|
||||
});
|
||||
resetConfigRuntimeState();
|
||||
const repaired = await readConfigFileSnapshot();
|
||||
expect(repaired.valid, JSON.stringify(repaired.issues)).toBe(true);
|
||||
expect(repaired.sourceConfig.agents?.entries).toEqual(canonicalEntries);
|
||||
expect(repaired.sourceConfig.agents?.list).toBeUndefined();
|
||||
expect(repaired.sourceConfig.agents?.ownership).toBe("explicit");
|
||||
expect(repaired.sourceConfig.agents?.defaults?.systemAgent).toEqual({ agentId: "beta" });
|
||||
expect(repaired.sourceConfig.bindings).toEqual(bindings);
|
||||
if (include) {
|
||||
expect(await fs.readFile(includePath, "utf8")).toBe(includeRaw);
|
||||
setupIncludeRaw = JSON.stringify({ entries: canonicalEntries });
|
||||
await fs.writeFile(includePath, setupIncludeRaw);
|
||||
const agents = repaired.sourceConfig.agents;
|
||||
if (!agents) {
|
||||
throw new Error("Doctor did not retain the fleet");
|
||||
}
|
||||
const { entries: _entries, ...rootOwnedAgents } = agents;
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
JSON.stringify({
|
||||
...repaired.sourceConfig,
|
||||
agents: { $include: "./roster.json", ...rootOwnedAgents },
|
||||
}),
|
||||
);
|
||||
resetConfigRuntimeState();
|
||||
expect((await readConfigFileSnapshot()).valid).toBe(true);
|
||||
}
|
||||
}
|
||||
const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() } as unknown as RuntimeEnv;
|
||||
const profileId = "fixture:setup";
|
||||
mocks.applyAuthChoice.mockImplementation(
|
||||
async ({
|
||||
nextConfig,
|
||||
target,
|
||||
}: {
|
||||
nextConfig: OpenClawConfig;
|
||||
target: OnboardingAgentTarget;
|
||||
}) => {
|
||||
expect(target).toEqual({ agentId: "beta", agentDir, workspaceDir: workspace });
|
||||
upsertAuthProfile({
|
||||
agentDir: target.agentDir,
|
||||
profileId,
|
||||
credential: { type: "api_key", provider: "fixture", key: "synthetic-setup-key" },
|
||||
});
|
||||
// Include-owned entries remain authored in their own file. Root-owned
|
||||
// model edits exercise the writer's existing ownership projection.
|
||||
return include
|
||||
? { ...nextConfig }
|
||||
: applyOnboardingPrimaryModel(nextConfig, target, "fixture/selected");
|
||||
},
|
||||
);
|
||||
|
||||
for (let rerun = 0; rerun < 2; rerun += 1) {
|
||||
await runNonInteractiveSetup(
|
||||
{
|
||||
nonInteractive: true,
|
||||
mode: "local",
|
||||
agentName: "ignored",
|
||||
workspace: path.join(home, "requested-workspace"),
|
||||
authChoice: "fixture-api-key",
|
||||
skipBootstrap: true,
|
||||
skipSkills: true,
|
||||
skipHealth: true,
|
||||
installDaemon: false,
|
||||
},
|
||||
runtime,
|
||||
);
|
||||
} else {
|
||||
const persisted = JSON.parse(await fs.readFile(configPath, "utf8"));
|
||||
expect(persisted.agents.entries).toEqual(expectedEntries);
|
||||
expect(persisted.agents.list).toBeUndefined();
|
||||
expect(persisted.agents.ownership).toBe("explicit");
|
||||
expect(persisted.agents.defaults.systemAgent).toEqual({ agentId: "beta" });
|
||||
}
|
||||
expect(
|
||||
loadAuthProfileStoreWithoutExternalProfiles(agentDir).profiles[profileId],
|
||||
).toMatchObject({ provider: "fixture" });
|
||||
for (const nonOwner of ["alpha", "ignored"]) {
|
||||
await expect(
|
||||
mocks.ensureOnboardingAgent.mock.results[rerun]?.value,
|
||||
).resolves.toMatchObject({
|
||||
agentId: "beta",
|
||||
createdAgent: false,
|
||||
});
|
||||
expect(mocks.ensureWorkspaceAndSessions).toHaveBeenLastCalledWith(
|
||||
workspace,
|
||||
runtime,
|
||||
expect.objectContaining({ agentId: "beta", skipBootstrap: true }),
|
||||
);
|
||||
closeOpenClawAgentDatabasesForTest();
|
||||
closeOpenClawStateDatabaseForTest();
|
||||
resetConfigRuntimeState();
|
||||
const reopened = await readConfigFileSnapshot();
|
||||
expect(reopened.valid, JSON.stringify(reopened.issues)).toBe(true);
|
||||
const expectedEntries = include
|
||||
? canonicalEntries
|
||||
: {
|
||||
...canonicalEntries,
|
||||
beta: {
|
||||
...entries.beta,
|
||||
workspace,
|
||||
model: { primary: "fixture/selected" },
|
||||
models: { "fixture/selected": {} },
|
||||
},
|
||||
};
|
||||
expect(reopened.sourceConfig.agents?.entries).toEqual(expectedEntries);
|
||||
expect(reopened.sourceConfig.agents?.defaults).toMatchObject({
|
||||
workspace,
|
||||
model: "fixture/shared",
|
||||
skipBootstrap: true,
|
||||
systemAgent: { agentId: "beta" },
|
||||
});
|
||||
expect(reopened.sourceConfig.bindings).toEqual(bindings);
|
||||
expect(reopened.sourceConfig.gateway?.port).toBe(24680);
|
||||
expect(reopened.sourceConfig.hooks?.internal?.entries?.["session-memory"]?.enabled).toBe(
|
||||
true,
|
||||
);
|
||||
if (include) {
|
||||
expect(await fs.readFile(includePath, "utf8")).toBe(setupIncludeRaw);
|
||||
expect(JSON.parse(await fs.readFile(configPath, "utf8")).agents.$include).toBe(
|
||||
"./roster.json",
|
||||
);
|
||||
} else {
|
||||
const persisted = JSON.parse(await fs.readFile(configPath, "utf8"));
|
||||
expect(persisted.agents.entries).toEqual(expectedEntries);
|
||||
expect(persisted.agents.list).toBeUndefined();
|
||||
expect(persisted.agents.ownership).toBe("explicit");
|
||||
expect(persisted.agents.defaults.systemAgent).toEqual({ agentId: "beta" });
|
||||
}
|
||||
expect(
|
||||
loadAuthProfileStoreWithoutExternalProfiles(
|
||||
path.join(stateDir, "agents", nonOwner, "agent"),
|
||||
).profiles[profileId],
|
||||
`credentials must not be stored for ${nonOwner}`,
|
||||
).toBeUndefined();
|
||||
loadAuthProfileStoreWithoutExternalProfiles(agentDir).profiles[profileId],
|
||||
).toMatchObject({ provider: "fixture" });
|
||||
for (const nonOwner of ["alpha", "ignored"]) {
|
||||
expect(
|
||||
loadAuthProfileStoreWithoutExternalProfiles(
|
||||
path.join(stateDir, "agents", nonOwner, "agent"),
|
||||
).profiles[profileId],
|
||||
`credentials must not be stored for ${nonOwner}`,
|
||||
).toBeUndefined();
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(runtime.exit).not.toHaveBeenCalled();
|
||||
expect(runtime.error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("existing agents keep their current workspace"),
|
||||
);
|
||||
});
|
||||
});
|
||||
expect(runtime.exit).not.toHaveBeenCalled();
|
||||
expect(runtime.error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("existing agents keep their current workspace"),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export {
|
|||
export { createWindowsCmdShimFixture } from "../test-helpers/windows-cmd-shim.js";
|
||||
export { createProviderUsageFetch, makeResponse } from "../test-utils/provider-usage-fetch.js";
|
||||
export { useIsolatedStateGuard } from "../test-utils/state-path-guard.js";
|
||||
export { createCanonicalAgentConfigFixture } from "../test-utils/config-roster.js";
|
||||
export { withStateDirEnv } from "../test-helpers/state-dir-env.js";
|
||||
export { captureEnv, withEnv, withEnvAsync } from "../test-utils/env.js";
|
||||
export { withFetchPreconnect, type FetchMock } from "../test-utils/fetch-mock.js";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import "../src/test-utils/prepare-compiled-subprocesses.js";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { withTempHome } from "openclaw/plugin-sdk/test-env";
|
||||
import { createCanonicalAgentConfigFixture, withTempHome } from "openclaw/plugin-sdk/test-env";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { runBuiltCli } from "./cli-json-stdout.test-support.js";
|
||||
|
||||
|
|
@ -184,18 +184,20 @@ describe("cli json stdout contract", () => {
|
|||
await withTempHome(
|
||||
async (tempHome) => {
|
||||
const configPath = path.join(tempHome, "openclaw.json");
|
||||
const existingConfig = `${JSON.stringify({
|
||||
agents: {
|
||||
ownership: "explicit",
|
||||
list: [
|
||||
{ id: "main", workspace: path.join(tempHome, "main") },
|
||||
{ id: "ops", workspace: path.join(tempHome, "ops") },
|
||||
const existingConfig = `${JSON.stringify(
|
||||
createCanonicalAgentConfigFixture({
|
||||
agents: {
|
||||
ownership: "explicit",
|
||||
list: [
|
||||
{ id: "main", workspace: path.join(tempHome, "main") },
|
||||
{ id: "ops", workspace: path.join(tempHome, "ops") },
|
||||
],
|
||||
},
|
||||
bindings: [
|
||||
{ type: "route", agentId: "ops", match: { channel: "telegram", accountId: "work" } },
|
||||
],
|
||||
},
|
||||
bindings: [
|
||||
{ type: "route", agentId: "ops", match: { channel: "telegram", accountId: "work" } },
|
||||
],
|
||||
})}\n`;
|
||||
}).config,
|
||||
)}\n`;
|
||||
if ("conflict" in testCase) {
|
||||
await fs.writeFile(configPath, existingConfig, "utf8");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import {
|
|||
restoreActivePluginRegistrySnapshot,
|
||||
setActivePluginRegistry,
|
||||
} from "../../../../src/plugins/runtime.js";
|
||||
import { createCanonicalAgentConfigFixture } from "../../../../src/test-utils/config-roster.js";
|
||||
import {
|
||||
GATEWAY_CLIENT_MODES,
|
||||
GATEWAY_CLIENT_NAMES,
|
||||
|
|
@ -113,7 +114,7 @@ describe("Canvas agent tool over a paired macOS node", () => {
|
|||
// Released here until the started Gateway owns the claim.
|
||||
let unstartedPortClaim: typeof portClaim | undefined = portClaim;
|
||||
const gatewayToken = "qa-canvas-agent-node-token";
|
||||
const config: OpenClawConfig = {
|
||||
const config: OpenClawConfig = createCanonicalAgentConfigFixture({
|
||||
gateway: {
|
||||
mode: "local",
|
||||
port,
|
||||
|
|
@ -129,7 +130,7 @@ describe("Canvas agent tool over a paired macOS node", () => {
|
|||
allow: ["canvas"],
|
||||
entries: { canvas: { enabled: true } },
|
||||
},
|
||||
};
|
||||
}).config;
|
||||
await state.writeConfig(config);
|
||||
|
||||
const invocations: CapturedInvocation[] = [];
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
disconnectGatewayClient,
|
||||
} from "../../../../src/gateway/test-helpers.e2e.js";
|
||||
import { loadOrCreateDeviceIdentity } from "../../../../src/infra/device-identity.js";
|
||||
import { createCanonicalAgentConfigFixture } from "../../../../src/test-utils/config-roster.js";
|
||||
import { stopQaGatewayFixture } from "../../../helpers/qa-gateway-cleanup.js";
|
||||
import { stopChildProcess } from "../../../helpers/stop-child-process.js";
|
||||
|
||||
|
|
@ -130,7 +131,7 @@ describe("node workspace document access", () => {
|
|||
});
|
||||
const nodeId = nodeIdentity.deviceId;
|
||||
const attachmentFixture = await writeAttachmentFixture(state.root);
|
||||
const config: OpenClawConfig = {
|
||||
const config: OpenClawConfig = createCanonicalAgentConfigFixture({
|
||||
gateway: {
|
||||
mode: "local",
|
||||
bind: "loopback",
|
||||
|
|
@ -176,7 +177,7 @@ describe("node workspace document access", () => {
|
|||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}).config;
|
||||
const gatewayOwner = createQaGatewayChild();
|
||||
let owner: GatewayClient | undefined;
|
||||
let reader: GatewayClient | undefined;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue