routines.test.ts•10.6 kB
import { promises as fs } from 'fs'
import {
createRoutine,
loadRoutines,
deleteRoutine,
updateRoutine,
Routine,
} from '../src/routines'
// Mock fs module
jest.mock('fs', () => ({
promises: {
writeFile: jest.fn(),
readFile: jest.fn()
}
}))
describe('Routine functions', () => {
const mockRoutine: Routine = {
name: 'Test Routine',
description: 'A test routine',
steps: [
{
description: 'Test step',
tool: 'test-tool',
params: { param1: 'value1' }
}
]
}
const testFilename = 'test-routines.json'
beforeEach(() => {
jest.clearAllMocks()
})
describe('createRoutine', () => {
it('should successfully create a routine when file is empty', async () => {
// Mock file not found error
(fs.readFile as jest.Mock).mockRejectedValue({ code: 'ENOENT' })
await createRoutine({ routine: mockRoutine, filename: testFilename })
expect(fs.writeFile).toHaveBeenCalledWith(
testFilename,
JSON.stringify([mockRoutine], null, 2)
)
})
it('should append routine to existing routines', async () => {
const existingRoutines: Routine[] = [{
name: 'Existing Routine',
description: 'An existing routine',
steps: [{ description: 'Step 1', tool: 'tool-1', params: {} }]
}]
;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify(existingRoutines))
await createRoutine({ routine: mockRoutine, filename: testFilename })
expect(fs.writeFile).toHaveBeenCalledWith(
testFilename,
JSON.stringify([...existingRoutines, mockRoutine], null, 2)
)
})
it('should throw error if routine name already exists', async () => {
const existingRoutines: Routine[] = [{ ...mockRoutine }]
;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify(existingRoutines))
await expect(
createRoutine({ routine: mockRoutine, filename: testFilename })
).rejects.toThrow('A routine with name "Test Routine" already exists')
})
it('should throw error if file content is not an array', async () => {
;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify({}))
await expect(
createRoutine({ routine: mockRoutine, filename: testFilename })
).rejects.toThrow('Existing file content must be an array of routines')
})
it('should throw error if file read fails with unexpected error', async () => {
;(fs.readFile as jest.Mock).mockRejectedValue(new Error('Permission denied'))
await expect(
createRoutine({ routine: mockRoutine, filename: testFilename })
).rejects.toThrow('Failed to create routine: Permission denied')
})
it('should throw error if routine name is missing', async () => {
const invalidRoutine = { ...mockRoutine, name: '' }
await expect(
createRoutine({ routine: invalidRoutine, filename: testFilename })
).rejects.toThrow('Routine must have a valid name')
})
it('should throw error if routine description is missing', async () => {
const invalidRoutine = { ...mockRoutine, description: '' }
await expect(
createRoutine({ routine: invalidRoutine, filename: testFilename })
).rejects.toThrow('Routine must have a valid description')
})
it('should throw error if steps array is empty', async () => {
const invalidRoutine = { ...mockRoutine, steps: [] }
await expect(
createRoutine({ routine: invalidRoutine, filename: testFilename })
).rejects.toThrow('Routine must have at least one step')
})
it('should throw error if step is invalid', async () => {
const invalidRoutine = {
...mockRoutine,
steps: [{ description: '', tool: 'test-tool', params: {} }]
}
await expect(
createRoutine({ routine: invalidRoutine, filename: testFilename })
).rejects.toThrow('Each step must have a valid description')
})
})
describe('loadRoutines', () => {
it('should successfully load routines', async () => {
const mockData = [mockRoutine]
;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify(mockData))
const result = await loadRoutines(testFilename)
expect(result).toEqual(mockData)
expect(fs.readFile).toHaveBeenCalledWith(testFilename, 'utf-8')
})
it('should return empty array when file does not exist', async () => {
(fs.readFile as jest.Mock).mockRejectedValue({ code: 'ENOENT' })
const result = await loadRoutines(testFilename)
expect(result).toEqual([])
expect(fs.readFile).toHaveBeenCalledWith(testFilename, 'utf-8')
})
it('should throw error if file content is not an array', async () => {
;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify({}))
await expect(loadRoutines(testFilename)).rejects.toThrow(
'File content must be an array of routines'
)
})
it('should throw error if routine in file is invalid', async () => {
const mockData = [{ ...mockRoutine, name: '' }]
;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify(mockData))
await expect(loadRoutines(testFilename)).rejects.toThrow(
'Each routine must have a valid name'
)
})
it('should throw error if file content is not valid JSON', async () => {
;(fs.readFile as jest.Mock).mockResolvedValue('invalid json')
await expect(loadRoutines(testFilename)).rejects.toThrow(
'Failed to load routines: Unexpected token'
)
})
it('should throw error if file read fails', async () => {
;(fs.readFile as jest.Mock).mockRejectedValue(new Error('File not found'))
await expect(loadRoutines(testFilename)).rejects.toThrow(
'Failed to load routines: File not found'
)
})
})
describe('deleteRoutine', () => {
it('should successfully delete a routine', async () => {
const existingRoutines = [mockRoutine, {
name: 'Another Routine',
description: 'Another test routine',
steps: [{ description: 'Step 1', tool: 'tool-1', params: {} }]
}]
;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify(existingRoutines))
await deleteRoutine({ name: mockRoutine.name, filename: testFilename })
expect(fs.writeFile).toHaveBeenCalledWith(
testFilename,
JSON.stringify([existingRoutines[1]], null, 2)
)
})
it('should throw error if routine does not exist', async () => {
const existingRoutines = [mockRoutine]
;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify(existingRoutines))
await expect(
deleteRoutine({ name: 'Non-existent Routine', filename: testFilename })
).rejects.toThrow('Failed to delete routine: No routine found with name "Non-existent Routine"')
})
it('should throw error if file read fails', async () => {
;(fs.readFile as jest.Mock).mockRejectedValue(new Error('Permission denied'))
await expect(
deleteRoutine({ name: mockRoutine.name, filename: testFilename })
).rejects.toThrow('Failed to delete routine: Failed to load routines: Permission denied')
})
it('should throw error if file write fails', async () => {
const existingRoutines = [mockRoutine]
;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify(existingRoutines))
;(fs.writeFile as jest.Mock).mockRejectedValue(new Error('Write permission denied'))
await expect(
deleteRoutine({ name: mockRoutine.name, filename: testFilename })
).rejects.toThrow('Failed to delete routine: Write permission denied')
})
})
describe('updateRoutine', () => {
it('should successfully update a routine', async () => {
const existingRoutines = [mockRoutine, {
name: 'Another Routine',
description: 'Another test routine',
steps: [{ description: 'Step 1', tool: 'tool-1', params: {} }]
}]
;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify(existingRoutines))
;(fs.writeFile as jest.Mock).mockResolvedValue(undefined)
const updatedRoutine = {
...mockRoutine,
description: 'Updated description',
steps: [
{
description: 'Updated step',
tool: 'updated-tool',
params: { param2: 'value2' }
}
]
}
await updateRoutine({ routine: updatedRoutine, filename: testFilename })
expect(fs.writeFile).toHaveBeenCalledWith(
testFilename,
JSON.stringify([updatedRoutine, existingRoutines[1]], null, 2)
)
})
it('should throw error if routine does not exist', async () => {
const existingRoutines = [mockRoutine]
;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify(existingRoutines))
const nonExistentRoutine = {
name: 'Non-existent Routine',
description: 'This routine does not exist',
steps: [{ description: 'Step 1', tool: 'tool-1', params: {} }]
}
await expect(
updateRoutine({ routine: nonExistentRoutine, filename: testFilename })
).rejects.toThrow('Failed to update routine: No routine found with name "Non-existent Routine"')
})
it('should throw error if updated routine is invalid', async () => {
const existingRoutines = [mockRoutine]
;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify(existingRoutines))
const invalidRoutine = {
...mockRoutine,
description: '' // Invalid empty description
}
await expect(
updateRoutine({ routine: invalidRoutine, filename: testFilename })
).rejects.toThrow('Routine must have a valid description')
})
it('should throw error if file read fails', async () => {
;(fs.readFile as jest.Mock).mockRejectedValue(new Error('Permission denied'))
await expect(
updateRoutine({ routine: mockRoutine, filename: testFilename })
).rejects.toThrow('Failed to update routine: Failed to load routines: Permission denied')
})
it('should throw error if file write fails', async () => {
const existingRoutines = [mockRoutine]
;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify(existingRoutines))
;(fs.writeFile as jest.Mock).mockRejectedValue(new Error('Write permission denied'))
await expect(
updateRoutine({ routine: mockRoutine, filename: testFilename })
).rejects.toThrow('Failed to update routine: Write permission denied')
})
})
})