144 lines
3.8 KiB
TypeScript
144 lines
3.8 KiB
TypeScript
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();
|
|
}
|