Skip to content

Commit baf4958

Browse files
committed
add agent tests
1 parent 728be8b commit baf4958

File tree

9 files changed

+240
-19
lines changed

9 files changed

+240
-19
lines changed

src/Backend/Action/Agent/Message/GetAll.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function handle(RequestInterface $request, ParametersInterface $configura
4343
{
4444
return $this->view->getCollection(
4545
(int) $request->get('agent_id'),
46-
(int) $request->get('parent_id'),
46+
(int) $request->get('parent'),
4747
$context
4848
);
4949
}

src/Backend/Action/Agent/Message/Submit.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,12 @@ public function handle(RequestInterface $request, ParametersInterface $configura
4747

4848
assert($body instanceof AgentContent);
4949

50-
$messageId = $this->sender->send(
50+
$message = $this->sender->send(
5151
$request->get('agent_id'),
5252
$body,
5353
$context,
5454
);
5555

56-
return new HttpResponse(201, [], [
57-
'success' => true,
58-
'message' => 'Agent message successfully submitted',
59-
'id' => '' . $messageId,
60-
]);
56+
return new HttpResponse(201, [], $message);
6157
}
6258
}

src/Installation/DataBag.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ public function addAgentMessage(string $agent, string $user, int $origin, string
258258
'user_id' => $this->getReference('fusio_user', $user, $tenantId),
259259
'parent_id' => $parentId !== null ? $parentId : null,
260260
'origin' => $origin,
261-
'content' => $content,
261+
'content' => Parser::encode(['type' => 'text', 'content' => $content]),
262262
'insert_date' => (new \DateTime($date ?? 'now'))->format('Y-m-d H:i:s'),
263263
];
264264
}

src/Installation/NewInstallation.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ private static function getOperations(): array
341341
httpPath: '/agent/$agent_id<[0-9]+|^~>/message',
342342
httpCode: 200,
343343
outgoing: Model\Backend\AgentMessageCollection::class,
344-
parameters: ['parent_id' => PropertyTypeFactory::getInteger()],
344+
parameters: ['parent' => PropertyTypeFactory::getInteger()],
345345
throws: [999 => Model\Common\Message::class],
346346
description: 'Returns a paginated list of agent messages',
347347
),
@@ -350,8 +350,8 @@ private static function getOperations(): array
350350
httpMethod: 'POST',
351351
httpPath: '/agent/$agent_id<[0-9]+|^~>/message',
352352
httpCode: 201,
353-
outgoing: Model\Common\Message::class,
354-
incoming: Model\Backend\AgentMessage::class,
353+
outgoing: Model\Backend\AgentMessage::class,
354+
incoming: Model\Backend\AgentContent::class,
355355
throws: [999 => Model\Common\Message::class],
356356
description: 'Submits a new agent message',
357357
),

src/Service/Agent/Sender.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Fusio\Model\Backend\AgentContent;
2727
use Fusio\Model\Backend\AgentContentObject;
2828
use Fusio\Model\Backend\AgentContentText;
29+
use Fusio\Model\Backend\AgentMessage;
2930
use PSX\Http\Exception\InternalServerErrorException;
3031
use PSX\Http\Exception\NotFoundException;
3132
use PSX\Http\Exception\StatusCodeException;
@@ -36,7 +37,7 @@
3637
use PSX\Schema\SchemaSource;
3738
use PSX\Sql\Condition;
3839
use PSX\Sql\OrderBy;
39-
use Symfony\AI\Agent\Agent;
40+
use Symfony\AI\Agent\AgentInterface;
4041
use Symfony\AI\Platform\Message\Message;
4142
use Symfony\AI\Platform\Message\MessageBag;
4243
use Throwable;
@@ -70,15 +71,15 @@ public function __construct(
7071
$this->objectMapper = new ObjectMapper($schemaManager);
7172
}
7273

