@@ -206,7 +206,7 @@ describe("DM Replay Tests", () => {
206206 expect ( state . mentionMessage ) . not . toBeNull ( ) ;
207207 } ) ;
208208
209- it ( "should receive user DM as new mention (threadTs differs from openDM subscription) " , async ( ) => {
209+ it ( "should receive user reply in DM when subscribed " , async ( ) => {
210210 // Step 1: Initial mention to subscribe
211211 await sendWebhook ( slackFixtures . mention ) ;
212212 expect ( state . mentionMessage ) . not . toBeNull ( ) ;
@@ -216,22 +216,23 @@ describe("DM Replay Tests", () => {
216216 expect ( state . openDMCalled ) . toBe ( true ) ;
217217 expect ( state . dmThreadId ) . not . toBeNull ( ) ;
218218
219- // Reset mention state to track the DM message
220- state . mentionMessage = null ;
221-
222219 // Step 3: User sends message in DM
223- // With the threadTs fix, top-level DM messages use event.ts as threadTs,
224- // which differs from the openDM subscription's empty threadTs.
225- // So this arrives as a new mention rather than a subscribed message.
226220 await sendWebhook ( slackFixtures . dmMessage ) ;
227221
228- // DM arrives as new mention (isMention=true for DMs)
229- const dmMentionMsg = state . mentionMessage as Message | null ;
230- expect ( dmMentionMsg ) . not . toBeNull ( ) ;
231- expect ( dmMentionMsg ?. text ) . toBe ( "Hey!" ) ;
222+ // Verify the DM reply was captured
223+ expect ( state . dmMessage ) . not . toBeNull ( ) ;
224+ expect ( state . dmMessage ?. text ) . toBe ( "Hey!" ) ;
232225
233226 // Verify the DM message is identified as im channel type
234227 expect ( slackFixtures . dmMessage . event . channel_type ) . toBe ( "im" ) ;
228+
229+ // Verify bot responded to the DM
230+ expect ( mockSlackClient . chat . postMessage ) . toHaveBeenCalledWith (
231+ expect . objectContaining ( {
232+ channel : slackFixtures . dmChannelId ,
233+ text : expect . stringContaining ( "Got your DM: Hey!" ) ,
234+ } )
235+ ) ;
235236 } ) ;
236237 } ) ;
237238
@@ -299,13 +300,13 @@ describe("DM Replay Tests", () => {
299300 expect ( state . mentionMessage ?. text ) . toBe ( "hello hello" ) ;
300301 } ) ;
301302
302- it ( "should use event.ts as threadTs for top-level DM messages" , async ( ) => {
303+ it ( "should use empty threadTs for top-level DM messages" , async ( ) => {
303304 await sendWebhook ( slackDirectFixtures . directDM ) ;
304305
305306 expect ( state . mentionMessage ) . not . toBeNull ( ) ;
306- // Top-level DM → threadId uses event.ts as threadTs
307+ // Top-level DM → threadId is "slack:<channel>:" with empty threadTs
307308 expect ( state . mentionMessage ?. threadId ) . toBe (
308- `slack:${ slackDirectFixtures . dmChannelId } :${ slackDirectFixtures . directDM . event . ts } `
309+ `slack:${ slackDirectFixtures . dmChannelId } :`
309310 ) ;
310311 } ) ;
311312
@@ -320,24 +321,23 @@ describe("DM Replay Tests", () => {
320321 ) ;
321322 } ) ;
322323
323- it ( "should receive follow-up DM as new mention (different threadTs) " , async ( ) => {
324+ it ( "should receive follow-up DM as subscribed message " , async ( ) => {
324325 // First DM triggers onNewMention and subscribes
325326 await sendWebhook ( slackDirectFixtures . directDM ) ;
326327 expect ( state . mentionMessage ) . not . toBeNull ( ) ;
327328
328- // Reset to track the second mention
329- const firstMention = state . mentionMessage ;
330- state . mentionMessage = null ;
331-
332- // Second DM has different ts, so gets a different threadId
333- // and is treated as a new mention rather than a subscribed message
329+ // Second DM (real recorded follow-up) triggers onSubscribedMessage
334330 await sendWebhook ( slackDirectFixtures . followUp ) ;
335331
336- const followUpMention = state . mentionMessage as Message | null ;
337- expect ( followUpMention ) . not . toBeNull ( ) ;
338- expect ( followUpMention ?. text ) . toBe ( "cool!!" ) ;
339- // Verify it's a different thread than the first DM
340- expect ( followUpMention ?. threadId ) . not . toBe ( firstMention ?. threadId ) ;
332+ expect ( state . dmMessage ) . not . toBeNull ( ) ;
333+ expect ( state . dmMessage ?. text ) . toBe ( "cool!!" ) ;
334+
335+ expect ( mockSlackClient . chat . postMessage ) . toHaveBeenCalledWith (
336+ expect . objectContaining ( {
337+ channel : slackDirectFixtures . dmChannelId ,
338+ text : expect . stringContaining ( "Follow-up: cool!!" ) ,
339+ } )
340+ ) ;
341341 } ) ;
342342
343343 it ( "should use thread_ts for DM thread replies" , async ( ) => {
0 commit comments