diff --git a/src/agent/tests/InputTest.php b/src/agent/tests/InputTest.php new file mode 100644 index 000000000..45f2c5541 --- /dev/null +++ b/src/agent/tests/InputTest.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\AI\Agent\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\AI\Agent\Input; +use Symfony\AI\Platform\Message\Message; +use Symfony\AI\Platform\Message\MessageBag; + +final class InputTest extends TestCase +{ + public function testConstructorSetsProperties() + { + $messageBag = new MessageBag(); + $options = ['temperature' => 0.7, 'max_tokens' => 100]; + + $input = new Input('gpt-4', $messageBag, $options); + + $this->assertSame('gpt-4', $input->getModel()); + $this->assertSame($messageBag, $input->getMessageBag()); + $this->assertSame($options, $input->getOptions()); + } + + public function testConstructorWithDefaultOptions() + { + $messageBag = new MessageBag(); + + $input = new Input('claude-3', $messageBag); + + $this->assertSame('claude-3', $input->getModel()); + $this->assertSame($messageBag, $input->getMessageBag()); + $this->assertSame([], $input->getOptions()); + } + + public function testGetModel() + { + $messageBag = new MessageBag(); + $input = new Input('test-model', $messageBag); + + $this->assertSame('test-model', $input->getModel()); + } + + public function testSetModel() + { + $messageBag = new MessageBag(); + $input = new Input('original-model', $messageBag); + + $input->setModel('new-model'); + + $this->assertSame('new-model', $input->getModel()); + } + + public function testGetMessageBag() + { + $messageBag = new MessageBag(); + $messageBag->add(Message::ofUser('Hello')); + + $input = new Input('model', $messageBag); + + $result = $input->getMessageBag(); + + $this->assertSame($messageBag, $result); + $this->assertCount(1, $result); + } + + public function testSetMessageBag() + { + $originalMessageBag = new MessageBag(); + $input = new Input('model', $originalMessageBag); + + $newMessageBag = new MessageBag(); + $newMessageBag->add(Message::ofUser('New message')); + + $input->setMessageBag($newMessageBag); + + $result = $input->getMessageBag(); + $this->assertSame($newMessageBag, $result); + $this->assertCount(1, $result); + } + + public function testGetOptions() + { + $messageBag = new MessageBag(); + $options = ['foo' => 'bar', 'baz' => 42]; + + $input = new Input('model', $messageBag, $options); + + $this->assertSame($options, $input->getOptions()); + } + + public function testSetOptions() + { + $messageBag = new MessageBag(); + $input = new Input('model', $messageBag, ['old' => 'option']); + + $newOptions = ['new' => 'options', 'count' => 3]; + $input->setOptions($newOptions); + + $this->assertSame($newOptions, $input->getOptions()); + } + + public function testSetOptionsReplacesAllOptions() + { + $messageBag = new MessageBag(); + $input = new Input('model', $messageBag, ['a' => 1, 'b' => 2]); + + $input->setOptions(['c' => 3]); + + $options = $input->getOptions(); + $this->assertArrayHasKey('c', $options); + $this->assertArrayNotHasKey('a', $options); + $this->assertArrayNotHasKey('b', $options); + } +} diff --git a/src/agent/tests/OutputTest.php b/src/agent/tests/OutputTest.php new file mode 100644 index 000000000..f48229935 --- /dev/null +++ b/src/agent/tests/OutputTest.php @@ -0,0 +1,146 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\AI\Agent\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\AI\Agent\Output; +use Symfony\AI\Platform\Message\Message; +use Symfony\AI\Platform\Message\MessageBag; +use Symfony\AI\Platform\Result\TextResult; + +final class OutputTest extends TestCase +{ + public function testConstructorSetsProperties() + { + $messageBag = new MessageBag(); + $result = new TextResult('Test content'); + $options = ['temperature' => 0.5]; + + $output = new Output('gpt-4', $result, $messageBag, $options); + + $this->assertSame('gpt-4', $output->getModel()); + $this->assertSame($result, $output->getResult()); + $this->assertSame($messageBag, $output->getMessageBag()); + $this->assertSame($options, $output->getOptions()); + } + + public function testConstructorWithDefaultOptions() + { + $messageBag = new MessageBag(); + $result = new TextResult('Content'); + + $output = new Output('claude-3', $result, $messageBag); + + $this->assertSame('claude-3', $output->getModel()); + $this->assertSame($result, $output->getResult()); + $this->assertSame($messageBag, $output->getMessageBag()); + $this->assertSame([], $output->getOptions()); + } + + public function testGetModel() + { + $messageBag = new MessageBag(); + $result = new TextResult('Test'); + + $output = new Output('test-model', $result, $messageBag); + + $this->assertSame('test-model', $output->getModel()); + } + + public function testGetResult() + { + $messageBag = new MessageBag(); + $result = new TextResult('Expected content'); + + $output = new Output('model', $result, $messageBag); + + $retrievedResult = $output->getResult(); + + $this->assertSame($result, $retrievedResult); + $this->assertSame('Expected content', $retrievedResult->getContent()); + } + + public function testSetResult() + { + $messageBag = new MessageBag(); + $originalResult = new TextResult('Original'); + $output = new Output('model', $originalResult, $messageBag); + + $newResult = new TextResult('New content'); + $output->setResult($newResult); + + $retrievedResult = $output->getResult(); + $this->assertSame($newResult, $retrievedResult); + $this->assertSame('New content', $retrievedResult->getContent()); + } + + public function testGetMessageBag() + { + $messageBag = new MessageBag(); + $messageBag->add(Message::ofUser('User message')); + $messageBag->add(Message::ofAssistant('Assistant reply')); + + $result = new TextResult('Content'); + $output = new Output('model', $result, $messageBag); + + $retrievedMessageBag = $output->getMessageBag(); + + $this->assertSame($messageBag, $retrievedMessageBag); + $this->assertCount(2, $retrievedMessageBag); + } + + public function testGetOptions() + { + $messageBag = new MessageBag(); + $result = new TextResult('Content'); + $options = ['max_tokens' => 500, 'temperature' => 0.8]; + + $output = new Output('model', $result, $messageBag, $options); + + $this->assertSame($options, $output->getOptions()); + $this->assertSame(500, $output->getOptions()['max_tokens']); + $this->assertSame(0.8, $output->getOptions()['temperature']); + } + + public function testModelIsReadOnly() + { + $messageBag = new MessageBag(); + $result = new TextResult('Content'); + + $output = new Output('original-model', $result, $messageBag); + + $this->assertSame('original-model', $output->getModel()); + } + + public function testMessageBagIsReadOnly() + { + $messageBag = new MessageBag(); + $messageBag->add(Message::ofUser('Test')); + + $result = new TextResult('Content'); + $output = new Output('model', $result, $messageBag); + + $retrievedBag = $output->getMessageBag(); + $this->assertSame($messageBag, $retrievedBag); + } + + public function testOptionsIsReadOnly() + { + $messageBag = new MessageBag(); + $result = new TextResult('Content'); + $options = ['key' => 'value']; + + $output = new Output('model', $result, $messageBag, $options); + + $this->assertSame($options, $output->getOptions()); + } +} diff --git a/src/chat/tests/Bridge/HttpFoundation/SessionStoreTest.php b/src/chat/tests/Bridge/HttpFoundation/SessionStoreTest.php new file mode 100644 index 000000000..ddb6e625c --- /dev/null +++ b/src/chat/tests/Bridge/HttpFoundation/SessionStoreTest.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\AI\Chat\Tests\Bridge\HttpFoundation; + +use PHPUnit\Framework\TestCase; +use Symfony\AI\Chat\Bridge\HttpFoundation\SessionStore; +use Symfony\AI\Platform\Message\Message; +use Symfony\AI\Platform\Message\MessageBag; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\SessionInterface; + +final class SessionStoreTest extends TestCase +{ + public function testSetupStoresEmptyMessageBag() + { + $session = $this->createMock(SessionInterface::class); + $requestStack = $this->createMock(RequestStack::class); + + $requestStack->expects($this->once()) + ->method('getSession') + ->willReturn($session); + + $session->expects($this->once()) + ->method('set') + ->with('messages', $this->isInstanceOf(MessageBag::class)); + + $store = new SessionStore($requestStack, 'messages'); + $store->setup(); + } + + public function testSetupWithCustomSessionKey() + { + $session = $this->createMock(SessionInterface::class); + $requestStack = $this->createMock(RequestStack::class); + + $requestStack->expects($this->once()) + ->method('getSession') + ->willReturn($session); + + $session->expects($this->once()) + ->method('set') + ->with('custom_key', $this->isInstanceOf(MessageBag::class)); + + $store = new SessionStore($requestStack, 'custom_key'); + $store->setup(); + } + + public function testSaveStoresMessageBag() + { + $messageBag = new MessageBag(); + $messageBag->add(Message::ofUser('Test message')); + + $session = $this->createMock(SessionInterface::class); + $requestStack = $this->createMock(RequestStack::class); + + $requestStack->expects($this->once()) + ->method('getSession') + ->willReturn($session); + + $session->expects($this->once()) + ->method('set') + ->with('messages', $messageBag); + + $store = new SessionStore($requestStack, 'messages'); + $store->save($messageBag); + } + + public function testLoadReturnsStoredMessages() + { + $messageBag = new MessageBag(); + $messageBag->add(Message::ofUser('Session message')); + + $session = $this->createMock(SessionInterface::class); + $requestStack = $this->createMock(RequestStack::class); + + $requestStack->expects($this->once()) + ->method('getSession') + ->willReturn($session); + + $session->expects($this->once()) + ->method('get') + ->with('messages', $this->isInstanceOf(MessageBag::class)) + ->willReturn($messageBag); + + $store = new SessionStore($requestStack, 'messages'); + $result = $store->load(); + + $this->assertSame($messageBag, $result); + $this->assertCount(1, $result); + } + + public function testLoadReturnsEmptyMessageBagWhenNotSet() + { + $session = $this->createMock(SessionInterface::class); + $requestStack = $this->createMock(RequestStack::class); + + $requestStack->expects($this->once()) + ->method('getSession') + ->willReturn($session); + + $session->expects($this->once()) + ->method('get') + ->with('messages', $this->isInstanceOf(MessageBag::class)) + ->willReturn(new MessageBag()); + + $store = new SessionStore($requestStack, 'messages'); + $result = $store->load(); + + $this->assertInstanceOf(MessageBag::class, $result); + $this->assertCount(0, $result); + } + + public function testDropRemovesSessionKey() + { + $session = $this->createMock(SessionInterface::class); + $requestStack = $this->createMock(RequestStack::class); + + $requestStack->expects($this->once()) + ->method('getSession') + ->willReturn($session); + + $session->expects($this->once()) + ->method('remove') + ->with('messages'); + + $store = new SessionStore($requestStack, 'messages'); + $store->drop(); + } +} diff --git a/src/chat/tests/Bridge/Local/CacheStoreTest.php b/src/chat/tests/Bridge/Local/CacheStoreTest.php new file mode 100644 index 000000000..92d72e634 --- /dev/null +++ b/src/chat/tests/Bridge/Local/CacheStoreTest.php @@ -0,0 +1,164 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\AI\Chat\Tests\Bridge\Local; + +use PHPUnit\Framework\TestCase; +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\AI\Chat\Bridge\Local\CacheStore; +use Symfony\AI\Platform\Message\Message; +use Symfony\AI\Platform\Message\MessageBag; + +final class CacheStoreTest extends TestCase +{ + public function testSetupStoresEmptyMessageBag() + { + $cache = $this->createMock(CacheItemPoolInterface::class); + $cacheItem = $this->createMock(CacheItemInterface::class); + + $cache->expects($this->once()) + ->method('getItem') + ->with('test_key') + ->willReturn($cacheItem); + + $cacheItem->expects($this->once()) + ->method('set') + ->with($this->isInstanceOf(MessageBag::class)); + + $cacheItem->expects($this->once()) + ->method('expiresAfter') + ->with(86400) + ->willReturn($cacheItem); + + $cache->expects($this->once()) + ->method('save') + ->with($cacheItem); + + $store = new CacheStore($cache, 'test_key'); + $store->setup(); + } + + public function testSetupWithCustomTtl() + { + $cache = $this->createMock(CacheItemPoolInterface::class); + $cacheItem = $this->createMock(CacheItemInterface::class); + + $cache->method('getItem')->willReturn($cacheItem); + $cacheItem->method('set')->willReturn($cacheItem); + + $cacheItem->expects($this->once()) + ->method('expiresAfter') + ->with(3600) + ->willReturn($cacheItem); + + $cache->expects($this->once()) + ->method('save') + ->with($cacheItem); + + $store = new CacheStore($cache, 'test_key', 3600); + $store->setup(); + } + + public function testSaveStoresMessageBag() + { + $messageBag = new MessageBag(); + $messageBag->add(Message::ofUser('Test message')); + + $cache = $this->createMock(CacheItemPoolInterface::class); + $cacheItem = $this->createMock(CacheItemInterface::class); + + $cache->expects($this->once()) + ->method('getItem') + ->with('messages') + ->willReturn($cacheItem); + + $cacheItem->expects($this->once()) + ->method('set') + ->with($messageBag); + + $cacheItem->expects($this->once()) + ->method('expiresAfter') + ->with(86400) + ->willReturn($cacheItem); + + $cache->expects($this->once()) + ->method('save') + ->with($cacheItem); + + $store = new CacheStore($cache, 'messages'); + $store->save($messageBag); + } + + public function testLoadReturnsStoredMessages() + { + $messageBag = new MessageBag(); + $messageBag->add(Message::ofUser('Cached message')); + + $cache = $this->createMock(CacheItemPoolInterface::class); + $cacheItem = $this->createMock(CacheItemInterface::class); + + $cache->expects($this->once()) + ->method('getItem') + ->with('test_key') + ->willReturn($cacheItem); + + $cacheItem->expects($this->once()) + ->method('isHit') + ->willReturn(true); + + $cacheItem->expects($this->once()) + ->method('get') + ->willReturn($messageBag); + + $store = new CacheStore($cache, 'test_key'); + $result = $store->load(); + + $this->assertSame($messageBag, $result); + $this->assertCount(1, $result); + } + + public function testLoadReturnsEmptyMessageBagWhenCacheMiss() + { + $cache = $this->createMock(CacheItemPoolInterface::class); + $cacheItem = $this->createMock(CacheItemInterface::class); + + $cache->expects($this->once()) + ->method('getItem') + ->with('test_key') + ->willReturn($cacheItem); + + $cacheItem->expects($this->once()) + ->method('isHit') + ->willReturn(false); + + $cacheItem->expects($this->never()) + ->method('get'); + + $store = new CacheStore($cache, 'test_key'); + $result = $store->load(); + + $this->assertInstanceOf(MessageBag::class, $result); + $this->assertCount(0, $result); + } + + public function testDropDeletesCacheItem() + { + $cache = $this->createMock(CacheItemPoolInterface::class); + + $cache->expects($this->once()) + ->method('deleteItem') + ->with('messages'); + + $store = new CacheStore($cache, 'messages'); + $store->drop(); + } +} diff --git a/src/chat/tests/Bridge/Local/InMemoryStoreTest.php b/src/chat/tests/Bridge/Local/InMemoryStoreTest.php new file mode 100644 index 000000000..dc19e02c3 --- /dev/null +++ b/src/chat/tests/Bridge/Local/InMemoryStoreTest.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\AI\Chat\Tests\Bridge\Local; + +use PHPUnit\Framework\TestCase; +use Symfony\AI\Chat\Bridge\Local\InMemoryStore; +use Symfony\AI\Platform\Message\Content\Text; +use Symfony\AI\Platform\Message\Message; +use Symfony\AI\Platform\Message\MessageBag; +use Symfony\AI\Platform\Message\UserMessage; + +final class InMemoryStoreTest extends TestCase +{ + public function testSetupInitializesEmptyMessageBag() + { + $store = new InMemoryStore(); + $store->setup(); + + $messages = $store->load(); + + $this->assertInstanceOf(MessageBag::class, $messages); + $this->assertCount(0, $messages); + } + + public function testSaveStoresMessageBag() + { + $store = new InMemoryStore(); + + $messageBag = new MessageBag(); + $messageBag->add(Message::ofUser('Hello')); + $messageBag->add(Message::ofAssistant('Hi there')); + + $store->save($messageBag); + + $loadedMessages = $store->load(); + + $this->assertSame($messageBag, $loadedMessages); + $this->assertCount(2, $loadedMessages); + } + + public function testLoadReturnsEmptyMessageBagWhenNotInitialized() + { + $store = new InMemoryStore(); + + $messages = $store->load(); + + $this->assertInstanceOf(MessageBag::class, $messages); + $this->assertCount(0, $messages); + } + + public function testLoadReturnsStoredMessages() + { + $store = new InMemoryStore(); + $store->setup(); + + $messageBag = new MessageBag(); + $messageBag->add(Message::ofUser('Test message')); + + $store->save($messageBag); + + $loadedMessages = $store->load(); + + $this->assertCount(1, $loadedMessages); + $messages = $loadedMessages->getMessages(); + $this->assertInstanceOf(UserMessage::class, $messages[0]); + $this->assertInstanceOf(Text::class, $messages[0]->content[0]); + $this->assertSame('Test message', $messages[0]->content[0]->text); + } + + public function testDropClearsMessages() + { + $store = new InMemoryStore(); + + $messageBag = new MessageBag(); + $messageBag->add(Message::ofUser('Message 1')); + $messageBag->add(Message::ofUser('Message 2')); + + $store->save($messageBag); + $this->assertCount(2, $store->load()); + + $store->drop(); + + $messages = $store->load(); + $this->assertCount(0, $messages); + } + + public function testSetupOptions() + { + $store = new InMemoryStore(); + + $store->setup(['foo' => 'bar']); + + $messages = $store->load(); + $this->assertInstanceOf(MessageBag::class, $messages); + } +}