This commit is contained in:
Bastian Wagner
2026-06-23 15:46:57 +02:00
parent e543a2f4a1
commit 35613eddb6
2 changed files with 255 additions and 17 deletions

View File

@@ -2,6 +2,11 @@ import { ServiceUnavailableException } from '@nestjs/common';
import { AssistantService } from './assistant.service';
describe('AssistantService', () => {
const connectorSystemMessage = [
'Benutze fuer Listify-Daten immer den listify Connector.',
'Wenn der User eine Liste anlegen will, musst du create_list verwenden und darfst keinen JSON-Entwurf als Antwort ausgeben.',
'Wenn der Connector keine Liste erstellt, sage kurz, dass die Liste nicht angelegt werden konnte.',
].join(' ');
const originalFetch = global.fetch;
const originalApiKey = process.env.MISTRAL_API_KEY;
const originalAgentId = process.env.MISTRAL_AGENT_ID;
@@ -65,7 +70,7 @@ describe('AssistantService', () => {
const result = await service.chat('user-1', {
messages: [
{ role: 'assistant', content: 'Hallo' },
{ role: 'user', content: 'Erstelle eine Liste' },
{ role: 'user', content: 'Hallo' },
],
});
@@ -81,8 +86,8 @@ describe('AssistantService', () => {
agent_id: 'agent-listify',
messages: [
{ role: 'assistant', content: 'Hallo' },
{ role: 'user', content: 'Erstelle eine Liste' },
{ role: 'system', content: 'benutze immer den listify connector' },
{ role: 'user', content: 'Hallo' },
{ role: 'system', content: connectorSystemMessage },
],
tools: [
{
@@ -281,7 +286,7 @@ describe('AssistantService', () => {
expect(contextContent).not.toContain('checkedByUserId');
expect(payload.messages.at(-1)).toEqual({
role: 'system',
content: 'benutze immer den listify connector',
content: connectorSystemMessage,
});
});
@@ -480,6 +485,118 @@ describe('AssistantService', () => {
);
});
it('extracts created list actions from raw tool JSON content', async () => {
const createdList = {
id: 'list-1',
ownerId: 'user-1',
accessRole: 'owner',
name: 'Einkauf',
kind: 'shopping',
items: [
{
id: 'item-1',
title: 'Milch',
required: true,
checked: false,
position: 0,
createdAt: '2026-06-12T00:00:00.000Z',
updatedAt: '2026-06-12T00:00:00.000Z',
},
],
collaborators: [],
createdAt: '2026-06-12T00:00:00.000Z',
updatedAt: '2026-06-12T00:00:00.000Z',
};
const providerResponse = {
choices: [
{
messages: [
{
role: 'assistant',
content: '',
tool_calls: [
{
id: 'call-1',
type: 'function',
function: {
name: 'listify_create_list',
arguments: '{"name": "Einkauf"}',
},
},
],
},
{
role: 'tool',
content: [
{
type: 'text',
text: JSON.stringify({ list: createdList }),
},
],
tool_call_id: 'call-1',
},
{
role: 'assistant',
content: JSON.stringify({ list: createdList }),
},
],
},
],
};
mockMistralResponse(providerResponse);
const result = await service.chat('user-1', {
messages: [{ role: 'user', content: 'Erstelle eine Einkaufsliste' }],
});
expect(result).toEqual({
message: {
role: 'assistant',
content: 'Ich habe die Liste **Einkauf** angelegt.',
},
actions: [
{
type: 'list.created',
listId: 'list-1',
list: createdList,
},
],
});
expect(listRealtimeService.publishSnapshot).toHaveBeenCalledWith(
'user-1',
createdList,
);
});
it('returns a clear message when list creation did not use the connector', async () => {
mockMistralResponse({
choices: [
{
message: {
content: JSON.stringify({
name: 'Einkauf',
items: [{ title: 'Milch' }],
}),
},
},
],
});
const result = await service.chat('user-1', {
messages: [{ role: 'user', content: 'Erstelle eine Einkaufsliste' }],
});
expect(result).toEqual({
message: {
role: 'assistant',
content:
'Ich konnte die Liste nicht anlegen, weil der Listify-Connector keine Liste erstellt hat.',
},
actions: [],
});
expect(listRealtimeService.publishSnapshot).not.toHaveBeenCalled();
});
it('fails clearly when the api key is missing', async () => {
delete process.env.MISTRAL_API_KEY;