1+ <?php
2+
3+ declare (strict_types=1 );
4+
5+ namespace PhpLlm \LlmChain \Tests \Platform \Message ;
6+
7+ use PhpLlm \LlmChain \Platform \Message \AssistantMessage ;
8+ use PhpLlm \LlmChain \Platform \Message \MessageInterface ;
9+ use PhpLlm \LlmChain \Platform \Message \Role ;
10+ use PhpLlm \LlmChain \Platform \Message \SystemMessage ;
11+ use PhpLlm \LlmChain \Platform \Message \ToolCallMessage ;
12+ use PhpLlm \LlmChain \Platform \Message \UserMessage ;
13+ use PhpLlm \LlmChain \Platform \Message \Content \Text ;
14+ use PhpLlm \LlmChain \Platform \Response \ToolCall ;
15+ use PHPUnit \Framework \TestCase ;
16+ use Symfony \Component \Uid \AbstractUid ;
17+ use Symfony \Component \Uid \TimeBasedUidInterface ;
18+ use Symfony \Component \Uid \Ulid ;
19+ use Symfony \Component \Uid \Uuid ;
20+ use Symfony \Component \Uid \UuidV7 ;
21+
22+ final class MessageIdTypeTest extends TestCase
23+ {
24+ public function testAssistantMessageIdImplementsRequiredInterfaces (): void
25+ {
26+ $ message = new AssistantMessage ('test ' );
27+
28+ self ::assertInstanceOf (AbstractUid::class, $ message ->getId ());
29+ self ::assertInstanceOf (TimeBasedUidInterface::class, $ message ->getId ());
30+ self ::assertInstanceOf (Uuid::class, $ message ->getId ());
31+ self ::assertInstanceOf (UuidV7::class, $ message ->getId ());
32+ }
33+
34+ public function testSystemMessageIdImplementsRequiredInterfaces (): void
35+ {
36+ $ message = new SystemMessage ('test ' );
37+
38+ self ::assertInstanceOf (AbstractUid::class, $ message ->getId ());
39+ self ::assertInstanceOf (TimeBasedUidInterface::class, $ message ->getId ());
40+ self ::assertInstanceOf (Uuid::class, $ message ->getId ());
41+ self ::assertInstanceOf (UuidV7::class, $ message ->getId ());
42+ }
43+
44+ public function testUserMessageIdImplementsRequiredInterfaces (): void
45+ {
46+ $ message = new UserMessage (new Text ('test ' ));
47+
48+ self ::assertInstanceOf (AbstractUid::class, $ message ->getId ());
49+ self ::assertInstanceOf (TimeBasedUidInterface::class, $ message ->getId ());
50+ self ::assertInstanceOf (Uuid::class, $ message ->getId ());
51+ self ::assertInstanceOf (UuidV7::class, $ message ->getId ());
52+ }
53+
54+ public function testToolCallMessageIdImplementsRequiredInterfaces (): void
55+ {
56+ $ toolCall = new ToolCall ('test-id ' , 'test-function ' , ['param ' => 'value ' ]);
57+ $ message = new ToolCallMessage ($ toolCall , 'test content ' );
58+
59+ self ::assertInstanceOf (AbstractUid::class, $ message ->getId ());
60+ self ::assertInstanceOf (TimeBasedUidInterface::class, $ message ->getId ());
61+ self ::assertInstanceOf (Uuid::class, $ message ->getId ());
62+ self ::assertInstanceOf (UuidV7::class, $ message ->getId ());
63+ }
64+
65+ public function testMessageIdSupportsToRfc4122Method (): void
66+ {
67+ $ message = new AssistantMessage ('test ' );
68+ $ id = $ message ->getId ();
69+
70+ // Test that toRfc4122() method exists and returns a valid UUID string
71+ $ rfc4122 = $ id ->toRfc4122 ();
72+ self ::assertIsString ($ rfc4122 );
73+ self ::assertMatchesRegularExpression ('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i ' , $ rfc4122 );
74+ }
75+
76+ public function testMessageIdSupportsGetDateTimeMethod (): void
77+ {
78+ $ message = new AssistantMessage ('test ' );
79+ $ id = $ message ->getId ();
80+
81+ // Test that getDateTime() method exists and returns a DateTimeImmutable
82+ $ dateTime = $ id ->getDateTime ();
83+ self ::assertInstanceOf (\DateTimeImmutable::class, $ dateTime );
84+
85+ // The timestamp should be recent (within the last minute)
86+ $ now = new \DateTimeImmutable ();
87+ $ diff = $ now ->getTimestamp () - $ dateTime ->getTimestamp ();
88+ self ::assertLessThanOrEqual (60 , $ diff );
89+ self ::assertGreaterThanOrEqual (0 , $ diff );
90+ }
91+
92+ public function testCustomMessageImplementationWithUuid (): void
93+ {
94+ $ customMessage = new class implements MessageInterface {
95+ public AbstractUid &TimeBasedUidInterface $ id ;
96+
97+ public function __construct () {
98+ $ this ->id = Uuid::v7 ();
99+ }
100+
101+ public function getRole (): Role
102+ {
103+ return Role::User;
104+ }
105+
106+ public function getId (): AbstractUid &TimeBasedUidInterface
107+ {
108+ return $ this ->id ;
109+ }
110+ };
111+
112+ self ::assertInstanceOf (AbstractUid::class, $ customMessage ->getId ());
113+ self ::assertInstanceOf (TimeBasedUidInterface::class, $ customMessage ->getId ());
114+ self ::assertInstanceOf (Uuid::class, $ customMessage ->getId ());
115+
116+ // Verify both methods work
117+ self ::assertIsString ($ customMessage ->getId ()->toRfc4122 ());
118+ self ::assertInstanceOf (\DateTimeImmutable::class, $ customMessage ->getId ()->getDateTime ());
119+ }
120+
121+ public function testCustomMessageImplementationWithUlid (): void
122+ {
123+ $ customMessage = new class implements MessageInterface {
124+ public AbstractUid &TimeBasedUidInterface $ id ;
125+
126+ public function __construct () {
127+ $ this ->id = new Ulid ();
128+ }
129+
130+ public function getRole (): Role
131+ {
132+ return Role::User;
133+ }
134+
135+ public function getId (): AbstractUid &TimeBasedUidInterface
136+ {
137+ return $ this ->id ;
138+ }
139+ };
140+
141+ self ::assertInstanceOf (AbstractUid::class, $ customMessage ->getId ());
142+ self ::assertInstanceOf (TimeBasedUidInterface::class, $ customMessage ->getId ());
143+ self ::assertInstanceOf (Ulid::class, $ customMessage ->getId ());
144+
145+ // Verify both methods work
146+ self ::assertIsString ($ customMessage ->getId ()->toRfc4122 ());
147+ self ::assertInstanceOf (\DateTimeImmutable::class, $ customMessage ->getId ()->getDateTime ());
148+
149+ // ULID specific: verify it's a valid ULID format
150+ self ::assertMatchesRegularExpression ('/^[0-9A-Z]{26}$/i ' , (string ) $ customMessage ->getId ());
151+ }
152+
153+ public function testDifferentUidTypesAreInterchangeable (): void
154+ {
155+ $ uuidMessage = new class implements MessageInterface {
156+ public AbstractUid &TimeBasedUidInterface $ id ;
157+
158+ public function __construct () {
159+ $ this ->id = Uuid::v7 ();
160+ }
161+
162+ public function getRole (): Role
163+ {
164+ return Role::Assistant;
165+ }
166+
167+ public function getId (): AbstractUid &TimeBasedUidInterface
168+ {
169+ return $ this ->id ;
170+ }
171+ };
172+
173+ $ ulidMessage = new class implements MessageInterface {
174+ public AbstractUid &TimeBasedUidInterface $ id ;
175+
176+ public function __construct () {
177+ $ this ->id = new Ulid ();
178+ }
179+
180+ public function getRole (): Role
181+ {
182+ return Role::Assistant;
183+ }
184+
185+ public function getId (): AbstractUid &TimeBasedUidInterface
186+ {
187+ return $ this ->id ;
188+ }
189+ };
190+
191+ // Both should be valid MessageInterface implementations
192+ self ::assertInstanceOf (MessageInterface::class, $ uuidMessage );
193+ self ::assertInstanceOf (MessageInterface::class, $ ulidMessage );
194+
195+ // Both should support the same methods
196+ self ::assertIsString ($ uuidMessage ->getId ()->toRfc4122 ());
197+ self ::assertIsString ($ ulidMessage ->getId ()->toRfc4122 ());
198+
199+ self ::assertInstanceOf (\DateTimeImmutable::class, $ uuidMessage ->getId ()->getDateTime ());
200+ self ::assertInstanceOf (\DateTimeImmutable::class, $ ulidMessage ->getId ()->getDateTime ());
201+ }
202+
203+ public function testMessageIdConversionMethods (): void
204+ {
205+ $ message = new AssistantMessage ('test ' );
206+ $ id = $ message ->getId ();
207+
208+ // Test various AbstractUid conversion methods
209+ self ::assertIsString ($ id ->toBinary ());
210+ self ::assertIsString ($ id ->toBase32 ());
211+ self ::assertIsString ($ id ->toBase58 ());
212+ self ::assertIsString ($ id ->toRfc4122 ());
213+
214+ // Test that conversions produce expected formats
215+ self ::assertEquals (16 , strlen ($ id ->toBinary ()));
216+ self ::assertEquals (26 , strlen ($ id ->toBase32 ()));
217+ self ::assertEquals (22 , strlen ($ id ->toBase58 ()));
218+ self ::assertEquals (36 , strlen ($ id ->toRfc4122 ()));
219+ }
220+
221+ public function testMessageIdComparison (): void
222+ {
223+ $ message1 = new AssistantMessage ('test1 ' );
224+ $ message2 = new AssistantMessage ('test2 ' );
225+
226+ // Messages should have different IDs
227+ self ::assertNotEquals ($ message1 ->getId ()->toRfc4122 (), $ message2 ->getId ()->toRfc4122 ());
228+
229+ // But the same message instance should always return the same ID
230+ self ::assertEquals ($ message1 ->getId ()->toRfc4122 (), $ message1 ->getId ()->toRfc4122 ());
231+ }
232+
233+ public function testMessageIdTimestampOrdering (): void
234+ {
235+ $ message1 = new AssistantMessage ('first ' );
236+ usleep (1000 ); // Sleep for 1ms to ensure different timestamp
237+ $ message2 = new AssistantMessage ('second ' );
238+
239+ $ time1 = $ message1 ->getId ()->getDateTime ();
240+ $ time2 = $ message2 ->getId ()->getDateTime ();
241+
242+ // Second message should have a later timestamp
243+ self ::assertGreaterThanOrEqual ($ time1 ->getTimestamp (), $ time2 ->getTimestamp ());
244+ }
245+ }
0 commit comments