mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-18 14:25:51 +00:00
style: 完成所有文件的lint
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { describe, expect, test } from 'bun:test'
|
||||
import {
|
||||
SettingsSchema,
|
||||
EnvironmentVariablesSchema,
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
isMcpServerCommandEntry,
|
||||
isMcpServerUrlEntry,
|
||||
CUSTOMIZATION_SURFACES,
|
||||
} from "../types";
|
||||
} from '../types'
|
||||
import {
|
||||
SETTING_SOURCES,
|
||||
SOURCES,
|
||||
@@ -19,465 +19,458 @@ import {
|
||||
getSettingSourceDisplayNameLowercase,
|
||||
getSettingSourceDisplayNameCapitalized,
|
||||
parseSettingSourcesFlag,
|
||||
} from "../constants";
|
||||
} from '../constants'
|
||||
import {
|
||||
formatZodError,
|
||||
filterInvalidPermissionRules,
|
||||
validateSettingsFileContent,
|
||||
} from "../validation";
|
||||
} from '../validation'
|
||||
|
||||
// ─── Settings Schema Validation ──────────────────────────────────────────
|
||||
|
||||
describe("SettingsSchema", () => {
|
||||
test("accepts empty object", () => {
|
||||
const result = SettingsSchema().safeParse({});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
describe('SettingsSchema', () => {
|
||||
test('accepts empty object', () => {
|
||||
const result = SettingsSchema().safeParse({})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("accepts model string", () => {
|
||||
const result = SettingsSchema().safeParse({ model: "sonnet" });
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
test('accepts model string', () => {
|
||||
const result = SettingsSchema().safeParse({ model: 'sonnet' })
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("accepts permissions block with allow rules", () => {
|
||||
test('accepts permissions block with allow rules', () => {
|
||||
const result = SettingsSchema().safeParse({
|
||||
permissions: { allow: ["Bash(npm install)"] },
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
permissions: { allow: ['Bash(npm install)'] },
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("accepts permissions block with deny rules", () => {
|
||||
test('accepts permissions block with deny rules', () => {
|
||||
const result = SettingsSchema().safeParse({
|
||||
permissions: { deny: ["Bash(rm -rf *)"] },
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
permissions: { deny: ['Bash(rm -rf *)'] },
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("accepts env variables", () => {
|
||||
test('accepts env variables', () => {
|
||||
const result = SettingsSchema().safeParse({
|
||||
env: { FOO: "bar", DEBUG: "1" },
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
env: { FOO: 'bar', DEBUG: '1' },
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("accepts hooks configuration", () => {
|
||||
test('accepts hooks configuration', () => {
|
||||
const result = SettingsSchema().safeParse({
|
||||
hooks: {
|
||||
PreToolUse: [
|
||||
{
|
||||
matcher: "Bash",
|
||||
hooks: [{ type: "command", command: "echo test" }],
|
||||
matcher: 'Bash',
|
||||
hooks: [{ type: 'command', command: 'echo test' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("accepts attribution settings", () => {
|
||||
test('accepts attribution settings', () => {
|
||||
const result = SettingsSchema().safeParse({
|
||||
attribution: {
|
||||
commit: "Generated by AI",
|
||||
pr: "AI-generated PR",
|
||||
commit: 'Generated by AI',
|
||||
pr: 'AI-generated PR',
|
||||
},
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("accepts worktree settings", () => {
|
||||
test('accepts worktree settings', () => {
|
||||
const result = SettingsSchema().safeParse({
|
||||
worktree: {
|
||||
symlinkDirectories: ["node_modules", ".cache"],
|
||||
sparsePaths: ["src/"],
|
||||
symlinkDirectories: ['node_modules', '.cache'],
|
||||
sparsePaths: ['src/'],
|
||||
},
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("accepts $schema field", () => {
|
||||
test('accepts $schema field', () => {
|
||||
const result = SettingsSchema().safeParse({
|
||||
$schema: CLAUDE_CODE_SETTINGS_SCHEMA_URL,
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("passes through unknown keys (passthrough mode)", () => {
|
||||
const result = SettingsSchema().safeParse({ unknownKey: "value" });
|
||||
expect(result.success).toBe(true);
|
||||
test('passes through unknown keys (passthrough mode)', () => {
|
||||
const result = SettingsSchema().safeParse({ unknownKey: 'value' })
|
||||
expect(result.success).toBe(true)
|
||||
if (result.success) {
|
||||
expect((result.data as any).unknownKey).toBe("value");
|
||||
expect((result.data as any).unknownKey).toBe('value')
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
test("coerces env var numbers to strings", () => {
|
||||
const result = EnvironmentVariablesSchema().safeParse({ PORT: 3000 });
|
||||
expect(result.success).toBe(true);
|
||||
test('coerces env var numbers to strings', () => {
|
||||
const result = EnvironmentVariablesSchema().safeParse({ PORT: 3000 })
|
||||
expect(result.success).toBe(true)
|
||||
if (result.success) {
|
||||
expect(result.data.PORT).toBe("3000");
|
||||
expect(result.data.PORT).toBe('3000')
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
test("accepts boolean settings", () => {
|
||||
test('accepts boolean settings', () => {
|
||||
const result = SettingsSchema().safeParse({
|
||||
includeCoAuthoredBy: true,
|
||||
respectGitignore: false,
|
||||
disableAllHooks: true,
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("accepts cleanupPeriodDays", () => {
|
||||
const result = SettingsSchema().safeParse({ cleanupPeriodDays: 30 });
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
test('accepts cleanupPeriodDays', () => {
|
||||
const result = SettingsSchema().safeParse({ cleanupPeriodDays: 30 })
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("rejects negative cleanupPeriodDays", () => {
|
||||
const result = SettingsSchema().safeParse({ cleanupPeriodDays: -1 });
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
test('rejects negative cleanupPeriodDays', () => {
|
||||
const result = SettingsSchema().safeParse({ cleanupPeriodDays: -1 })
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test("accepts statusLine configuration", () => {
|
||||
test('accepts statusLine configuration', () => {
|
||||
const result = SettingsSchema().safeParse({
|
||||
statusLine: { type: "command", command: "echo status" },
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
statusLine: { type: 'command', command: 'echo status' },
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("accepts sshConfigs", () => {
|
||||
test('accepts sshConfigs', () => {
|
||||
const result = SettingsSchema().safeParse({
|
||||
sshConfigs: [
|
||||
{
|
||||
id: "dev-server",
|
||||
name: "Development Server",
|
||||
sshHost: "dev.example.com",
|
||||
id: 'dev-server',
|
||||
name: 'Development Server',
|
||||
sshHost: 'dev.example.com',
|
||||
sshPort: 22,
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
// ─── Permissions Schema ─────────────────────────────────────────────────
|
||||
|
||||
describe("PermissionsSchema", () => {
|
||||
test("accepts defaultMode", () => {
|
||||
describe('PermissionsSchema', () => {
|
||||
test('accepts defaultMode', () => {
|
||||
const result = PermissionsSchema().safeParse({
|
||||
defaultMode: "acceptEdits",
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
defaultMode: 'acceptEdits',
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("accepts additionalDirectories", () => {
|
||||
test('accepts additionalDirectories', () => {
|
||||
const result = PermissionsSchema().safeParse({
|
||||
additionalDirectories: ["/tmp/extra"],
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
additionalDirectories: ['/tmp/extra'],
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("accepts disableBypassPermissionsMode", () => {
|
||||
test('accepts disableBypassPermissionsMode', () => {
|
||||
const result = PermissionsSchema().safeParse({
|
||||
disableBypassPermissionsMode: "disable",
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
disableBypassPermissionsMode: 'disable',
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
// ─── AllowedMcpServerEntrySchema ────────────────────────────────────────
|
||||
|
||||
describe("AllowedMcpServerEntrySchema", () => {
|
||||
test("accepts serverName entry", () => {
|
||||
describe('AllowedMcpServerEntrySchema', () => {
|
||||
test('accepts serverName entry', () => {
|
||||
const result = AllowedMcpServerEntrySchema().safeParse({
|
||||
serverName: "my-server",
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
serverName: 'my-server',
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("accepts serverCommand entry", () => {
|
||||
test('accepts serverCommand entry', () => {
|
||||
const result = AllowedMcpServerEntrySchema().safeParse({
|
||||
serverCommand: ["npx", "mcp-server"],
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
serverCommand: ['npx', 'mcp-server'],
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("accepts serverUrl entry", () => {
|
||||
test('accepts serverUrl entry', () => {
|
||||
const result = AllowedMcpServerEntrySchema().safeParse({
|
||||
serverUrl: "https://*.example.com/*",
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
serverUrl: 'https://*.example.com/*',
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test("rejects entry with no fields", () => {
|
||||
const result = AllowedMcpServerEntrySchema().safeParse({});
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
test('rejects entry with no fields', () => {
|
||||
const result = AllowedMcpServerEntrySchema().safeParse({})
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test("rejects entry with multiple fields", () => {
|
||||
test('rejects entry with multiple fields', () => {
|
||||
const result = AllowedMcpServerEntrySchema().safeParse({
|
||||
serverName: "my-server",
|
||||
serverUrl: "https://example.com",
|
||||
});
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
serverName: 'my-server',
|
||||
serverUrl: 'https://example.com',
|
||||
})
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test("rejects invalid serverName characters", () => {
|
||||
test('rejects invalid serverName characters', () => {
|
||||
const result = AllowedMcpServerEntrySchema().safeParse({
|
||||
serverName: "my server with spaces",
|
||||
});
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
serverName: 'my server with spaces',
|
||||
})
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test("rejects empty serverCommand array", () => {
|
||||
test('rejects empty serverCommand array', () => {
|
||||
const result = AllowedMcpServerEntrySchema().safeParse({
|
||||
serverCommand: [],
|
||||
});
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
});
|
||||
})
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
// ─── Type guards ─────────────────────────────────────────────────────────
|
||||
|
||||
describe("MCP server entry type guards", () => {
|
||||
test("isMcpServerNameEntry identifies name entry", () => {
|
||||
expect(isMcpServerNameEntry({ serverName: "test" })).toBe(true);
|
||||
});
|
||||
describe('MCP server entry type guards', () => {
|
||||
test('isMcpServerNameEntry identifies name entry', () => {
|
||||
expect(isMcpServerNameEntry({ serverName: 'test' })).toBe(true)
|
||||
})
|
||||
|
||||
test("isMcpServerNameEntry rejects non-name entry", () => {
|
||||
expect(isMcpServerNameEntry({ serverUrl: "https://example.com" })).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
test('isMcpServerNameEntry rejects non-name entry', () => {
|
||||
expect(isMcpServerNameEntry({ serverUrl: 'https://example.com' })).toBe(
|
||||
false,
|
||||
)
|
||||
})
|
||||
|
||||
test("isMcpServerCommandEntry identifies command entry", () => {
|
||||
expect(isMcpServerCommandEntry({ serverCommand: ["npx", "srv"] })).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
test('isMcpServerCommandEntry identifies command entry', () => {
|
||||
expect(isMcpServerCommandEntry({ serverCommand: ['npx', 'srv'] })).toBe(
|
||||
true,
|
||||
)
|
||||
})
|
||||
|
||||
test("isMcpServerCommandEntry rejects non-command entry", () => {
|
||||
expect(isMcpServerCommandEntry({ serverName: "test" })).toBe(false);
|
||||
});
|
||||
test('isMcpServerCommandEntry rejects non-command entry', () => {
|
||||
expect(isMcpServerCommandEntry({ serverName: 'test' })).toBe(false)
|
||||
})
|
||||
|
||||
test("isMcpServerUrlEntry identifies url entry", () => {
|
||||
expect(
|
||||
isMcpServerUrlEntry({ serverUrl: "https://example.com" })
|
||||
).toBe(true);
|
||||
});
|
||||
test('isMcpServerUrlEntry identifies url entry', () => {
|
||||
expect(isMcpServerUrlEntry({ serverUrl: 'https://example.com' })).toBe(true)
|
||||
})
|
||||
|
||||
test("isMcpServerUrlEntry rejects non-url entry", () => {
|
||||
expect(isMcpServerUrlEntry({ serverName: "test" })).toBe(false);
|
||||
});
|
||||
});
|
||||
test('isMcpServerUrlEntry rejects non-url entry', () => {
|
||||
expect(isMcpServerUrlEntry({ serverName: 'test' })).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
// ─── Constants ──────────────────────────────────────────────────────────
|
||||
|
||||
describe("SETTING_SOURCES", () => {
|
||||
test("contains all five sources in order", () => {
|
||||
describe('SETTING_SOURCES', () => {
|
||||
test('contains all five sources in order', () => {
|
||||
expect(SETTING_SOURCES).toEqual([
|
||||
"userSettings",
|
||||
"projectSettings",
|
||||
"localSettings",
|
||||
"flagSettings",
|
||||
"policySettings",
|
||||
]);
|
||||
});
|
||||
});
|
||||
'userSettings',
|
||||
'projectSettings',
|
||||
'localSettings',
|
||||
'flagSettings',
|
||||
'policySettings',
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("SOURCES (editable)", () => {
|
||||
test("contains three editable sources", () => {
|
||||
describe('SOURCES (editable)', () => {
|
||||
test('contains three editable sources', () => {
|
||||
expect(SOURCES).toEqual([
|
||||
"localSettings",
|
||||
"projectSettings",
|
||||
"userSettings",
|
||||
]);
|
||||
});
|
||||
});
|
||||
'localSettings',
|
||||
'projectSettings',
|
||||
'userSettings',
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("CUSTOMIZATION_SURFACES", () => {
|
||||
test("contains expected surfaces", () => {
|
||||
expect(CUSTOMIZATION_SURFACES).toEqual([
|
||||
"skills",
|
||||
"agents",
|
||||
"hooks",
|
||||
"mcp",
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('CUSTOMIZATION_SURFACES', () => {
|
||||
test('contains expected surfaces', () => {
|
||||
expect(CUSTOMIZATION_SURFACES).toEqual(['skills', 'agents', 'hooks', 'mcp'])
|
||||
})
|
||||
})
|
||||
|
||||
describe("getSettingSourceName", () => {
|
||||
test("maps userSettings to user", () => {
|
||||
expect(getSettingSourceName("userSettings")).toBe("user");
|
||||
});
|
||||
describe('getSettingSourceName', () => {
|
||||
test('maps userSettings to user', () => {
|
||||
expect(getSettingSourceName('userSettings')).toBe('user')
|
||||
})
|
||||
|
||||
test("maps projectSettings to project", () => {
|
||||
expect(getSettingSourceName("projectSettings")).toBe("project");
|
||||
});
|
||||
test('maps projectSettings to project', () => {
|
||||
expect(getSettingSourceName('projectSettings')).toBe('project')
|
||||
})
|
||||
|
||||
test("maps localSettings to project, gitignored", () => {
|
||||
expect(getSettingSourceName("localSettings")).toBe("project, gitignored");
|
||||
});
|
||||
test('maps localSettings to project, gitignored', () => {
|
||||
expect(getSettingSourceName('localSettings')).toBe('project, gitignored')
|
||||
})
|
||||
|
||||
test("maps flagSettings to cli flag", () => {
|
||||
expect(getSettingSourceName("flagSettings")).toBe("cli flag");
|
||||
});
|
||||
test('maps flagSettings to cli flag', () => {
|
||||
expect(getSettingSourceName('flagSettings')).toBe('cli flag')
|
||||
})
|
||||
|
||||
test("maps policySettings to managed", () => {
|
||||
expect(getSettingSourceName("policySettings")).toBe("managed");
|
||||
});
|
||||
});
|
||||
test('maps policySettings to managed', () => {
|
||||
expect(getSettingSourceName('policySettings')).toBe('managed')
|
||||
})
|
||||
})
|
||||
|
||||
describe("getSourceDisplayName", () => {
|
||||
test("maps userSettings to User", () => {
|
||||
expect(getSourceDisplayName("userSettings")).toBe("User");
|
||||
});
|
||||
describe('getSourceDisplayName', () => {
|
||||
test('maps userSettings to User', () => {
|
||||
expect(getSourceDisplayName('userSettings')).toBe('User')
|
||||
})
|
||||
|
||||
test("maps plugin to Plugin", () => {
|
||||
expect(getSourceDisplayName("plugin")).toBe("Plugin");
|
||||
});
|
||||
test('maps plugin to Plugin', () => {
|
||||
expect(getSourceDisplayName('plugin')).toBe('Plugin')
|
||||
})
|
||||
|
||||
test("maps built-in to Built-in", () => {
|
||||
expect(getSourceDisplayName("built-in")).toBe("Built-in");
|
||||
});
|
||||
});
|
||||
test('maps built-in to Built-in', () => {
|
||||
expect(getSourceDisplayName('built-in')).toBe('Built-in')
|
||||
})
|
||||
})
|
||||
|
||||
describe("getSettingSourceDisplayNameLowercase", () => {
|
||||
test("maps policySettings correctly", () => {
|
||||
expect(getSettingSourceDisplayNameLowercase("policySettings")).toBe(
|
||||
"enterprise managed settings"
|
||||
);
|
||||
});
|
||||
describe('getSettingSourceDisplayNameLowercase', () => {
|
||||
test('maps policySettings correctly', () => {
|
||||
expect(getSettingSourceDisplayNameLowercase('policySettings')).toBe(
|
||||
'enterprise managed settings',
|
||||
)
|
||||
})
|
||||
|
||||
test("maps cliArg correctly", () => {
|
||||
expect(getSettingSourceDisplayNameLowercase("cliArg")).toBe("CLI argument");
|
||||
});
|
||||
test('maps cliArg correctly', () => {
|
||||
expect(getSettingSourceDisplayNameLowercase('cliArg')).toBe('CLI argument')
|
||||
})
|
||||
|
||||
test("maps session correctly", () => {
|
||||
expect(getSettingSourceDisplayNameLowercase("session")).toBe(
|
||||
"current session"
|
||||
);
|
||||
});
|
||||
});
|
||||
test('maps session correctly', () => {
|
||||
expect(getSettingSourceDisplayNameLowercase('session')).toBe(
|
||||
'current session',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("getSettingSourceDisplayNameCapitalized", () => {
|
||||
test("maps userSettings correctly", () => {
|
||||
expect(getSettingSourceDisplayNameCapitalized("userSettings")).toBe(
|
||||
"User settings"
|
||||
);
|
||||
});
|
||||
describe('getSettingSourceDisplayNameCapitalized', () => {
|
||||
test('maps userSettings correctly', () => {
|
||||
expect(getSettingSourceDisplayNameCapitalized('userSettings')).toBe(
|
||||
'User settings',
|
||||
)
|
||||
})
|
||||
|
||||
test("maps command correctly", () => {
|
||||
expect(getSettingSourceDisplayNameCapitalized("command")).toBe(
|
||||
"Command configuration"
|
||||
);
|
||||
});
|
||||
});
|
||||
test('maps command correctly', () => {
|
||||
expect(getSettingSourceDisplayNameCapitalized('command')).toBe(
|
||||
'Command configuration',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("parseSettingSourcesFlag", () => {
|
||||
test("parses comma-separated sources", () => {
|
||||
expect(parseSettingSourcesFlag("user,project,local")).toEqual([
|
||||
"userSettings",
|
||||
"projectSettings",
|
||||
"localSettings",
|
||||
]);
|
||||
});
|
||||
describe('parseSettingSourcesFlag', () => {
|
||||
test('parses comma-separated sources', () => {
|
||||
expect(parseSettingSourcesFlag('user,project,local')).toEqual([
|
||||
'userSettings',
|
||||
'projectSettings',
|
||||
'localSettings',
|
||||
])
|
||||
})
|
||||
|
||||
test("parses single source", () => {
|
||||
expect(parseSettingSourcesFlag("user")).toEqual(["userSettings"]);
|
||||
});
|
||||
test('parses single source', () => {
|
||||
expect(parseSettingSourcesFlag('user')).toEqual(['userSettings'])
|
||||
})
|
||||
|
||||
test("returns empty array for empty string", () => {
|
||||
expect(parseSettingSourcesFlag("")).toEqual([]);
|
||||
});
|
||||
test('returns empty array for empty string', () => {
|
||||
expect(parseSettingSourcesFlag('')).toEqual([])
|
||||
})
|
||||
|
||||
test("trims whitespace", () => {
|
||||
expect(parseSettingSourcesFlag("user , project")).toEqual([
|
||||
"userSettings",
|
||||
"projectSettings",
|
||||
]);
|
||||
});
|
||||
test('trims whitespace', () => {
|
||||
expect(parseSettingSourcesFlag('user , project')).toEqual([
|
||||
'userSettings',
|
||||
'projectSettings',
|
||||
])
|
||||
})
|
||||
|
||||
test("throws for invalid source name", () => {
|
||||
expect(() => parseSettingSourcesFlag("invalid")).toThrow(
|
||||
"Invalid setting source"
|
||||
);
|
||||
});
|
||||
});
|
||||
test('throws for invalid source name', () => {
|
||||
expect(() => parseSettingSourcesFlag('invalid')).toThrow(
|
||||
'Invalid setting source',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
// ─── Validation ─────────────────────────────────────────────────────────
|
||||
|
||||
describe("filterInvalidPermissionRules", () => {
|
||||
test("returns empty for non-object input", () => {
|
||||
expect(filterInvalidPermissionRules(null, "test.json")).toEqual([]);
|
||||
expect(filterInvalidPermissionRules("string", "test.json")).toEqual([]);
|
||||
});
|
||||
describe('filterInvalidPermissionRules', () => {
|
||||
test('returns empty for non-object input', () => {
|
||||
expect(filterInvalidPermissionRules(null, 'test.json')).toEqual([])
|
||||
expect(filterInvalidPermissionRules('string', 'test.json')).toEqual([])
|
||||
})
|
||||
|
||||
test("returns empty when no permissions", () => {
|
||||
expect(filterInvalidPermissionRules({}, "test.json")).toEqual([]);
|
||||
});
|
||||
test('returns empty when no permissions', () => {
|
||||
expect(filterInvalidPermissionRules({}, 'test.json')).toEqual([])
|
||||
})
|
||||
|
||||
test("filters non-string rules and returns warnings", () => {
|
||||
const data = { permissions: { allow: ["Bash", 123, "Read"] } };
|
||||
const warnings = filterInvalidPermissionRules(data, "test.json");
|
||||
expect(warnings.length).toBe(1);
|
||||
expect(warnings[0]!.path).toBe("permissions.allow");
|
||||
expect((data.permissions as any).allow).toEqual(["Bash", "Read"]);
|
||||
});
|
||||
test('filters non-string rules and returns warnings', () => {
|
||||
const data = { permissions: { allow: ['Bash', 123, 'Read'] } }
|
||||
const warnings = filterInvalidPermissionRules(data, 'test.json')
|
||||
expect(warnings.length).toBe(1)
|
||||
expect(warnings[0]!.path).toBe('permissions.allow')
|
||||
expect((data.permissions as any).allow).toEqual(['Bash', 'Read'])
|
||||
})
|
||||
|
||||
test("preserves valid rules", () => {
|
||||
test('preserves valid rules', () => {
|
||||
const data = {
|
||||
permissions: { allow: ["Bash(npm install)", "Read", "Write"] },
|
||||
};
|
||||
const warnings = filterInvalidPermissionRules(data, "test.json");
|
||||
expect(warnings).toEqual([]);
|
||||
permissions: { allow: ['Bash(npm install)', 'Read', 'Write'] },
|
||||
}
|
||||
const warnings = filterInvalidPermissionRules(data, 'test.json')
|
||||
expect(warnings).toEqual([])
|
||||
expect((data.permissions as any).allow).toEqual([
|
||||
"Bash(npm install)",
|
||||
"Read",
|
||||
"Write",
|
||||
]);
|
||||
});
|
||||
});
|
||||
'Bash(npm install)',
|
||||
'Read',
|
||||
'Write',
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("validateSettingsFileContent", () => {
|
||||
test("accepts valid JSON settings", () => {
|
||||
const result = validateSettingsFileContent('{"model": "sonnet"}');
|
||||
expect(result.isValid).toBe(true);
|
||||
});
|
||||
describe('validateSettingsFileContent', () => {
|
||||
test('accepts valid JSON settings', () => {
|
||||
const result = validateSettingsFileContent('{"model": "sonnet"}')
|
||||
expect(result.isValid).toBe(true)
|
||||
})
|
||||
|
||||
test("accepts empty object", () => {
|
||||
const result = validateSettingsFileContent("{}");
|
||||
expect(result.isValid).toBe(true);
|
||||
});
|
||||
test('accepts empty object', () => {
|
||||
const result = validateSettingsFileContent('{}')
|
||||
expect(result.isValid).toBe(true)
|
||||
})
|
||||
|
||||
test("rejects invalid JSON", () => {
|
||||
const result = validateSettingsFileContent("not json");
|
||||
expect(result.isValid).toBe(false);
|
||||
test('rejects invalid JSON', () => {
|
||||
const result = validateSettingsFileContent('not json')
|
||||
expect(result.isValid).toBe(false)
|
||||
if (!result.isValid) {
|
||||
expect((result as any).error).toContain("Invalid JSON");
|
||||
expect((result as any).error).toContain('Invalid JSON')
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
test("rejects unknown keys in strict mode", () => {
|
||||
const result = validateSettingsFileContent('{"unknownField": true}');
|
||||
expect(result.isValid).toBe(false);
|
||||
});
|
||||
});
|
||||
test('rejects unknown keys in strict mode', () => {
|
||||
const result = validateSettingsFileContent('{"unknownField": true}')
|
||||
expect(result.isValid).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("formatZodError", () => {
|
||||
test("formats invalid type error", () => {
|
||||
const result = SettingsSchema().safeParse({ model: 123 });
|
||||
expect(result.success).toBe(false);
|
||||
describe('formatZodError', () => {
|
||||
test('formats invalid type error', () => {
|
||||
const result = SettingsSchema().safeParse({ model: 123 })
|
||||
expect(result.success).toBe(false)
|
||||
if (!result.success) {
|
||||
const errors = formatZodError(result.error, "settings.json");
|
||||
expect(errors.length).toBeGreaterThan(0);
|
||||
expect(errors[0]!.file).toBe("settings.json");
|
||||
expect(errors[0]!.path).toContain("model");
|
||||
const errors = formatZodError(result.error, 'settings.json')
|
||||
expect(errors.length).toBeGreaterThan(0)
|
||||
expect(errors[0]!.file).toBe('settings.json')
|
||||
expect(errors[0]!.path).toContain('model')
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
describe("gemini settings", () => {
|
||||
test("accepts gemini modelType", () => {
|
||||
const result = SettingsSchema().safeParse({ modelType: "gemini" });
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
describe('gemini settings', () => {
|
||||
test('accepts gemini modelType', () => {
|
||||
const result = SettingsSchema().safeParse({ modelType: 'gemini' })
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -879,7 +879,9 @@ export const SettingsSchema = lazySchema(() =>
|
||||
voiceProvider: z
|
||||
.enum(['anthropic', 'doubao'])
|
||||
.optional()
|
||||
.describe('Voice STT backend: "anthropic" (default) or "doubao" (Doubao ASR)'),
|
||||
.describe(
|
||||
'Voice STT backend: "anthropic" (default) or "doubao" (Doubao ASR)',
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
...(feature('KAIROS')
|
||||
@@ -1159,4 +1161,3 @@ export type PluginConfig = {
|
||||
[serverName: string]: UserConfigValues
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user