Skip to content

Commit 0108ab5

Browse files
authored
[OneChat] FTR tests for agents (elastic#228559)
## Summary Add FTR tests for agent API Additionally: - fix error msg - fix update logic to throw 404 on non-existent id instead of crashing with 500
1 parent fac46b4 commit 0108ab5

File tree

4 files changed

+382
-3
lines changed

4 files changed

+382
-3
lines changed

x-pack/platform/plugins/shared/onechat/server/services/agents/client/client.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,19 @@ class AgentClientImpl implements AgentClient {
182182
}
183183

184184
const now = new Date();
185-
const document = await this.storage.getClient().get({ id: agentId });
185+
186+
let document: Document;
187+
try {
188+
document = await this.storage.getClient().get({ id: agentId });
189+
} catch (e) {
190+
if (e instanceof esErrors.ResponseError && e.statusCode === 404) {
191+
throw createAgentNotFoundError({
192+
agentId,
193+
});
194+
} else {
195+
throw e;
196+
}
197+
}
186198

187199
if (!hasAccess({ profile: document, user: this.user })) {
188200
throw createAgentNotFoundError({ agentId });

x-pack/platform/plugins/shared/onechat/server/services/agents/client/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const idRegexp = /^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?$/;
1717

1818
export const ensureValidId = (id: string) => {
1919
if (!idRegexp.test(id)) {
20-
throw createBadRequestError(`Invalid profile id: ${id}`);
20+
throw createBadRequestError(`Invalid agent id: ${id}`);
2121
}
2222
};
2323

Lines changed: 366 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import expect from '@kbn/expect';
9+
import { FtrProviderContext } from '../../api_integration/ftr_provider_context';
10+
11+
export default function ({ getService }: FtrProviderContext) {
12+
const supertest = getService('supertest');
13+
const kibanaServer = getService('kibanaServer');
14+
const log = getService('log');
15+
16+
describe('Agent API', () => {
17+
const createdAgentIds: string[] = [];
18+
19+
const mockAgent = {
20+
id: 'test-agent',
21+
name: 'Test Agent',
22+
description: 'A test agent for API testing',
23+
configuration: {
24+
instructions: 'You are a helpful test agent',
25+
tools: [
26+
{
27+
type: 'builtin',
28+
tool_ids: ['*'],
29+
},
30+
],
31+
},
32+
};
33+
34+
after(async () => {
35+
for (const agentId of createdAgentIds) {
36+
try {
37+
await supertest
38+
.delete(`/api/chat/agents/${agentId}`)
39+
.set('kbn-xsrf', 'kibana')
40+
.expect(200);
41+
} catch (error) {
42+
log.warning(`Failed to delete agent ${agentId}: ${error.message}`);
43+
}
44+
}
45+
46+
await kibanaServer.uiSettings.update({
47+
'onechat:api:enabled': false,
48+
});
49+
});
50+
51+
describe('POST /api/chat/agents', () => {
52+
it('should create a new agent successfully', async () => {
53+
const response = await supertest
54+
.post('/api/chat/agents')
55+
.set('kbn-xsrf', 'kibana')
56+
.send(mockAgent)
57+
.expect(200);
58+
59+
expect(response.body).to.have.property('id', mockAgent.id);
60+
expect(response.body).to.have.property('name', mockAgent.name);
61+
expect(response.body).to.have.property('description', mockAgent.description);
62+
expect(response.body).to.have.property('configuration');
63+
expect(response.body.configuration).to.have.property(
64+
'instructions',
65+
mockAgent.configuration.instructions
66+
);
67+
expect(response.body.configuration.tools).to.eql(mockAgent.configuration.tools);
68+
69+
createdAgentIds.push(mockAgent.id);
70+
});
71+
72+
it('should validate agent ID format', async () => {
73+
const invalidAgent = {
74+
...mockAgent,
75+
id: 'invalid agent id!',
76+
};
77+
78+
const response = await supertest
79+
.post('/api/chat/agents')
80+
.set('kbn-xsrf', 'kibana')
81+
.send(invalidAgent)
82+
.expect(400);
83+
84+
expect(response.body).to.have.property('message');
85+
expect(response.body.message).to.contain('Invalid agent id');
86+
});
87+
88+
it('should require required fields', async () => {
89+
const incompleteAgent = {
90+
id: 'incomplete-agent',
91+
};
92+
93+
await supertest
94+
.post('/api/chat/agents')
95+
.set('kbn-xsrf', 'kibana')
96+
.send(incompleteAgent)
97+
.expect(400);
98+
});
99+
100+
it('should return 404 when agent API is disabled', async () => {
101+
await kibanaServer.uiSettings.update({
102+
'onechat:api:enabled': false,
103+
});
104+
105+
await supertest
106+
.post('/api/chat/agents')
107+
.set('kbn-xsrf', 'kibana')
108+
.send(mockAgent)
109+
.expect(404);
110+
111+
await kibanaServer.uiSettings.update({
112+
'onechat:api:enabled': true,
113+
});
114+
});
115+
116+
it('should validate tool configuration', async () => {
117+
const agentWithInvalidTools = {
118+
...mockAgent,
119+
id: 'invalid-tools-agent',
120+
configuration: {
121+
instructions: 'Test agent with invalid tools',
122+
tools: [
123+
{
124+
type: 'invalid_type',
125+
tool_ids: ['non-existent-tool'],
126+
},
127+
],
128+
},
129+
};
130+
131+
await supertest
132+
.post('/api/chat/agents')
133+
.set('kbn-xsrf', 'kibana')
134+
.send(agentWithInvalidTools)
135+
.expect(400);
136+
});
137+
});
138+
139+
describe('GET /api/chat/agents/get-test-agent', () => {
140+
let testAgentId: string;
141+
142+
before(async () => {
143+
const testAgent = {
144+
...mockAgent,
145+
id: 'get-test-agent',
146+
};
147+
148+
const response = await supertest
149+
.post('/api/chat/agents')
150+
.set('kbn-xsrf', 'kibana')
151+
.send(testAgent)
152+
.expect(200);
153+
154+
testAgentId = response.body.id;
155+
createdAgentIds.push(testAgentId);
156+
});
157+
158+
it('should retrieve an existing agent', async () => {
159+
const response = await supertest.get(`/api/chat/agents/get-test-agent`).expect(200);
160+
161+
expect(response.body).to.have.property('id', 'get-test-agent');
162+
expect(response.body).to.have.property('name', mockAgent.name);
163+
expect(response.body).to.have.property('description', mockAgent.description);
164+
expect(response.body).to.have.property('configuration');
165+
expect(response.body.configuration).to.have.property(
166+
'instructions',
167+
mockAgent.configuration.instructions
168+
);
169+
expect(response.body.configuration.tools).to.eql(mockAgent.configuration.tools);
170+
});
171+
172+
it('should return 404 for non-existent agent', async () => {
173+
const response = await supertest.get('/api/chat/agents/non-existent-agent').expect(404);
174+
175+
expect(response.body).to.have.property('message');
176+
expect(response.body.message).to.contain('not found');
177+
});
178+
179+
it('should return 404 when agent API is disabled', async () => {
180+
await kibanaServer.uiSettings.update({
181+
'onechat:api:enabled': false,
182+
});
183+
184+
await supertest.get(`/api/chat/agents/get-test-agent`).expect(404);
185+
186+
await kibanaServer.uiSettings.update({
187+
'onechat:api:enabled': true,
188+
});
189+
});
190+
});
191+
192+
describe('GET /api/chat/agents', () => {
193+
const testAgentIds: string[] = [];
194+
195+
before(async () => {
196+
for (let i = 0; i < 3; i++) {
197+
const testAgent = {
198+
...mockAgent,
199+
id: `list-test-agent-${i}`,
200+
name: `List Test Agent ${i}`,
201+
};
202+
203+
await supertest
204+
.post('/api/chat/agents')
205+
.set('kbn-xsrf', 'kibana')
206+
.send(testAgent)
207+
.expect(200);
208+
209+
testAgentIds.push(testAgent.id);
210+
createdAgentIds.push(testAgent.id);
211+
}
212+
});
213+
214+
it('should list all agents', async () => {
215+
const response = await supertest.get('/api/chat/agents').expect(200);
216+
217+
expect(response.body).to.have.property('results');
218+
expect(response.body.results).to.be.an('array');
219+
expect(response.body.results.length).to.greaterThan(1);
220+
});
221+
222+
it('should return 404 when agent API is disabled', async () => {
223+
await kibanaServer.uiSettings.update({
224+
'onechat:api:enabled': false,
225+
});
226+
227+
await supertest.get('/api/chat/agents').expect(404);
228+
229+
await kibanaServer.uiSettings.update({
230+
'onechat:api:enabled': true,
231+
});
232+
});
233+
});
234+
235+
describe('PUT /api/chat/agents/update-test-agent', () => {
236+
before(async () => {
237+
const testAgent = {
238+
...mockAgent,
239+
id: 'update-test-agent',
240+
};
241+
242+
await supertest
243+
.post('/api/chat/agents')
244+
.set('kbn-xsrf', 'kibana')
245+
.send(testAgent)
246+
.expect(200);
247+
248+
createdAgentIds.push(testAgent.id);
249+
});
250+
251+
it('should update an existing agent', async () => {
252+
const updates = {
253+
name: 'Updated Test Agent',
254+
description: 'Updated description',
255+
};
256+
257+
const response = await supertest
258+
.put(`/api/chat/agents/update-test-agent`)
259+
.set('kbn-xsrf', 'kibana')
260+
.send(updates)
261+
.expect(200);
262+
263+
expect(response.body).to.have.property('id', 'update-test-agent');
264+
expect(response.body).to.have.property('name', updates.name);
265+
expect(response.body).to.have.property('description', updates.description);
266+
});
267+
268+
it('should update agent configuration', async () => {
269+
const configUpdates = {
270+
configuration: {
271+
instructions: 'Updated instructions for the agent',
272+
tools: [],
273+
},
274+
};
275+
276+
const response = await supertest
277+
.put(`/api/chat/agents/update-test-agent`)
278+
.set('kbn-xsrf', 'kibana')
279+
.send(configUpdates)
280+
.expect(200);
281+
282+
expect(response.body).to.have.property('id', 'update-test-agent');
283+
expect(response.body.configuration).to.have.property(
284+
'instructions',
285+
configUpdates.configuration.instructions
286+
);
287+
expect(response.body.configuration.tools).to.eql(configUpdates.configuration.tools);
288+
});
289+
290+
it('should return 404 for non-existent agent', async () => {
291+
await supertest
292+
.put('/api/chat/agents/non-existent-agent')
293+
.set('kbn-xsrf', 'kibana')
294+
.send({ name: 'Updated name' })
295+
.expect(404);
296+
});
297+
298+
it('should return 404 when agent API is disabled', async () => {
299+
await kibanaServer.uiSettings.update({
300+
'onechat:api:enabled': false,
301+
});
302+
303+
await supertest
304+
.put(`/api/chat/agents/update-test-agent`)
305+
.set('kbn-xsrf', 'kibana')
306+
.send({ name: 'Updated Name' })
307+
.expect(404);
308+
309+
await kibanaServer.uiSettings.update({
310+
'onechat:api:enabled': true,
311+
});
312+
});
313+
});
314+
315+
describe('DELETE /api/chat/agents/delete-test-agent', () => {
316+
before(async () => {
317+
const testAgent = {
318+
...mockAgent,
319+
id: 'delete-test-agent',
320+
};
321+
322+
await supertest
323+
.post('/api/chat/agents')
324+
.set('kbn-xsrf', 'kibana')
325+
.send(testAgent)
326+
.expect(200);
327+
328+
createdAgentIds.push(testAgent.id);
329+
});
330+
331+
it('should delete an existing agent', async () => {
332+
const response = await supertest
333+
.delete(`/api/chat/agents/delete-test-agent`)
334+
.set('kbn-xsrf', 'kibana')
335+
.expect(200);
336+
337+
expect(response.body).to.have.property('success', true);
338+
});
339+
340+
it('should return 404 for non-existent agent', async () => {
341+
const response = await supertest
342+
.delete('/api/chat/agents/non-existent-agent')
343+
.set('kbn-xsrf', 'kibana')
344+
.expect(404);
345+
346+
expect(response.body).to.have.property('message');
347+
expect(response.body.message).to.contain('not found');
348+
});
349+
350+
it('should return 404 when agent API is disabled', async () => {
351+
await kibanaServer.uiSettings.update({
352+
'onechat:api:enabled': false,
353+
});
354+
355+
await supertest
356+
.delete(`/api/chat/agents/delete-test-agent`)
357+
.set('kbn-xsrf', 'kibana')
358+
.expect(404);
359+
360+
await kibanaServer.uiSettings.update({
361+
'onechat:api:enabled': true,
362+
});
363+
});
364+
});
365+
});
366+
}

0 commit comments

Comments
 (0)