mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-11 08:11:42 +00:00
test: table-drive utils and channel-match cases
This commit is contained in:
@@ -42,44 +42,44 @@ describe("resolveChannelEntryMatch", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("resolveChannelEntryMatchWithFallback", () => {
|
describe("resolveChannelEntryMatchWithFallback", () => {
|
||||||
it("prefers direct matches over parent and wildcard", () => {
|
const fallbackCases = [
|
||||||
const entries = { a: { allow: true }, parent: { allow: false }, "*": { allow: false } };
|
{
|
||||||
const match = resolveChannelEntryMatchWithFallback({
|
name: "prefers direct matches over parent and wildcard",
|
||||||
entries,
|
entries: { a: { allow: true }, parent: { allow: false }, "*": { allow: false } },
|
||||||
keys: ["a"],
|
args: { keys: ["a"], parentKeys: ["parent"], wildcardKey: "*" },
|
||||||
parentKeys: ["parent"],
|
expectedEntryKey: "a",
|
||||||
wildcardKey: "*",
|
expectedSource: "direct",
|
||||||
});
|
expectedMatchKey: "a",
|
||||||
expect(match.entry).toBe(entries.a);
|
},
|
||||||
expect(match.matchSource).toBe("direct");
|
{
|
||||||
expect(match.matchKey).toBe("a");
|
name: "falls back to parent when direct misses",
|
||||||
});
|
entries: { parent: { allow: false }, "*": { allow: true } },
|
||||||
|
args: { keys: ["missing"], parentKeys: ["parent"], wildcardKey: "*" },
|
||||||
|
expectedEntryKey: "parent",
|
||||||
|
expectedSource: "parent",
|
||||||
|
expectedMatchKey: "parent",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "falls back to wildcard when no direct or parent match",
|
||||||
|
entries: { "*": { allow: true } },
|
||||||
|
args: { keys: ["missing"], parentKeys: ["still-missing"], wildcardKey: "*" },
|
||||||
|
expectedEntryKey: "*",
|
||||||
|
expectedSource: "wildcard",
|
||||||
|
expectedMatchKey: "*",
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
it("falls back to parent when direct misses", () => {
|
for (const testCase of fallbackCases) {
|
||||||
const entries = { parent: { allow: false }, "*": { allow: true } };
|
it(testCase.name, () => {
|
||||||
const match = resolveChannelEntryMatchWithFallback({
|
const match = resolveChannelEntryMatchWithFallback({
|
||||||
entries,
|
entries: testCase.entries,
|
||||||
keys: ["missing"],
|
...testCase.args,
|
||||||
parentKeys: ["parent"],
|
});
|
||||||
wildcardKey: "*",
|
expect(match.entry).toBe(testCase.entries[testCase.expectedEntryKey]);
|
||||||
|
expect(match.matchSource).toBe(testCase.expectedSource);
|
||||||
|
expect(match.matchKey).toBe(testCase.expectedMatchKey);
|
||||||
});
|
});
|
||||||
expect(match.entry).toBe(entries.parent);
|
}
|
||||||
expect(match.matchSource).toBe("parent");
|
|
||||||
expect(match.matchKey).toBe("parent");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("falls back to wildcard when no direct or parent match", () => {
|
|
||||||
const entries = { "*": { allow: true } };
|
|
||||||
const match = resolveChannelEntryMatchWithFallback({
|
|
||||||
entries,
|
|
||||||
keys: ["missing"],
|
|
||||||
parentKeys: ["still-missing"],
|
|
||||||
wildcardKey: "*",
|
|
||||||
});
|
|
||||||
expect(match.entry).toBe(entries["*"]);
|
|
||||||
expect(match.matchSource).toBe("wildcard");
|
|
||||||
expect(match.matchKey).toBe("*");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("matches normalized keys when normalizeKey is provided", () => {
|
it("matches normalized keys when normalizeKey is provided", () => {
|
||||||
const entries = { "My Team": { allow: true } };
|
const entries = { "My Team": { allow: true } };
|
||||||
@@ -153,44 +153,52 @@ describe("validateSenderIdentity", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("resolveNestedAllowlistDecision", () => {
|
describe("resolveNestedAllowlistDecision", () => {
|
||||||
it("allows when outer allowlist is disabled", () => {
|
const cases = [
|
||||||
expect(
|
{
|
||||||
resolveNestedAllowlistDecision({
|
name: "allows when outer allowlist is disabled",
|
||||||
|
value: {
|
||||||
outerConfigured: false,
|
outerConfigured: false,
|
||||||
outerMatched: false,
|
outerMatched: false,
|
||||||
innerConfigured: false,
|
innerConfigured: false,
|
||||||
innerMatched: false,
|
innerMatched: false,
|
||||||
}),
|
},
|
||||||
).toBe(true);
|
expected: true,
|
||||||
});
|
},
|
||||||
|
{
|
||||||
it("blocks when outer allowlist is configured but missing match", () => {
|
name: "blocks when outer allowlist is configured but missing match",
|
||||||
expect(
|
value: {
|
||||||
resolveNestedAllowlistDecision({
|
|
||||||
outerConfigured: true,
|
outerConfigured: true,
|
||||||
outerMatched: false,
|
outerMatched: false,
|
||||||
innerConfigured: false,
|
innerConfigured: false,
|
||||||
innerMatched: false,
|
innerMatched: false,
|
||||||
}),
|
},
|
||||||
).toBe(false);
|
expected: false,
|
||||||
});
|
},
|
||||||
|
{
|
||||||
it("requires inner match when inner allowlist is configured", () => {
|
name: "requires inner match when inner allowlist is configured",
|
||||||
expect(
|
value: {
|
||||||
resolveNestedAllowlistDecision({
|
|
||||||
outerConfigured: true,
|
outerConfigured: true,
|
||||||
outerMatched: true,
|
outerMatched: true,
|
||||||
innerConfigured: true,
|
innerConfigured: true,
|
||||||
innerMatched: false,
|
innerMatched: false,
|
||||||
}),
|
},
|
||||||
).toBe(false);
|
expected: false,
|
||||||
expect(
|
},
|
||||||
resolveNestedAllowlistDecision({
|
{
|
||||||
|
name: "allows when both outer and inner allowlists match",
|
||||||
|
value: {
|
||||||
outerConfigured: true,
|
outerConfigured: true,
|
||||||
outerMatched: true,
|
outerMatched: true,
|
||||||
innerConfigured: true,
|
innerConfigured: true,
|
||||||
innerMatched: true,
|
innerMatched: true,
|
||||||
}),
|
},
|
||||||
).toBe(true);
|
expected: true,
|
||||||
});
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
for (const testCase of cases) {
|
||||||
|
it(testCase.name, () => {
|
||||||
|
expect(resolveNestedAllowlistDecision(testCase.value)).toBe(testCase.expected);
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,52 +2,64 @@ import { describe, expect, it } from "vitest";
|
|||||||
import { resolveReactionLevel } from "./reaction-level.js";
|
import { resolveReactionLevel } from "./reaction-level.js";
|
||||||
|
|
||||||
describe("resolveReactionLevel", () => {
|
describe("resolveReactionLevel", () => {
|
||||||
it("defaults when value is missing", () => {
|
const cases = [
|
||||||
expect(
|
{
|
||||||
resolveReactionLevel({ value: undefined, defaultLevel: "minimal", invalidFallback: "ack" }),
|
name: "defaults when value is missing",
|
||||||
).toEqual({
|
input: {
|
||||||
level: "minimal",
|
value: undefined,
|
||||||
ackEnabled: false,
|
defaultLevel: "minimal" as const,
|
||||||
agentReactionsEnabled: true,
|
invalidFallback: "ack" as const,
|
||||||
agentReactionGuidance: "minimal",
|
},
|
||||||
});
|
expected: {
|
||||||
});
|
level: "minimal",
|
||||||
|
ackEnabled: false,
|
||||||
it("supports ack", () => {
|
agentReactionsEnabled: true,
|
||||||
expect(
|
agentReactionGuidance: "minimal",
|
||||||
resolveReactionLevel({ value: "ack", defaultLevel: "minimal", invalidFallback: "ack" }),
|
},
|
||||||
).toEqual({ level: "ack", ackEnabled: true, agentReactionsEnabled: false });
|
},
|
||||||
});
|
{
|
||||||
|
name: "supports ack",
|
||||||
it("supports extensive", () => {
|
input: { value: "ack", defaultLevel: "minimal" as const, invalidFallback: "ack" as const },
|
||||||
expect(
|
expected: { level: "ack", ackEnabled: true, agentReactionsEnabled: false },
|
||||||
resolveReactionLevel({
|
},
|
||||||
|
{
|
||||||
|
name: "supports extensive",
|
||||||
|
input: {
|
||||||
value: "extensive",
|
value: "extensive",
|
||||||
defaultLevel: "minimal",
|
defaultLevel: "minimal" as const,
|
||||||
invalidFallback: "ack",
|
invalidFallback: "ack" as const,
|
||||||
}),
|
},
|
||||||
).toEqual({
|
expected: {
|
||||||
level: "extensive",
|
level: "extensive",
|
||||||
ackEnabled: false,
|
ackEnabled: false,
|
||||||
agentReactionsEnabled: true,
|
agentReactionsEnabled: true,
|
||||||
agentReactionGuidance: "extensive",
|
agentReactionGuidance: "extensive",
|
||||||
});
|
},
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
name: "uses invalid fallback ack",
|
||||||
|
input: { value: "bogus", defaultLevel: "minimal" as const, invalidFallback: "ack" as const },
|
||||||
|
expected: { level: "ack", ackEnabled: true, agentReactionsEnabled: false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "uses invalid fallback minimal",
|
||||||
|
input: {
|
||||||
|
value: "bogus",
|
||||||
|
defaultLevel: "minimal" as const,
|
||||||
|
invalidFallback: "minimal" as const,
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
level: "minimal",
|
||||||
|
ackEnabled: false,
|
||||||
|
agentReactionsEnabled: true,
|
||||||
|
agentReactionGuidance: "minimal",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
it("uses invalid fallback ack", () => {
|
for (const testCase of cases) {
|
||||||
expect(
|
it(testCase.name, () => {
|
||||||
resolveReactionLevel({ value: "bogus", defaultLevel: "minimal", invalidFallback: "ack" }),
|
expect(resolveReactionLevel(testCase.input)).toEqual(testCase.expected);
|
||||||
).toEqual({ level: "ack", ackEnabled: true, agentReactionsEnabled: false });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("uses invalid fallback minimal", () => {
|
|
||||||
expect(
|
|
||||||
resolveReactionLevel({ value: "bogus", defaultLevel: "minimal", invalidFallback: "minimal" }),
|
|
||||||
).toEqual({
|
|
||||||
level: "minimal",
|
|
||||||
ackEnabled: false,
|
|
||||||
agentReactionsEnabled: true,
|
|
||||||
agentReactionGuidance: "minimal",
|
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -43,40 +43,48 @@ describe("parseBooleanValue", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("isReasoningTagProvider", () => {
|
describe("isReasoningTagProvider", () => {
|
||||||
it("returns false for ollama - native reasoning field, no tags needed (#2279)", () => {
|
const cases: Array<{
|
||||||
expect(isReasoningTagProvider("ollama")).toBe(false);
|
name: string;
|
||||||
expect(isReasoningTagProvider("Ollama")).toBe(false);
|
value: string | null | undefined;
|
||||||
});
|
expected: boolean;
|
||||||
|
}> = [
|
||||||
|
{
|
||||||
|
name: "returns false for ollama - native reasoning field, no tags needed (#2279)",
|
||||||
|
value: "ollama",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "returns false for case-insensitive ollama",
|
||||||
|
value: "Ollama",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{ name: "returns true for google-gemini-cli", value: "google-gemini-cli", expected: true },
|
||||||
|
{
|
||||||
|
name: "returns true for google-generative-ai",
|
||||||
|
value: "google-generative-ai",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{ name: "returns true for google-antigravity", value: "google-antigravity", expected: true },
|
||||||
|
{
|
||||||
|
name: "returns true for google-antigravity model suffixes",
|
||||||
|
value: "google-antigravity/gemini-3",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{ name: "returns true for minimax", value: "minimax", expected: true },
|
||||||
|
{ name: "returns true for minimax-cn", value: "minimax-cn", expected: true },
|
||||||
|
{ name: "returns false for null", value: null, expected: false },
|
||||||
|
{ name: "returns false for undefined", value: undefined, expected: false },
|
||||||
|
{ name: "returns false for empty", value: "", expected: false },
|
||||||
|
{ name: "returns false for anthropic", value: "anthropic", expected: false },
|
||||||
|
{ name: "returns false for openai", value: "openai", expected: false },
|
||||||
|
{ name: "returns false for openrouter", value: "openrouter", expected: false },
|
||||||
|
];
|
||||||
|
|
||||||
it("returns true for google-gemini-cli", () => {
|
for (const testCase of cases) {
|
||||||
expect(isReasoningTagProvider("google-gemini-cli")).toBe(true);
|
it(testCase.name, () => {
|
||||||
});
|
expect(isReasoningTagProvider(testCase.value)).toBe(testCase.expected);
|
||||||
|
});
|
||||||
it("returns true for google-generative-ai", () => {
|
}
|
||||||
expect(isReasoningTagProvider("google-generative-ai")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns true for google-antigravity", () => {
|
|
||||||
expect(isReasoningTagProvider("google-antigravity")).toBe(true);
|
|
||||||
expect(isReasoningTagProvider("google-antigravity/gemini-3")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns true for minimax", () => {
|
|
||||||
expect(isReasoningTagProvider("minimax")).toBe(true);
|
|
||||||
expect(isReasoningTagProvider("minimax-cn")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns false for null/undefined/empty", () => {
|
|
||||||
expect(isReasoningTagProvider(null)).toBe(false);
|
|
||||||
expect(isReasoningTagProvider(undefined)).toBe(false);
|
|
||||||
expect(isReasoningTagProvider("")).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns false for standard providers", () => {
|
|
||||||
expect(isReasoningTagProvider("anthropic")).toBe(false);
|
|
||||||
expect(isReasoningTagProvider("openai")).toBe(false);
|
|
||||||
expect(isReasoningTagProvider("openrouter")).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("splitShellArgs", () => {
|
describe("splitShellArgs", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user