feat: add AutoFix config schema and reader module
Implements AutoFixConfigSchema (Zod v4) with validation for lint/test commands, maxRetries (0-10, default 3), and timeout (1000-300000ms, default 30000). Adds getAutoFixConfig helper that returns null for disabled or invalid configs. All 9 unit tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
77
src/services/autoFix/autoFixConfig.test.ts
Normal file
77
src/services/autoFix/autoFixConfig.test.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import { describe, expect, test } from 'bun:test'
|
||||||
|
import { AutoFixConfigSchema, getAutoFixConfig, type AutoFixConfig } from './autoFixConfig.js'
|
||||||
|
|
||||||
|
describe('AutoFixConfigSchema', () => {
|
||||||
|
test('parses valid full config', () => {
|
||||||
|
const input = {
|
||||||
|
enabled: true,
|
||||||
|
lint: 'eslint . --fix',
|
||||||
|
test: 'bun test',
|
||||||
|
maxRetries: 3,
|
||||||
|
timeout: 30000,
|
||||||
|
}
|
||||||
|
const result = AutoFixConfigSchema.safeParse(input)
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
if (result.success) {
|
||||||
|
expect(result.data.enabled).toBe(true)
|
||||||
|
expect(result.data.lint).toBe('eslint . --fix')
|
||||||
|
expect(result.data.test).toBe('bun test')
|
||||||
|
expect(result.data.maxRetries).toBe(3)
|
||||||
|
expect(result.data.timeout).toBe(30000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parses minimal config with defaults', () => {
|
||||||
|
const input = { enabled: true, lint: 'eslint .' }
|
||||||
|
const result = AutoFixConfigSchema.safeParse(input)
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
if (result.success) {
|
||||||
|
expect(result.data.maxRetries).toBe(3)
|
||||||
|
expect(result.data.timeout).toBe(30000)
|
||||||
|
expect(result.data.test).toBeUndefined()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('rejects config with enabled but no lint or test', () => {
|
||||||
|
const input = { enabled: true }
|
||||||
|
const result = AutoFixConfigSchema.safeParse(input)
|
||||||
|
expect(result.success).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('accepts disabled config without commands', () => {
|
||||||
|
const input = { enabled: false }
|
||||||
|
const result = AutoFixConfigSchema.safeParse(input)
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('rejects negative maxRetries', () => {
|
||||||
|
const input = { enabled: true, lint: 'eslint .', maxRetries: -1 }
|
||||||
|
const result = AutoFixConfigSchema.safeParse(input)
|
||||||
|
expect(result.success).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('rejects maxRetries above 10', () => {
|
||||||
|
const input = { enabled: true, lint: 'eslint .', maxRetries: 11 }
|
||||||
|
const result = AutoFixConfigSchema.safeParse(input)
|
||||||
|
expect(result.success).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getAutoFixConfig', () => {
|
||||||
|
test('returns null when settings have no autoFix', () => {
|
||||||
|
const result = getAutoFixConfig(undefined)
|
||||||
|
expect(result).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('returns null when autoFix is disabled', () => {
|
||||||
|
const result = getAutoFixConfig({ enabled: false })
|
||||||
|
expect(result).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('returns parsed config when valid and enabled', () => {
|
||||||
|
const result = getAutoFixConfig({ enabled: true, lint: 'eslint .' })
|
||||||
|
expect(result).not.toBeNull()
|
||||||
|
expect(result!.enabled).toBe(true)
|
||||||
|
expect(result!.lint).toBe('eslint .')
|
||||||
|
})
|
||||||
|
})
|
||||||
52
src/services/autoFix/autoFixConfig.ts
Normal file
52
src/services/autoFix/autoFixConfig.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { z } from 'zod/v4'
|
||||||
|
|
||||||
|
export const AutoFixConfigSchema = z
|
||||||
|
.object({
|
||||||
|
enabled: z.boolean().describe('Whether auto-fix is enabled'),
|
||||||
|
lint: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe('Lint command to run after file edits (e.g. "eslint . --fix")'),
|
||||||
|
test: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe('Test command to run after file edits (e.g. "bun test")'),
|
||||||
|
maxRetries: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.min(0)
|
||||||
|
.max(10)
|
||||||
|
.default(3)
|
||||||
|
.describe('Maximum number of auto-fix retry attempts (default: 3)'),
|
||||||
|
timeout: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.min(1000)
|
||||||
|
.max(300000)
|
||||||
|
.default(30000)
|
||||||
|
.describe('Timeout in ms for each lint/test command (default: 30000)'),
|
||||||
|
})
|
||||||
|
.refine(
|
||||||
|
data => !data.enabled || data.lint !== undefined || data.test !== undefined,
|
||||||
|
{
|
||||||
|
message: 'At least one of "lint" or "test" must be set when enabled',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
export type AutoFixConfig = z.infer<typeof AutoFixConfigSchema>
|
||||||
|
|
||||||
|
export function getAutoFixConfig(
|
||||||
|
rawConfig: unknown,
|
||||||
|
): AutoFixConfig | null {
|
||||||
|
if (!rawConfig || typeof rawConfig !== 'object') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const parsed = AutoFixConfigSchema.safeParse(rawConfig)
|
||||||
|
if (!parsed.success) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (!parsed.data.enabled) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return parsed.data
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user