refactor(config): consolidate regression fixtures (#114459)

This commit is contained in:
Peter Steinberger
2026-08-09 04:35:00 -07:00
committed by GitHub
parent b869d5e73f
commit 4cb33d48cd
6 changed files with 160 additions and 317 deletions
+19
View File
@@ -3,12 +3,31 @@ import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "./config.js";
import { resolveChannelGroupRequireMention, resolveToolsBySender } from "./group-policy.js";
import {
resolveScopeKeyCaseInsensitive,
resolveScopeIntroHint,
resolveScopeRequireMention,
resolveScopeToolsPolicy,
type ScopeTree,
} from "./group-scope-tree.js";
describe("resolveScopeKeyCaseInsensitive", () => {
it("preserves exact scope identity before case-insensitive fallback", () => {
const tree: ScopeTree = {
scopes: {
"Room:Mixed": { requireMention: true },
"room:mixed": { requireMention: false },
},
};
expect(resolveScopeKeyCaseInsensitive(tree, "Room:Mixed")).toBe("Room:Mixed");
expect(resolveScopeKeyCaseInsensitive(tree, "ROOM:MIXED")).toBe("Room:Mixed");
expect(resolveScopeKeyCaseInsensitive(tree, " room:mixed ")).toBe("Room:Mixed");
expect(resolveScopeKeyCaseInsensitive(tree, "unknown")).toBeUndefined();
expect(resolveScopeKeyCaseInsensitive(tree, undefined)).toBeUndefined();
expect(resolveScopeKeyCaseInsensitive(tree, null)).toBeUndefined();
});
});
describe("resolveScopeRequireMention", () => {
const scalarCases: Array<{
name: string;
+31 -93
View File
@@ -34,6 +34,30 @@ function applyWithApnChannelConfig(extra?: {
});
}
function materializeEnvCatalogCandidates(
stateDir: string,
candidates: Parameters<typeof materializePluginAutoEnableCandidates>[0]["candidates"] = [
{ pluginId: "env-primary", kind: "channel-configured", channelId: "env-primary" },
{ pluginId: "env-secondary", kind: "channel-configured", channelId: "env-secondary" },
],
) {
return materializePluginAutoEnableCandidates({
config: {
channels: {
"env-primary": { token: "primary" },
"env-secondary": { token: "secondary" },
},
},
candidates,
env: {
...makeIsolatedEnv(),
OPENCLAW_STATE_DIR: stateDir,
OPENCLAW_BUNDLED_PLUGINS_DIR: "/nonexistent/bundled/plugins",
},
manifestRegistry: makeRegistry([]),
});
}
beforeEach(() => {
resetPluginAutoEnableTestState();
});
@@ -73,32 +97,7 @@ describe("applyPluginAutoEnable channels", () => {
"utf-8",
);
const result = materializePluginAutoEnableCandidates({
config: {
channels: {
"env-primary": { token: "primary" },
"env-secondary": { token: "secondary" },
},
},
candidates: [
{
pluginId: "env-primary",
kind: "channel-configured",
channelId: "env-primary",
},
{
pluginId: "env-secondary",
kind: "channel-configured",
channelId: "env-secondary",
},
],
env: {
...makeIsolatedEnv(),
OPENCLAW_STATE_DIR: stateDir,
OPENCLAW_BUNDLED_PLUGINS_DIR: "/nonexistent/bundled/plugins",
},
manifestRegistry: makeRegistry([]),
});
const result = materializeEnvCatalogCandidates(stateDir);
expect(result.config.plugins?.entries?.["env-secondary"]?.enabled).toBe(true);
expect(result.config.plugins?.entries?.["env-primary"]).toBeUndefined();
@@ -151,25 +150,14 @@ describe("applyPluginAutoEnable channels", () => {
const realpathSpy = vi.spyOn(fs, "realpathSync");
try {
materializePluginAutoEnableCandidates({
config: {
channels: {
"env-primary": { token: "primary" },
"env-secondary": { token: "secondary" },
},
},
candidates: Array.from({ length: 20 }, (_, index) => ({
materializeEnvCatalogCandidates(
stateDir,
Array.from({ length: 20 }, (_, index) => ({
pluginId: index % 2 === 0 ? "env-primary" : "env-secondary",
kind: "channel-configured" as const,
channelId: index % 2 === 0 ? "env-primary" : "env-secondary",
})),
env: {
...makeIsolatedEnv(),
OPENCLAW_STATE_DIR: stateDir,
OPENCLAW_BUNDLED_PLUGINS_DIR: "/nonexistent/bundled/plugins",
},
manifestRegistry: makeRegistry([]),
});
);
expect(
realpathSpy.mock.calls.filter(([filePath]) =>
@@ -211,32 +199,7 @@ describe("applyPluginAutoEnable channels", () => {
const catalogPath = path.join(pluginsDir, "catalog.json");
fs.symlinkSync(realPath, catalogPath);
const result = materializePluginAutoEnableCandidates({
config: {
channels: {
"env-primary": { token: "primary" },
"env-secondary": { token: "secondary" },
},
},
candidates: [
{
pluginId: "env-primary",
kind: "channel-configured" as const,
channelId: "env-primary",
},
{
pluginId: "env-secondary",
kind: "channel-configured" as const,
channelId: "env-secondary",
},
],
env: {
...makeIsolatedEnv(),
OPENCLAW_STATE_DIR: stateDir,
OPENCLAW_BUNDLED_PLUGINS_DIR: "/nonexistent/bundled/plugins",
},
manifestRegistry: makeRegistry([]),
});
const result = materializeEnvCatalogCandidates(stateDir);
expect(result.config.plugins?.entries?.["env-secondary"]?.enabled).toBe(true);
expect(result.config.plugins?.entries?.["env-primary"]).toBeUndefined();
@@ -257,32 +220,7 @@ describe("applyPluginAutoEnable channels", () => {
fs.closeSync(fd);
}
const result = materializePluginAutoEnableCandidates({
config: {
channels: {
"env-primary": { token: "primary" },
"env-secondary": { token: "secondary" },
},
},
candidates: [
{
pluginId: "env-primary",
kind: "channel-configured" as const,
channelId: "env-primary",
},
{
pluginId: "env-secondary",
kind: "channel-configured" as const,
channelId: "env-secondary",
},
],
env: {
...makeIsolatedEnv(),
OPENCLAW_STATE_DIR: stateDir,
OPENCLAW_BUNDLED_PLUGINS_DIR: "/nonexistent/bundled/plugins",
},
manifestRegistry: makeRegistry([]),
});
const result = materializeEnvCatalogCandidates(stateDir);
// Selection continues: env-secondary is still auto-enabled.
expect(result.config.plugins?.entries?.["env-secondary"]?.enabled).toBe(true);
@@ -4,6 +4,7 @@ import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import {
decodeSessionArchiveBytes,
encodeSessionArchiveContent,
materializeSessionArchiveForRead,
readSessionArchiveContentSync,
@@ -30,6 +31,31 @@ function makeTempDir(): string {
}
describe("archive compression", () => {
it("decodes both plain bytes and runtime-supported compressed archive bytes", () => {
const content = `${JSON.stringify({ type: "message", body: "archive round trip" })}\n`;
const encoded = encodeSessionArchiveContent(content);
expect(decodeSessionArchiveBytes(Buffer.from(content, "utf8"), false)).toBe(content);
expect(decodeSessionArchiveBytes(encoded.bytes, encoded.suffix !== "")).toBe(content);
});
it("invalidates a materialized cache when its source archive is removed", () => {
const encoded = encodeSessionArchiveContent("cached archive contents\n");
if (encoded.suffix !== SESSION_ARCHIVE_ZSTD_SUFFIX) {
return;
}
const dir = makeTempDir();
const archivePath = path.join(dir, `removed.jsonl.deleted.2026-07-11${encoded.suffix}`);
fs.writeFileSync(archivePath, encoded.bytes);
const cachePath = materializeSessionArchiveForRead(archivePath);
expect(fs.existsSync(cachePath)).toBe(true);
fs.rmSync(archivePath);
expect(() => materializeSessionArchiveForRead(archivePath)).toThrow();
expect(fs.existsSync(cachePath)).toBe(false);
});
it("round-trips archived transcript content through encode and read", () => {
const content = `${JSON.stringify({ type: "message", body: "hello" })}\n`.repeat(200);
const encoded = encodeSessionArchiveContent(content);
+21 -65
View File
@@ -71,6 +71,14 @@ const buildEntry = (deliveryContext: DeliveryContext): SessionEntryFixture => ({
deliveryContext,
});
function createMixedCaseMatrixDelivery(): DeliveryContext {
return { channel: "matrix", to: "room:!MixedCase:Example.Org", accountId: "matrix-account" };
}
function createTelegramUserDelivery(): DeliveryContext {
return { channel: "telegram", to: "telegram:user-123", accountId: "default" };
}
beforeAll(async () => {
({ extractDeliveryInfo } = await import("./delivery-info.js"));
});
@@ -121,11 +129,7 @@ describe("extractDeliveryInfo", () => {
it("reads borrowed accessor views for direct session keys", () => {
const sessionKey = "agent:main:telegram:dm:user-123";
storeState.store[sessionKey] = buildEntry({
channel: "telegram",
to: "telegram:user-123",
accountId: "default",
});
storeState.store[sessionKey] = buildEntry(createTelegramUserDelivery());
const result = extractDeliveryInfo(sessionKey);
@@ -142,11 +146,7 @@ describe("extractDeliveryInfo", () => {
// extractDeliveryInfo would return no delivery context.
storeState.store = new Proxy(
{
[sessionKey]: buildEntry({
channel: "telegram",
to: "telegram:user-123",
accountId: "default",
}),
[sessionKey]: buildEntry(createTelegramUserDelivery()),
},
{
ownKeys() {
@@ -158,31 +158,19 @@ describe("extractDeliveryInfo", () => {
const result = extractDeliveryInfo(sessionKey);
expect(result).toEqual({
deliveryContext: {
channel: "telegram",
to: "telegram:user-123",
accountId: "default",
},
deliveryContext: createTelegramUserDelivery(),
threadId: undefined,
});
});
it("returns deliveryContext for direct session keys", () => {
const sessionKey = "agent:main:telegram:dm:user-123";
storeState.store[sessionKey] = buildEntry({
channel: "telegram",
to: "telegram:user-123",
accountId: "default",
});
storeState.store[sessionKey] = buildEntry(createTelegramUserDelivery());
const result = extractDeliveryInfo(sessionKey);
expect(result).toEqual({
deliveryContext: {
channel: "telegram",
to: "telegram:user-123",
accountId: "default",
},
deliveryContext: createTelegramUserDelivery(),
threadId: undefined,
});
});
@@ -366,11 +354,7 @@ describe("extractDeliveryInfo", () => {
storeState.store[sessionKey] = {
sessionId: "direct-routable-session",
updatedAt: Date.now() - 1_000,
deliveryContext: {
channel: "matrix",
to: "room:!MixedCase:Example.Org",
accountId: "matrix-account",
},
deliveryContext: createMixedCaseMatrixDelivery(),
};
storeState.store[canonicalKey] = {
sessionId: "fresh-normalized-session",
@@ -383,11 +367,7 @@ describe("extractDeliveryInfo", () => {
const result = extractDeliveryInfo(sessionKey);
expect(result).toEqual({
deliveryContext: {
channel: "matrix",
to: "room:!MixedCase:Example.Org",
accountId: "matrix-account",
},
deliveryContext: createMixedCaseMatrixDelivery(),
threadId: undefined,
});
});
@@ -492,11 +472,7 @@ describe("extractDeliveryInfo", () => {
storeState.store[queriedKey] = {
sessionId: "exact-mixedcase-session",
updatedAt: Date.now() - 1_000,
deliveryContext: {
channel: "matrix",
to: "room:!MixedCase:Example.Org",
accountId: "matrix-account",
},
deliveryContext: createMixedCaseMatrixDelivery(),
};
storeState.store[legacyFoldedKey] = {
sessionId: "fresher-legacy-folded-session",
@@ -511,11 +487,7 @@ describe("extractDeliveryInfo", () => {
const result = extractDeliveryInfo(queriedKey);
expect(result).toEqual({
deliveryContext: {
channel: "matrix",
to: "room:!MixedCase:Example.Org",
accountId: "matrix-account",
},
deliveryContext: createMixedCaseMatrixDelivery(),
threadId: undefined,
});
});
@@ -569,11 +541,7 @@ describe("extractDeliveryInfo", () => {
it("does not return a mixed-case Matrix sibling for a lowercase room query", () => {
const queriedKey = "agent:main:matrix:channel:!mixedcase:example.org";
const mixedSiblingKey = "agent:main:matrix:channel:!MixedCase:Example.Org";
storeState.store[mixedSiblingKey] = buildEntry({
channel: "matrix",
to: "room:!MixedCase:Example.Org",
accountId: "matrix-account",
});
storeState.store[mixedSiblingKey] = buildEntry(createMixedCaseMatrixDelivery());
const result = extractDeliveryInfo(queriedKey);
@@ -585,11 +553,7 @@ describe("extractDeliveryInfo", () => {
it("does not return an exact lowercase Matrix key with mixed-case delivery metadata", () => {
const queriedKey = "agent:main:matrix:channel:!mixedcase:example.org";
storeState.store[queriedKey] = buildEntry({
channel: "matrix",
to: "room:!MixedCase:Example.Org",
accountId: "matrix-account",
});
storeState.store[queriedKey] = buildEntry(createMixedCaseMatrixDelivery());
const result = extractDeliveryInfo(queriedKey);
@@ -602,20 +566,12 @@ describe("extractDeliveryInfo", () => {
it("returns a confirmed lowercased Matrix legacy artifact for a mixed-case key", () => {
const queriedKey = "agent:main:matrix:channel:!MixedCase:Example.Org";
const legacyArtifactKey = "agent:main:matrix:channel:!mixedcase:example.org";
storeState.store[legacyArtifactKey] = buildEntry({
channel: "matrix",
to: "room:!MixedCase:Example.Org",
accountId: "matrix-account",
});
storeState.store[legacyArtifactKey] = buildEntry(createMixedCaseMatrixDelivery());
const result = extractDeliveryInfo(queriedKey);
expect(result).toEqual({
deliveryContext: {
channel: "matrix",
to: "room:!MixedCase:Example.Org",
accountId: "matrix-account",
},
deliveryContext: createMixedCaseMatrixDelivery(),
threadId: undefined,
});
});
+39 -91
View File
@@ -368,61 +368,47 @@ describe("applyFileBackedSessionStoreMaintenance", () => {
}
});
it("preserves every active admission instead of only the writer session", async () => {
it.each([
{
name: "preserves every active admission instead of only the writer session",
storeName: "active-admissions",
preserved: [
["agent:main:cron:job:run:active", "active-session"],
["writer", "writer-session"],
],
identities: ["agent:main:cron:job:run:active", "active-session"],
activeSessionKey: "writer",
},
{
name: "preserves every store alias backed by an active session id",
storeName: "active-aliases",
preserved: [
["agent:main:cron:job:run:active", "active-alias-session"],
["agent:main:cron:job:run:active:thread:reply", "active-alias-session"],
],
identities: ["active-alias-session"],
activeSessionKey: undefined,
},
{
name: "preserves a raw legacy store key matched by a canonical admission identity",
storeName: "active-legacy-key",
preserved: [["Agent:Main:Subagent:CHILD", "active-legacy-session"]],
identities: ["agent:main:subagent:child"],
activeSessionKey: undefined,
},
] as const)("$name", async ({ storeName, preserved, identities, activeSessionKey }) => {
const now = Date.now();
const storePath = "/tmp/openclaw-sessions/active-admissions.json";
const activeKey = "agent:main:cron:job:run:active";
const storePath = `/tmp/openclaw-sessions/${storeName}.json`;
const store = makeStore([
[activeKey, { sessionId: "active-session", updatedAt: now - 3 }],
["removable", { sessionId: "removable-session", updatedAt: now - 2 }],
["writer", { sessionId: "writer-session", updatedAt: now - 1 }],
]);
const admission = await beginSessionWorkAdmission({
scope: storePath,
identities: [activeKey, "active-session"],
assertAllowed: () => {},
});
try {
await applyFileBackedSessionStoreMaintenance({
storePath,
store,
activeSessionKey: "writer",
maintenanceConfig: {
mode: "enforce",
pruneAfterMs: 30 * DAY_MS,
maxEntries: 1,
modelRunPruneAfterMs: DAY_MS,
resetArchiveRetentionMs: null,
maxDiskBytes: null,
highWaterBytes: null,
},
log: { warn: () => {}, info: () => {} },
artifacts: createMaintenanceArtifacts(),
});
expect(store).toHaveProperty(activeKey);
expect(store).toHaveProperty("writer");
expect(store.removable).toBeUndefined();
} finally {
admission.release();
}
});
it("preserves every store alias backed by an active session id", async () => {
const now = Date.now();
const storePath = "/tmp/openclaw-sessions/active-aliases.json";
const activeSessionId = "active-alias-session";
const firstAlias = "agent:main:cron:job:run:active";
const secondAlias = "agent:main:cron:job:run:active:thread:reply";
const store = makeStore([
[firstAlias, { sessionId: activeSessionId, updatedAt: now - 3 }],
[secondAlias, { sessionId: activeSessionId, updatedAt: now - 2 }],
...preserved.map(([key, sessionId], index): [string, SessionEntry] => [
key,
{ sessionId, updatedAt: now - preserved.length - 1 + index },
]),
["removable", { sessionId: "removable-session", updatedAt: now - 1 }],
]);
const admission = await beginSessionWorkAdmission({
scope: storePath,
identities: [activeSessionId],
identities: [...identities],
assertAllowed: () => {},
});
@@ -430,6 +416,7 @@ describe("applyFileBackedSessionStoreMaintenance", () => {
await applyFileBackedSessionStoreMaintenance({
storePath,
store,
activeSessionKey,
maintenanceConfig: {
mode: "enforce",
pruneAfterMs: 30 * DAY_MS,
@@ -442,48 +429,9 @@ describe("applyFileBackedSessionStoreMaintenance", () => {
log: { warn: () => {}, info: () => {} },
artifacts: createMaintenanceArtifacts(),
});
expect(store).toHaveProperty(firstAlias);
expect(store).toHaveProperty(secondAlias);
expect(store.removable).toBeUndefined();
} finally {
admission.release();
}
});
it("preserves a raw legacy store key matched by a canonical admission identity", async () => {
const now = Date.now();
const storePath = "/tmp/openclaw-sessions/active-legacy-key.json";
const rawActiveKey = "Agent:Main:Subagent:CHILD";
const canonicalActiveKey = "agent:main:subagent:child";
const store = makeStore([
[rawActiveKey, { sessionId: "active-legacy-session", updatedAt: now - 2 }],
["removable", { sessionId: "removable-session", updatedAt: now - 1 }],
]);
const admission = await beginSessionWorkAdmission({
scope: storePath,
identities: [canonicalActiveKey],
assertAllowed: () => {},
});
try {
await applyFileBackedSessionStoreMaintenance({
storePath,
store,
maintenanceConfig: {
mode: "enforce",
pruneAfterMs: 30 * DAY_MS,
maxEntries: 1,
modelRunPruneAfterMs: DAY_MS,
resetArchiveRetentionMs: null,
maxDiskBytes: null,
highWaterBytes: null,
},
log: { warn: () => {}, info: () => {} },
artifacts: createMaintenanceArtifacts(),
});
expect(store).toHaveProperty(rawActiveKey);
for (const [key] of preserved) {
expect(store).toHaveProperty(key);
}
expect(store.removable).toBeUndefined();
} finally {
admission.release();
+24 -68
View File
@@ -852,16 +852,27 @@ describe("validateConfigObjectRawWithPlugins channel metadata", () => {
}
});
it("names the external plugin owner for unsupported channel properties", () => {
mockLoadPluginManifestRegistry.mockReturnValue(createExternalFeishuSchemaRegistry());
it.each([
{
name: "names the external plugin owner for unsupported channel properties",
createRegistry: createExternalFeishuSchemaRegistry,
rejectedOwner: undefined,
},
{
name: "keeps unsupported property diagnostics assigned to the schema owner",
createRegistry: createExternalFeishuSchemaWithCloserMetadataRegistry,
rejectedOwner: "workspace-channel-labels",
},
{
name: "keeps schema ownership coupled when closer root metadata preserves a schema",
createRegistry: createExternalFeishuSchemaWithRootOnlyShadowRegistry,
rejectedOwner: "other-global-feishu",
},
] as const)("$name", ({ createRegistry, rejectedOwner }) => {
mockLoadPluginManifestRegistry.mockReturnValue(createRegistry());
const result = validateConfigObjectRawWithPlugins({
channels: {
feishu: {
appId: "app-id",
appSecret: "secret",
unsupportedField: true,
},
feishu: { appId: "app-id", appSecret: "secret", unsupportedField: true },
},
});
@@ -874,66 +885,11 @@ describe("validateConfigObjectRawWithPlugins channel metadata", () => {
'invalid config for plugin openclaw-lark: must not have additional properties: "unsupportedField"',
}),
);
}
});
it("keeps unsupported property diagnostics assigned to the schema owner", () => {
mockLoadPluginManifestRegistry.mockReturnValue(
createExternalFeishuSchemaWithCloserMetadataRegistry(),
);
const result = validateConfigObjectRawWithPlugins({
channels: {
feishu: {
appId: "app-id",
appSecret: "secret",
unsupportedField: true,
},
},
});
expect(result.ok).toBe(false);
if (!result.ok) {
expect(result.issues).toContainEqual(
expect.objectContaining({
path: "channels.feishu",
message:
'invalid config for plugin openclaw-lark: must not have additional properties: "unsupportedField"',
}),
);
expect(result.issues.map((issue) => issue.message)).not.toContain(
'invalid config for plugin workspace-channel-labels: must not have additional properties: "unsupportedField"',
);
}
});
it("keeps schema ownership coupled when closer root metadata preserves a schema", () => {
mockLoadPluginManifestRegistry.mockReturnValue(
createExternalFeishuSchemaWithRootOnlyShadowRegistry(),
);
const result = validateConfigObjectRawWithPlugins({
channels: {
feishu: {
appId: "app-id",
appSecret: "secret",
unsupportedField: true,
},
},
});
expect(result.ok).toBe(false);
if (!result.ok) {
expect(result.issues).toContainEqual(
expect.objectContaining({
path: "channels.feishu",
message:
'invalid config for plugin openclaw-lark: must not have additional properties: "unsupportedField"',
}),
);
expect(result.issues.map((issue) => issue.message)).not.toContain(
'invalid config for plugin other-global-feishu: must not have additional properties: "unsupportedField"',
);
if (rejectedOwner) {
expect(result.issues.map((issue) => issue.message)).not.toContain(
`invalid config for plugin ${rejectedOwner}: must not have additional properties: "unsupportedField"`,
);
}
}
});