chore: implement requested changes
This commit is contained in:
@@ -79,9 +79,7 @@ function generateExampleFromSchema(schema) {
|
|||||||
if (def.checks) {
|
if (def.checks) {
|
||||||
const minCheck = def.checks.find((c) => c.kind === 'min');
|
const minCheck = def.checks.find((c) => c.kind === 'min');
|
||||||
const maxCheck = def.checks.find((c) => c.kind === 'max');
|
const maxCheck = def.checks.find((c) => c.kind === 'max');
|
||||||
if (minCheck && minCheck.value >= 20) {
|
if (minCheck && maxCheck) {
|
||||||
return '<string with at least ' + minCheck.value + ' characters>';
|
|
||||||
} else if (minCheck && maxCheck) {
|
|
||||||
return (
|
return (
|
||||||
'<string between ' +
|
'<string between ' +
|
||||||
minCheck.value +
|
minCheck.value +
|
||||||
@@ -89,6 +87,8 @@ function generateExampleFromSchema(schema) {
|
|||||||
maxCheck.value +
|
maxCheck.value +
|
||||||
' characters>'
|
' characters>'
|
||||||
);
|
);
|
||||||
|
} else if (minCheck) {
|
||||||
|
return '<string with at least ' + minCheck.value + ' characters>';
|
||||||
} else if (maxCheck) {
|
} else if (maxCheck) {
|
||||||
return '<string up to ' + maxCheck.value + ' characters>';
|
return '<string up to ' + maxCheck.value + ' characters>';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ import {
|
|||||||
hasCodebaseAnalysis
|
hasCodebaseAnalysis
|
||||||
} from '../config-manager.js';
|
} from '../config-manager.js';
|
||||||
import { getPromptManager } from '../prompt-manager.js';
|
import { getPromptManager } from '../prompt-manager.js';
|
||||||
import generateTaskFiles from './generate-task-files.js';
|
|
||||||
import { COMPLEXITY_REPORT_FILE } from '../../../src/constants/paths.js';
|
|
||||||
import { getDebugFlag, getDefaultSubtasks } from '../config-manager.js';
|
import { getDebugFlag, getDefaultSubtasks } from '../config-manager.js';
|
||||||
import { getPromptManager } from '../prompt-manager.js';
|
import { getPromptManager } from '../prompt-manager.js';
|
||||||
import { findProjectRoot, flattenTasksWithSubtasks } from '../utils.js';
|
import { findProjectRoot, flattenTasksWithSubtasks } from '../utils.js';
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { BaseTaskSchema } from './base-schemas.js';
|
import { BaseTaskSchema, SubtaskSchema } from './base-schemas.js';
|
||||||
|
|
||||||
export const UpdatedTaskSchema = BaseTaskSchema.extend({
|
export const UpdatedTaskSchema = BaseTaskSchema.extend({
|
||||||
subtasks: z.array(z.any()).nullable().default(null)
|
subtasks: z.array(SubtaskSchema).nullable().default(null)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const UpdateTasksResponseSchema = z.object({
|
export const UpdateTasksResponseSchema = z.object({
|
||||||
|
|||||||
@@ -38,17 +38,16 @@ describe('Prompt Migration Validation', () => {
|
|||||||
if (lowerContent.includes(lowerPhrase)) {
|
if (lowerContent.includes(lowerPhrase)) {
|
||||||
// Check if this phrase is allowed in its context
|
// Check if this phrase is allowed in its context
|
||||||
const allowedInContext = allowedContexts[lowerPhrase];
|
const allowedInContext = allowedContexts[lowerPhrase];
|
||||||
if (allowedInContext) {
|
const isAllowed =
|
||||||
const isAllowed = allowedInContext.some((context) =>
|
allowedInContext &&
|
||||||
|
allowedInContext.some((context) =>
|
||||||
lowerContent.includes(context.toLowerCase())
|
lowerContent.includes(context.toLowerCase())
|
||||||
);
|
);
|
||||||
if (isAllowed) {
|
|
||||||
return; // Skip this phrase - it's allowed in this context
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we get here, the phrase is not allowed
|
expect(isAllowed).toBe(
|
||||||
expect(lowerContent).not.toContain(lowerPhrase);
|
true,
|
||||||
|
`File ${file} contains banned phrase "${phrase}" without allowed context`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -136,3 +136,204 @@ describe('updateTaskById validation', () => {
|
|||||||
expect(log).toHaveBeenCalled();
|
expect(log).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('updateTaskById success path with generateObjectService', () => {
|
||||||
|
let fs;
|
||||||
|
let generateObjectService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
jest.spyOn(process, 'exit').mockImplementation(() => {
|
||||||
|
throw new Error('process.exit called');
|
||||||
|
});
|
||||||
|
fs = await import('fs');
|
||||||
|
const aiServices = await import(
|
||||||
|
'../../../../../scripts/modules/ai-services-unified.js'
|
||||||
|
);
|
||||||
|
generateObjectService = aiServices.generateObjectService;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('successfully updates task with all fields from generateObjectService', async () => {
|
||||||
|
fs.existsSync.mockReturnValue(true);
|
||||||
|
readJSON.mockReturnValue({
|
||||||
|
tag: 'master',
|
||||||
|
tasks: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: 'Original Task',
|
||||||
|
description: 'Original description',
|
||||||
|
status: 'pending',
|
||||||
|
dependencies: [],
|
||||||
|
priority: 'low',
|
||||||
|
details: null,
|
||||||
|
testStrategy: null,
|
||||||
|
subtasks: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const updatedTaskData = {
|
||||||
|
id: 1,
|
||||||
|
title: 'Updated Task',
|
||||||
|
description: 'Updated description',
|
||||||
|
status: 'pending',
|
||||||
|
dependencies: [2],
|
||||||
|
priority: 'high',
|
||||||
|
details: 'New implementation details',
|
||||||
|
testStrategy: 'Unit tests required',
|
||||||
|
subtasks: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: 'Subtask 1',
|
||||||
|
description: 'First subtask',
|
||||||
|
status: 'pending',
|
||||||
|
dependencies: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
generateObjectService.mockResolvedValue({
|
||||||
|
mainResult: {
|
||||||
|
task: updatedTaskData
|
||||||
|
},
|
||||||
|
telemetryData: {
|
||||||
|
model: 'claude-3-5-sonnet-20241022',
|
||||||
|
inputTokens: 100,
|
||||||
|
outputTokens: 200
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await updateTaskById(
|
||||||
|
'tasks/tasks.json',
|
||||||
|
1,
|
||||||
|
'Update task with new requirements',
|
||||||
|
false,
|
||||||
|
{ tag: 'master' },
|
||||||
|
'json'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify generateObjectService was called (not generateTextService)
|
||||||
|
expect(generateObjectService).toHaveBeenCalled();
|
||||||
|
const callArgs = generateObjectService.mock.calls[0][0];
|
||||||
|
|
||||||
|
// Verify correct arguments were passed
|
||||||
|
expect(callArgs).toMatchObject({
|
||||||
|
role: 'main',
|
||||||
|
commandName: 'update-task',
|
||||||
|
objectName: 'task'
|
||||||
|
});
|
||||||
|
expect(callArgs.schema).toBeDefined();
|
||||||
|
expect(callArgs.systemPrompt).toContain('update a software development task');
|
||||||
|
expect(callArgs.prompt).toContain('Update task with new requirements');
|
||||||
|
|
||||||
|
// Verify the returned task contains all expected fields
|
||||||
|
expect(result).toEqual({
|
||||||
|
updatedTask: expect.objectContaining({
|
||||||
|
id: 1,
|
||||||
|
title: 'Updated Task',
|
||||||
|
description: 'Updated description',
|
||||||
|
status: 'pending',
|
||||||
|
dependencies: [2],
|
||||||
|
priority: 'high',
|
||||||
|
details: 'New implementation details',
|
||||||
|
testStrategy: 'Unit tests required',
|
||||||
|
subtasks: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: 1,
|
||||||
|
title: 'Subtask 1',
|
||||||
|
description: 'First subtask',
|
||||||
|
status: 'pending'
|
||||||
|
})
|
||||||
|
])
|
||||||
|
}),
|
||||||
|
telemetryData: expect.objectContaining({
|
||||||
|
model: 'claude-3-5-sonnet-20241022',
|
||||||
|
inputTokens: 100,
|
||||||
|
outputTokens: 200
|
||||||
|
}),
|
||||||
|
tagInfo: undefined
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handles generateObjectService with malformed mainResult', async () => {
|
||||||
|
fs.existsSync.mockReturnValue(true);
|
||||||
|
readJSON.mockReturnValue({
|
||||||
|
tag: 'master',
|
||||||
|
tasks: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: 'Task',
|
||||||
|
description: 'Description',
|
||||||
|
status: 'pending',
|
||||||
|
dependencies: [],
|
||||||
|
priority: 'medium',
|
||||||
|
details: null,
|
||||||
|
testStrategy: null,
|
||||||
|
subtasks: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
generateObjectService.mockResolvedValue({
|
||||||
|
mainResult: {
|
||||||
|
task: null // Malformed: task is null
|
||||||
|
},
|
||||||
|
telemetryData: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
updateTaskById(
|
||||||
|
'tasks/tasks.json',
|
||||||
|
1,
|
||||||
|
'Update task',
|
||||||
|
false,
|
||||||
|
{ tag: 'master' },
|
||||||
|
'json'
|
||||||
|
)
|
||||||
|
).rejects.toThrow('Received invalid task object from AI');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handles generateObjectService with missing required fields', async () => {
|
||||||
|
fs.existsSync.mockReturnValue(true);
|
||||||
|
readJSON.mockReturnValue({
|
||||||
|
tag: 'master',
|
||||||
|
tasks: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: 'Task',
|
||||||
|
description: 'Description',
|
||||||
|
status: 'pending',
|
||||||
|
dependencies: [],
|
||||||
|
priority: 'medium',
|
||||||
|
details: null,
|
||||||
|
testStrategy: null,
|
||||||
|
subtasks: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
generateObjectService.mockResolvedValue({
|
||||||
|
mainResult: {
|
||||||
|
task: {
|
||||||
|
id: 1,
|
||||||
|
// Missing title and description
|
||||||
|
status: 'pending',
|
||||||
|
dependencies: [],
|
||||||
|
priority: 'medium'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
telemetryData: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
updateTaskById(
|
||||||
|
'tasks/tasks.json',
|
||||||
|
1,
|
||||||
|
'Update task',
|
||||||
|
false,
|
||||||
|
{ tag: 'master' },
|
||||||
|
'json'
|
||||||
|
)
|
||||||
|
).rejects.toThrow('Updated task missing required fields');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user