@@ -14,6 +14,11 @@ import {
1414import { hasPersistedIMessageEcho } from "./monitor/persisted-echo-cache.js" ;
1515import { sendMessageIMessage } from "./send.js" ;
1616
17+ type TestIMessageRpcClient = IMessageRpcClient & {
18+ requestMock : ReturnType < typeof vi . fn > ;
19+ stopMock : ReturnType < typeof vi . fn > ;
20+ } ;
21+
1722const IMESSAGE_TEST_CFG = {
1823 channels : {
1924 imessage : {
@@ -24,21 +29,29 @@ const IMESSAGE_TEST_CFG = {
2429 } ,
2530} ;
2631
27- function createClient ( result : Record < string , unknown > ) : IMessageRpcClient {
32+ function createClient ( result : Record < string , unknown > ) : TestIMessageRpcClient {
33+ const requestMock = vi . fn ( async ( ) => result ) ;
34+ const stopMock = vi . fn ( async ( ) => { } ) ;
2835 return {
29- request : vi . fn ( async ( ) => result ) ,
30- stop : vi . fn ( async ( ) => { } ) ,
31- } as unknown as IMessageRpcClient ;
36+ request : requestMock ,
37+ stop : stopMock ,
38+ requestMock,
39+ stopMock,
40+ } as unknown as TestIMessageRpcClient ;
3241}
3342
34- function createRejectingClient ( error : Error ) : IMessageRpcClient {
43+ function createRejectingClient ( error : Error ) : TestIMessageRpcClient {
44+ const requestMock = vi . fn ( async ( ) => {
45+ await Promise . resolve ( ) ;
46+ throw error ;
47+ } ) ;
48+ const stopMock = vi . fn ( async ( ) => { } ) ;
3549 return {
36- request : vi . fn ( async ( ) => {
37- await Promise . resolve ( ) ;
38- throw error ;
39- } ) ,
40- stop : vi . fn ( async ( ) => { } ) ,
41- } as unknown as IMessageRpcClient ;
50+ request : requestMock ,
51+ stop : stopMock ,
52+ requestMock,
53+ stopMock,
54+ } as unknown as TestIMessageRpcClient ;
4255}
4356
4457function createApprovalText ( id = "approval-123" ) : string {
@@ -118,7 +131,7 @@ describe("sendMessageIMessage receipts", () => {
118131 expect ( result . echoText ) . toBe ( "<media:image>" ) ;
119132 expect ( result . receipt . primaryPlatformMessageId ) . toBe ( "p:0/media-guid" ) ;
120133 expect ( result . receipt . platformMessageIds ) . toEqual ( [ "p:0/media-guid" ] ) ;
121- expect ( client . request ) . not . toHaveBeenCalled ( ) ;
134+ expect ( client . requestMock ) . not . toHaveBeenCalled ( ) ;
122135 expect ( runCliJson . mock . calls ) . toEqual ( [
123136 [ [ "send-attachment" , "--chat" , "chat-1" , "--file" , "/tmp/image.png" , "--transport" , "auto" ] ] ,
124137 ] ) ;
@@ -162,7 +175,7 @@ describe("sendMessageIMessage receipts", () => {
162175 } ) ;
163176
164177 expect ( result . messageId ) . toBe ( "p:0/media-guid" ) ;
165- expect ( client . request ) . not . toHaveBeenCalled ( ) ;
178+ expect ( client . requestMock ) . not . toHaveBeenCalled ( ) ;
166179 expect ( runCliJson . mock . calls ) . toEqual ( [
167180 [ [ "group" , "--chat-id" , "42" ] ] ,
168181 [
@@ -208,7 +221,7 @@ describe("sendMessageIMessage receipts", () => {
208221 expect ( runCliJson . mock . calls ) . toEqual ( [
209222 [ [ "send-attachment" , "--chat" , "chat-1" , "--file" , "/tmp/image.png" , "--transport" , "auto" ] ] ,
210223 ] ) ;
211- expect ( client . request ) . toHaveBeenCalledWith (
224+ expect ( client . requestMock ) . toHaveBeenCalledWith (
212225 "send" ,
213226 expect . objectContaining ( {
214227 chat_guid : "chat-1" ,
@@ -233,7 +246,7 @@ describe("sendMessageIMessage receipts", () => {
233246
234247 expect ( result . messageId ) . toBe ( "12345" ) ;
235248 expect ( runCliJson . mock . calls ) . toEqual ( [ [ [ "group" , "--chat-id" , "42" ] ] ] ) ;
236- expect ( client . request ) . toHaveBeenCalledWith (
249+ expect ( client . requestMock ) . toHaveBeenCalledWith (
237250 "send" ,
238251 expect . objectContaining ( {
239252 chat_id : 42 ,
@@ -259,7 +272,7 @@ describe("sendMessageIMessage receipts", () => {
259272 runCliJson,
260273 } ) ,
261274 ) . rejects . toThrow ( "attachment delivery failed" ) ;
262- expect ( client . request ) . not . toHaveBeenCalled ( ) ;
275+ expect ( client . requestMock ) . not . toHaveBeenCalled ( ) ;
263276 } ) ;
264277
265278 it ( "routes DM handle media-only sends through send-attachment" , async ( ) => {
@@ -294,7 +307,7 @@ describe("sendMessageIMessage receipts", () => {
294307 isFromMe : true ,
295308 } ) ,
296309 ) ;
297- expect ( client . request ) . not . toHaveBeenCalled ( ) ;
310+ expect ( client . requestMock ) . not . toHaveBeenCalled ( ) ;
298311 } ) ;
299312
300313 it ( "preserves explicit SMS service for bare-handle media sends" , async ( ) => {
@@ -319,7 +332,7 @@ describe("sendMessageIMessage receipts", () => {
319332 "--transport" ,
320333 "auto" ,
321334 ] ) ;
322- expect ( client . request ) . not . toHaveBeenCalled ( ) ;
335+ expect ( client . requestMock ) . not . toHaveBeenCalled ( ) ;
323336 } ) ;
324337
325338 it ( "preserves configured iMessage service for bare-handle media sends" , async ( ) => {
@@ -353,7 +366,7 @@ describe("sendMessageIMessage receipts", () => {
353366 "--transport" ,
354367 "auto" ,
355368 ] ) ;
356- expect ( client . request ) . not . toHaveBeenCalled ( ) ;
369+ expect ( client . requestMock ) . not . toHaveBeenCalled ( ) ;
357370 } ) ;
358371
359372 it ( "keeps national-format phone media sends on the region-aware RPC path" , async ( ) => {
@@ -370,7 +383,7 @@ describe("sendMessageIMessage receipts", () => {
370383 } ) ;
371384
372385 expect ( runCliJson ) . not . toHaveBeenCalled ( ) ;
373- expect ( client . request ) . toHaveBeenCalledWith (
386+ expect ( client . requestMock ) . toHaveBeenCalledWith (
374387 "send" ,
375388 expect . objectContaining ( {
376389 file : "/tmp/image.png" ,
@@ -395,7 +408,7 @@ describe("sendMessageIMessage receipts", () => {
395408 } ) ;
396409
397410 expect ( runCliJson ) . not . toHaveBeenCalled ( ) ;
398- expect ( client . request ) . toHaveBeenCalledWith (
411+ expect ( client . requestMock ) . toHaveBeenCalledWith (
399412 "send" ,
400413 expect . objectContaining ( {
401414 chat_identifier : "team-thread" ,
@@ -428,7 +441,7 @@ describe("sendMessageIMessage receipts", () => {
428441 "--transport" ,
429442 "auto" ,
430443 ] ) ;
431- expect ( client . request ) . toHaveBeenCalledWith (
444+ expect ( client . requestMock ) . toHaveBeenCalledWith (
432445 "send" ,
433446 expect . objectContaining ( {
434447 to : "+15550004567" ,
@@ -495,7 +508,7 @@ describe("sendMessageIMessage receipts", () => {
495508 expect ( runCliJson . mock . calls ) . toEqual ( [
496509 [ [ "send-attachment" , "--chat" , "chat-1" , "--file" , "/tmp/image.png" , "--transport" , "auto" ] ] ,
497510 ] ) ;
498- expect ( client . request ) . toHaveBeenCalledWith (
511+ expect ( client . requestMock ) . toHaveBeenCalledWith (
499512 "send" ,
500513 expect . objectContaining ( {
501514 chat_guid : "chat-1" ,
@@ -522,15 +535,15 @@ describe("sendMessageIMessage receipts", () => {
522535 } ) ;
523536
524537 expect ( createClientImpl ) . toHaveBeenCalledTimes ( 1 ) ;
525- expect ( createdClient . request ) . toHaveBeenCalledWith (
538+ expect ( createdClient . requestMock ) . toHaveBeenCalledWith (
526539 "send" ,
527540 expect . objectContaining ( {
528541 to : "+15550004567" ,
529542 text : "caption" ,
530543 } ) ,
531544 expect . any ( Object ) ,
532545 ) ;
533- expect ( createdClient . stop ) . toHaveBeenCalledOnce ( ) ;
546+ expect ( createdClient . stopMock ) . toHaveBeenCalledOnce ( ) ;
534547 expect ( result . receipt . platformMessageIds ) . toEqual ( [ "p:0/dm-media-guid" , "p:0/caption-guid" ] ) ;
535548 expect ( result . receipt . parts . map ( ( part ) => part . kind ) ) . toEqual ( [ "media" , "text" ] ) ;
536549 } ) ;
@@ -545,7 +558,7 @@ describe("sendMessageIMessage receipts", () => {
545558
546559 expect ( result . sentText ) . toBe ( "literal <media:image> text" ) ;
547560 expect ( result . echoText ) . toBe ( "literal <media:image> text" ) ;
548- expect ( client . request ) . toHaveBeenCalledWith (
561+ expect ( client . requestMock ) . toHaveBeenCalledWith (
549562 "send" ,
550563 expect . objectContaining ( {
551564 chat_id : 42 ,
@@ -619,14 +632,14 @@ describe("sendMessageIMessage receipts", () => {
619632
620633 it ( "recovers approval prompt GUID without resending when rpc send times out" , async ( ) => {
621634 const client = createRejectingClient ( new Error ( "imsg rpc timeout (send)" ) ) ;
622- const createClient = vi . fn ( async ( ) => client ) ;
635+ const createClientImpl = vi . fn ( async ( ) => client ) ;
623636 const runCliJson = vi . fn ( ) ;
624637 const resolveSentMessageGuidImpl = vi . fn ( async ( ) => "p:0/fallback-guid" ) ;
625638 const approvalText = createApprovalText ( ) ;
626639
627640 const result = await sendMessageIMessage ( "chat_id:42" , approvalText , {
628641 config : IMESSAGE_TEST_CFG ,
629- createClient,
642+ createClient : createClientImpl ,
630643 runCliJson,
631644 service : "sms" ,
632645 dbPath : "/Users/me/Library/Messages/chat.db" ,
@@ -635,7 +648,7 @@ describe("sendMessageIMessage receipts", () => {
635648
636649 expect ( result . messageId ) . toBe ( "p:0/fallback-guid" ) ;
637650 expect ( result . guid ) . toBe ( "p:0/fallback-guid" ) ;
638- expect ( client . stop ) . toHaveBeenCalledOnce ( ) ;
651+ expect ( client . stopMock ) . toHaveBeenCalledOnce ( ) ;
639652 expect ( runCliJson ) . not . toHaveBeenCalled ( ) ;
640653 expect ( resolveSentMessageGuidImpl ) . toHaveBeenCalledWith ( {
641654 dbPath : "/Users/me/Library/Messages/chat.db" ,
0 commit comments