mcp support
This commit is contained in:
143
listify-api/src/mcp/list-suggestion-agent.service.spec.ts
Normal file
143
listify-api/src/mcp/list-suggestion-agent.service.spec.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import {
|
||||
ListTemplate,
|
||||
UserList,
|
||||
} from '../list-templates/list-template.types';
|
||||
import { ListTemplatesService } from '../list-templates/list-templates.service';
|
||||
import { ListsService } from '../lists/lists.service';
|
||||
import { ListSuggestionAgentService } from './list-suggestion-agent.service';
|
||||
|
||||
describe('ListSuggestionAgentService', () => {
|
||||
let listsService: Pick<ListsService, 'listLists' | 'createList'>;
|
||||
let listTemplatesService: Pick<ListTemplatesService, 'listTemplates'>;
|
||||
let service: ListSuggestionAgentService;
|
||||
|
||||
beforeEach(() => {
|
||||
listsService = {
|
||||
listLists: jest.fn(),
|
||||
createList: jest.fn(),
|
||||
};
|
||||
listTemplatesService = {
|
||||
listTemplates: jest.fn(),
|
||||
};
|
||||
service = new ListSuggestionAgentService(
|
||||
listsService as ListsService,
|
||||
listTemplatesService as ListTemplatesService,
|
||||
);
|
||||
});
|
||||
|
||||
it('suggests read-only list ideas from matching templates', async () => {
|
||||
jest.mocked(listsService.listLists).mockResolvedValue([
|
||||
list({ name: 'Urlaub: Sommerurlaub' }),
|
||||
]);
|
||||
jest.mocked(listTemplatesService.listTemplates).mockResolvedValue([
|
||||
template({
|
||||
id: 'template-1',
|
||||
name: 'Urlaub',
|
||||
kind: 'packing',
|
||||
items: ['Pass', 'Tickets', 'Ladegeraete'],
|
||||
}),
|
||||
template({
|
||||
id: 'template-2',
|
||||
name: 'Wocheneinkauf',
|
||||
kind: 'shopping',
|
||||
items: ['Milch'],
|
||||
}),
|
||||
]);
|
||||
|
||||
const result = await service.suggestLists('user-1', {
|
||||
goal: 'Sommerurlaub',
|
||||
constraints: ['Handgepaeck beachten'],
|
||||
});
|
||||
|
||||
expect(result.suggestions[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
name: 'Urlaub: Sommerurlaub 2',
|
||||
kind: 'packing',
|
||||
sourceTemplateId: 'template-1',
|
||||
}),
|
||||
);
|
||||
expect(result.suggestions[0].items[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
title: 'Handgepaeck beachten',
|
||||
required: true,
|
||||
}),
|
||||
);
|
||||
expect(listsService.createList).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('falls back to inferred kind when no template matches', async () => {
|
||||
jest.mocked(listsService.listLists).mockResolvedValue([]);
|
||||
jest.mocked(listTemplatesService.listTemplates).mockResolvedValue([]);
|
||||
|
||||
const result = await service.suggestLists('user-1', {
|
||||
goal: 'Projektplanung fuer Release',
|
||||
});
|
||||
|
||||
expect(result.suggestions[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
name: 'Projektplanung fuer Release',
|
||||
kind: 'todo',
|
||||
}),
|
||||
);
|
||||
expect(result.suggestions[0].items).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ title: 'Ziel klaeren' }),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects empty goals and invalid constraints', async () => {
|
||||
await expect(service.suggestLists('user-1', { goal: ' ' })).rejects.toThrow(
|
||||
BadRequestException,
|
||||
);
|
||||
await expect(
|
||||
service.suggestLists('user-1', {
|
||||
goal: 'Liste',
|
||||
constraints: 'bad' as never,
|
||||
}),
|
||||
).rejects.toThrow('Constraints must be an array.');
|
||||
});
|
||||
});
|
||||
|
||||
function template(options: {
|
||||
id: string;
|
||||
name: string;
|
||||
kind: ListTemplate['kind'];
|
||||
items: string[];
|
||||
}): ListTemplate {
|
||||
return {
|
||||
id: options.id,
|
||||
ownerId: 'user-1',
|
||||
name: options.name,
|
||||
kind: options.kind,
|
||||
items: options.items.map((title, position) => ({
|
||||
id: `${options.id}-item-${position}`,
|
||||
title,
|
||||
required: true,
|
||||
position,
|
||||
createdAt: now(),
|
||||
updatedAt: now(),
|
||||
})),
|
||||
createdAt: now(),
|
||||
updatedAt: now(),
|
||||
};
|
||||
}
|
||||
|
||||
function list(options: { name: string }): UserList {
|
||||
return {
|
||||
id: 'list-1',
|
||||
ownerId: 'user-1',
|
||||
accessRole: 'owner',
|
||||
name: options.name,
|
||||
kind: 'packing',
|
||||
items: [],
|
||||
collaborators: [],
|
||||
createdAt: now(),
|
||||
updatedAt: now(),
|
||||
};
|
||||
}
|
||||
|
||||
function now(): string {
|
||||
return new Date(0).toISOString();
|
||||
}
|
||||
Reference in New Issue
Block a user