2525import org .springframework .ai .chat .model .ChatResponse ;
2626import reactor .core .publisher .Flux ;
2727
28+ import java .util .concurrent .CountDownLatch ;
2829import java .util .concurrent .ExecutorService ;
2930import java .util .concurrent .Executors ;
3031import java .util .concurrent .TimeUnit ;
31- import java .util .concurrent .atomic .AtomicReference ;
3232
33+ import static org .junit .jupiter .api .Assertions .assertTrue ;
3334import static org .mockito .ArgumentMatchers .*;
3435import static org .mockito .Mockito .*;
3536
@@ -77,7 +78,7 @@ void scheduleTitleGeneration_sessionNotFound_doesNotCallRename() throws Exceptio
7778
7879 service .scheduleTitleGeneration ("session-1" , "hello" );
7980 executorService .shutdown ();
80- executorService .awaitTermination (5 , TimeUnit .SECONDS );
81+ assertTrue ( executorService .awaitTermination (5 , TimeUnit .SECONDS ), "the title task must actually finish" );
8182
8283 verify (chatSessionService ).findBySessionId ("session-1" );
8384 verify (chatSessionService , never ()).renameSession (anyString (), anyString ());
@@ -90,7 +91,7 @@ void scheduleTitleGeneration_sessionHasCustomTitle_skips() throws Exception {
9091
9192 service .scheduleTitleGeneration ("session-1" , "hello" );
9293 executorService .shutdown ();
93- executorService .awaitTermination (5 , TimeUnit .SECONDS );
94+ assertTrue ( executorService .awaitTermination (5 , TimeUnit .SECONDS ), "the title task must actually finish" );
9495
9596 verify (chatSessionService , never ()).renameSession (anyString (), anyString ());
9697 }
@@ -106,40 +107,44 @@ void scheduleTitleGeneration_generatesAndPersistsTitle() throws Exception {
106107
107108 service .scheduleTitleGeneration ("session-1" , "hello world" );
108109 executorService .shutdown ();
109- executorService .awaitTermination (5 , TimeUnit .SECONDS );
110+ assertTrue ( executorService .awaitTermination (5 , TimeUnit .SECONDS ), "the title task must actually finish" );
110111
111112 verify (chatSessionService ).renameSession (eq ("session-1" ), eq ("Generated Title" ));
112113 verify (sessionEventPublisher ).publishTitleUpdated (eq (1 ), eq ("session-1" ), eq ("Generated Title" ));
113114 }
114115
115116 @ Test
116117 void scheduleTitleGeneration_duplicateSessionId_skipsSecondCall () throws Exception {
117- AtomicReference <ChatSession > sessionRef = new AtomicReference <>(
118- ChatSession .builder ().id ("session-1" ).agentId (1 ).title ("\u65b0 \u4f1a \u8bdd " ).build ());
119- when (chatSessionService .findBySessionId ("session-1" )).thenAnswer (invocation -> sessionRef .get ());
120-
121- doAnswer (invocation -> {
122- String newTitle = invocation .getArgument (1 );
123- ChatSession session = sessionRef .get ();
124- ChatSession newSession = ChatSession .builder ()
125- .title (newTitle )
126- .id (session .getId ())
127- .agentId (session .getAgentId ())
128- .build ();
129- sessionRef .set (newSession );
130- return null ;
131- }).when (chatSessionService ).renameSession (anyString (), anyString ());
118+ ChatSession session = ChatSession .builder ().id ("session-1" ).agentId (1 ).title ("\u65b0 \u4f1a \u8bdd " ).build ();
119+ CountDownLatch firstTaskStarted = new CountDownLatch (1 );
120+ CountDownLatch allowFirstTaskToFinish = new CountDownLatch (1 );
121+ when (chatSessionService .findBySessionId ("session-1" )).thenAnswer (invocation -> {
122+ firstTaskStarted .countDown ();
123+ if (!allowFirstTaskToFinish .await (5 , TimeUnit .SECONDS )) {
124+ throw new IllegalStateException ("timed out waiting to finish the first title task" );
125+ }
126+ return session ;
127+ });
132128
133129 Flux <ChatResponse > chatResponseFlux = Flux .empty ();
134130 when (llmService .call (anyString (), anyString ())).thenReturn (chatResponseFlux );
135131 when (llmService .toStringFlux (chatResponseFlux )).thenReturn (Flux .just ("Title" ));
136132
137133 service .scheduleTitleGeneration ("session-1" , "hello" );
138- service .scheduleTitleGeneration ("session-1" , "hello again" );
134+ try {
135+ assertTrue (firstTaskStarted .await (5 , TimeUnit .SECONDS ), "the first title task must actually start" );
136+ service .scheduleTitleGeneration ("session-1" , "hello again" );
137+ }
138+ finally {
139+ allowFirstTaskToFinish .countDown ();
140+ }
139141 executorService .shutdown ();
140- executorService .awaitTermination (5 , TimeUnit .SECONDS );
142+ assertTrue ( executorService .awaitTermination (5 , TimeUnit .SECONDS ), "the title task must actually finish" );
141143
142- verify (chatSessionService , atMostOnce ()).renameSession (anyString (), anyString ());
144+ verify (chatSessionService , times (1 )).findBySessionId ("session-1" );
145+ verify (llmService , times (1 )).call (anyString (), anyString ());
146+ verify (chatSessionService , times (1 )).renameSession ("session-1" , "Title" );
147+ verify (sessionEventPublisher , times (1 )).publishTitleUpdated (1 , "session-1" , "Title" );
143148 }
144149
145150}
0 commit comments