73-
public function send(int $agentId, AgentContent $content, ContextInterface $context): int
74+
public function send(int $agentId, AgentContent $content, ContextInterface $context): AgentMessage
7475
{
7576
$row = $this->agentTable->findOneByTenantAndId($context->getTenantId(), $context->getUser()->getCategoryId(), $agentId);
7677
if (!$row instanceof Table\Generated\AgentRow) {
7778
throw new NotFoundException('Could not find provided agent');
7879
}
7980

8081
$agent = $this->connector->getConnection($row->getConnectionId());
81-
if (!$agent instanceof Agent) {
82+
if (!$agent instanceof AgentInterface) {
8283
throw new InternalServerErrorException('Could not resolve agent connection');
8384
}
8485

@@ -135,7 +136,15 @@ public function send(int $agentId, AgentContent $content, ContextInterface $cont
135136

136137
$this->agentTable->commit();
137138

138-
return $messageRow->getId();
139+
$message = new AgentMessage();
140+
$message->setId($messageRow->getId());
141+
$message->setRole(match ($messageRow->getOrigin()) {
142+
Table\Agent\Message::ORIGIN_ASSISTANT => 'assistant',
143+
Table\Agent\Message::ORIGIN_SYSTEM => 'system',
144+
default => 'user',
145+
});
146+
$message->setContent($output);
147+
return $message;
139148
} catch (Throwable $e) {
140149
$this->agentTable->rollBack();
141150

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
/*
3+
* Fusio - Self-Hosted API Management for Builders.
4+
* For the current version and information visit <https://www.fusio-project.org/>
5+
*
6+
* Copyright (c) Christoph Kappestein <christoph.kappestein@gmail.com>
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
namespace Fusio\Impl\Tests\Adapter\Test;
22+
23+
use Fusio\Engine\ConnectionAbstract;
24+
use Fusio\Engine\ParametersInterface;
25+
use Symfony\AI\Agent\MockAgent;
26+
use Symfony\AI\Agent\MockResponse;
27+
28+
/**
29+
* AgentConnection
30+
*
31+
* @author Christoph Kappestein <christoph.kappestein@gmail.com>
32+
* @license http://www.apache.org/licenses/LICENSE-2.0
33+
* @link https://www.fusio-project.org
34+
*/
35+
class AgentConnection extends ConnectionAbstract
36+
{
37+
public function getName(): string
38+
{
39+
return 'Agent-Connection';
40+
}
41+
42+
public function getConnection(ParametersInterface $config): mixed
43+
{
44+
$response = new MockResponse('The answer ist: 42');
45+
46+
$agent = new MockAgent();
47+
$agent->addResponse('What is the meaning of life?', $response);
48+
49+
return $agent;
50+
}
51+
}

tests/Adapter/container.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22

33
use Fusio\Engine\Adapter\ServiceBuilder;
4+
use Fusio\Impl\Tests\Adapter\Test\AgentConnection;
45
use Fusio\Impl\Tests\Adapter\Test\InspectAction;
56
use Fusio\Impl\Tests\Adapter\Test\MimeAction;
67
use Fusio\Impl\Tests\Adapter\Test\Paypal;
@@ -12,6 +13,7 @@
1213

1314
return static function (ContainerConfigurator $container) {
1415
$services = ServiceBuilder::build($container);
16+
$services->set(AgentConnection::class);
1517
$services->set(InspectAction::class);
1618
$services->set(MimeAction::class);
1719
$services->set(Paypal::class);
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<?php
2+
/*
3+
* Fusio - Self-Hosted API Management for Builders.
4+
* For the current version and information visit <https://www.fusio-project.org/>
5+
*
6+
* Copyright (c) Christoph Kappestein <christoph.kappestein@gmail.com>
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
namespace Fusio\Impl\Tests\Backend\Api\Agent\Message;
22+
23+
use Fusio\Impl\Table;
24+
use Fusio\Impl\Tests\DbTestCase;
25+
use PSX\Json\Parser;
26+
27+
/**
28+
* CollectionTest
29+
*
30+
* @author Christoph Kappestein <christoph.kappestein@gmail.com>
31+
* @license http://www.apache.org/licenses/LICENSE-2.0
32+
* @link https://www.fusio-project.org
33+
*/
34+
class CollectionTest extends DbTestCase
35+
{
36+
public function testGet()
37+
{
38+
$response = $this->sendRequest('/backend/agent/1/message', 'GET', array(
39+
'User-Agent' => 'Fusio TestCase',
40+
'Authorization' => 'Bearer da250526d583edabca8ac2f99e37ee39aa02a3c076c0edc6929095e20ca18dcf'
41+
));
42+
43+
$body = (string) $response->getBody();
44+
$expect = <<<'JSON'
45+
{
46+
"totalResults": 1,
47+
"startIndex": 0,
48+
"itemsPerPage": 16,
49+
"entry": [
50+
{
51+
"id": 1,
52+
"role": "user",
53+
"content": {
54+
"type": "text",
55+
"content": "This is a test message"
56+
},
57+
"insertDate": "2026-02-22T19:17:00Z"
58+
}
59+
]
60+
}
61+
JSON;
62+
63+
$this->assertEquals(200, $response->getStatusCode(), $body);
64+
$this->assertJsonStringEqualsJsonString($expect, $body, $body);
65+
}
66+
public function testGetParent()
67+
{
68+
$response = $this->sendRequest('/backend/agent/1/message?parent=1', 'GET', array(
69+
'User-Agent' => 'Fusio TestCase',
70+
'Authorization' => 'Bearer da250526d583edabca8ac2f99e37ee39aa02a3c076c0edc6929095e20ca18dcf'
71+
));
72+
73+
$body = (string) $response->getBody();
74+
$expect = <<<'JSON'
75+
{
76+
"totalResults": 1,
77+
"startIndex": 0,
78+
"itemsPerPage": 16,
79+
"entry": [
80+
{
81+
"id": 2,
82+
"role": "assistant",
83+
"content": {
84+
"type": "text",
85+
"content": "And an agent response"
86+
},
87+
"insertDate": "2026-02-22T19:17:00Z"
88+
}
89+
]
90+
}
91+
JSON;
92+
93+
$this->assertEquals(200, $response->getStatusCode(), $body);
94+
$this->assertJsonStringEqualsJsonString($expect, $body, $body);
95+
}
96+
97+
public function testPost()
98+
{
99+
$response = $this->sendRequest('/backend/agent/1/message', 'POST', array(
100+
'User-Agent' => 'Fusio TestCase',
101+
'Authorization' => 'Bearer da250526d583edabca8ac2f99e37ee39aa02a3c076c0edc6929095e20ca18dcf'
102+
), json_encode([
103+
'type' => 'text',
104+
'content' => 'What is the meaning of life?',
105+
]));
106+
107+
$body = (string) $response->getBody();
108+
$expect = <<<'JSON'
109+
{
110+
"id": 4,
111+
"role": "assistant",
112+
"content": {
113+
"type": "text",
114+
"content": "The answer ist: 42"
115+
}
116+
}
117+
JSON;
118+
119+
$this->assertEquals(201, $response->getStatusCode(), $body);
120+
$this->assertJsonStringEqualsJsonString($expect, $body, $body);
121+
122+
// check database
123+
$sql = $this->connection->createQueryBuilder()
124+
->select('origin', 'content')
125+
->from('fusio_agent_message')
126+
->where('agent_id = :agent_id')
127+
->orderBy('id', 'DESC')
128+
->getSQL();
129+
130+
$row = $this->connection->fetchAssociative($sql, ['agent_id' => 1]);
131+
132+
$this->assertEquals(2, $row['origin']);
133+
$this->assertJsonStringEqualsJsonString(Parser::encode(['type' => 'text', 'content' => 'The answer ist: 42']), $row['content']);
134+
}
135+
136+
public function testPut()
137+
{
138+
$response = $this->sendRequest('/backend/agent/1/message', 'PUT', array(
139+
'User-Agent' => 'Fusio TestCase',
140+
'Authorization' => 'Bearer da250526d583edabca8ac2f99e37ee39aa02a3c076c0edc6929095e20ca18dcf'
141+
), json_encode([
142+
'foo' => 'bar',
143+
]));
144+
145+
$body = (string) $response->getBody();
146+
147+
$this->assertEquals(404, $response->getStatusCode(), $body);
148+
}
149+
150+
public function testDelete()
151+
{
152+
$response = $this->sendRequest('/backend/agent/1/message', 'DELETE', array(
153+
'User-Agent' => 'Fusio TestCase',
154+
'Authorization' => 'Bearer da250526d583edabca8ac2f99e37ee39aa02a3c076c0edc6929095e20ca18dcf'
155+
), json_encode([
156+
'foo' => 'bar',
157+
]));
158+
159+
$body = (string) $response->getBody();
160+
161+
$this->assertEquals(404, $response->getStatusCode(), $body);
162+
}
163+
}

tests/Fixture.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
namespace Fusio\Impl\Tests;
2222

23-
use Fusio\Adapter\Ai\Connection\Agent;
2423
use Fusio\Adapter\File\Connection\Filesystem;
2524
use Fusio\Adapter\Http\Connection\Http;
2625
use Fusio\Adapter\SdkFabric\Connection\Starwars;
@@ -41,6 +40,7 @@
4140
use Fusio\Impl\Provider\Identity\OpenIDConnect;
4241
use Fusio\Impl\Service;
4342
use Fusio\Impl\Table;
43+
use Fusio\Impl\Tests\Adapter\Test\AgentConnection;
4444
use Fusio\Impl\Tests\Adapter\Test\InspectAction;
4545
use Fusio\Impl\Tests\Adapter\Test\MimeAction;
4646
use Fusio\Impl\Tests\Adapter\Test\PaypalConnection;
@@ -133,7 +133,7 @@ private static function appendTestInserts(DataBag $data): void
133133
$data->addConnection('LocalFilesystem', Filesystem::class, Service\Connection\Encrypter::encrypt(['config' => './tests/resources'], $secretKey));
134134
$data->addConnection('FusioHttpClient', Http::class, Service\Connection\Encrypter::encrypt(['url' => 'https://api.fusio-project.org/'], $secretKey));
135135
$data->addConnection('StarwarsSDK', Starwars::class, Service\Connection\Encrypter::encrypt([], $secretKey));
136-
$data->addConnection('Agent', Agent::class, Service\Connection\Encrypter::encrypt(['type' => 'ollama', 'model' => 'gpt-oss:20b-cloud', 'url' => 'http://localhost:11434'], $secretKey));
136+
$data->addConnection('Agent', AgentConnection::class, Service\Connection\Encrypter::encrypt([], $secretKey));
137137
$data->addCronjob('default', 'Test-Cron', '* * * * *', 'Sql-Select-All', ['foo' => 'bar']);
138138
$data->addCronjob('default', 'Second-Cron', '* * * * *', 'Sql-Select-All', ['foo' => 'bar'], taxonomy: 'feature_a');
139139
$data->addCronjobError('Test-Cron', 'Syntax error, malformed JSON');
@@ -143,8 +143,8 @@ private static function appendTestInserts(DataBag $data): void
143143
$data->addFirewall('my_v6_rule', '2001:0db8:85a3:08d3:1319:8a2e:0370:7344', ['foo' => 'bar']);
144144
$data->addForm('my_form', 'test.createFoo', ['foo' => 'bar'], ['foo' => 'bar']);
145145
$data->addAgent('default', 'Agent', Table\Agent::TYPE_GENERAL, 'agent-test', 'An agent test', 'A test agent which always return "Hello World"', ['test_listFoo'], 'schema://Entry-Schema', 'action://Inspect-Action', date: '2026-02-22 13:06:00');
146-
$data->addAgentMessage('agent-test', 'Consumer', Table\Agent\Message::ORIGIN_USER, 'This is a test message');
147-
$data->addAgentMessage('agent-test', 'Consumer', Table\Agent\Message::ORIGIN_ASSISTANT, 'And an agent response', 1);
146+
$data->addAgentMessage('agent-test', 'Administrator', Table\Agent\Message::ORIGIN_USER, 'This is a test message', date: '2026-02-22 19:17:00');
147+
$data->addAgentMessage('agent-test', 'Administrator', Table\Agent\Message::ORIGIN_ASSISTANT, 'And an agent response', 1, date: '2026-02-22 19:17:00');
148148
$data->addWebhook('foo-event', 'Administrator', 'ping', 'http://www.fusio-project.org/ping');
149149
$data->addWebhook('foo-event', 'Consumer', 'pong', 'http://www.fusio-project.org/ping');
150150
$data->addWebhookResponse(1);

0 commit comments

Comments
 (0)