@@ -442,6 +442,215 @@ describe("Slack plugin - chat.getPermalink", () => {
442442 } ) ;
443443} ) ;
444444
445+ describe ( "Slack plugin - chat.postEphemeral" , ( ) => {
446+ let app : SlackTestApp [ "app" ] ;
447+ let store : Store ;
448+
449+ beforeEach ( ( ) => {
450+ ( { app, store } = createTestApp ( ) ) ;
451+ } ) ;
452+
453+ it ( "stores ephemeral messages outside channel history" , async ( ) => {
454+ const ss = getSlackStore ( store ) ;
455+ const ch = ss . channels . all ( ) [ 0 ] ;
456+ const blocks = [ { type : "section" , text : { type : "plain_text" , text : "Only you can see this" } } ] ;
457+
458+ const res = await app . request ( `${ base } /api/chat.postEphemeral` , {
459+ method : "POST" ,
460+ headers : authHeaders ( ) ,
461+ body : JSON . stringify ( { channel : ch . channel_id , user : "U000000001" , blocks } ) ,
462+ } ) ;
463+ const body = ( await res . json ( ) ) as any ;
464+ expect ( body . ok ) . toBe ( true ) ;
465+ expect ( body . message_ts ) . toBeDefined ( ) ;
466+
467+ const ephemeral = ss . ephemeralMessages . findOneBy ( "ts" , body . message_ts ) ;
468+ expect ( ephemeral ?. target_user ) . toBe ( "U000000001" ) ;
469+ expect ( ephemeral ?. blocks ) . toEqual ( blocks ) ;
470+
471+ const historyRes = await app . request ( `${ base } /api/conversations.history` , {
472+ method : "POST" ,
473+ headers : authHeaders ( ) ,
474+ body : JSON . stringify ( { channel : ch . channel_id } ) ,
475+ } ) ;
476+ const history = ( await historyRes . json ( ) ) as any ;
477+ expect ( history . messages ) . toEqual ( [ ] ) ;
478+ } ) ;
479+
480+ it ( "accepts channel membership stored by seeded login name" , async ( ) => {
481+ const ss = getSlackStore ( store ) ;
482+ const ch = ss . channels . all ( ) [ 0 ] ;
483+ ss . channels . update ( ch . id , { members : [ "admin" ] , num_members : 1 } ) ;
484+
485+ const res = await app . request ( `${ base } /api/chat.postEphemeral` , {
486+ method : "POST" ,
487+ headers : authHeaders ( ) ,
488+ body : JSON . stringify ( { channel : ch . channel_id , user : "U000000001" , text : "private" } ) ,
489+ } ) ;
490+ const body = ( await res . json ( ) ) as any ;
491+ expect ( body . ok ) . toBe ( true ) ;
492+ expect ( body . message_ts ) . toBeDefined ( ) ;
493+ } ) ;
494+
495+ it ( "returns user_not_in_channel for a target outside the channel" , async ( ) => {
496+ const ss = getSlackStore ( store ) ;
497+ const ch = ss . channels . all ( ) [ 0 ] ;
498+ ss . users . insert ( {
499+ user_id : "U000000999" ,
500+ team_id : "T000000001" ,
501+ name : "outsider" ,
502+ real_name : "Outsider" ,
503+ email : "outsider@emulate.dev" ,
504+ is_admin : false ,
505+ is_bot : false ,
506+ deleted : false ,
507+ profile : {
508+ display_name : "outsider" ,
509+ real_name : "Outsider" ,
510+ email : "outsider@emulate.dev" ,
511+ image_48 : "" ,
512+ image_192 : "" ,
513+ } ,
514+ } ) ;
515+
516+ const res = await app . request ( `${ base } /api/chat.postEphemeral` , {
517+ method : "POST" ,
518+ headers : authHeaders ( ) ,
519+ body : JSON . stringify ( { channel : ch . channel_id , user : "U000000999" , text : "private" } ) ,
520+ } ) ;
521+ const body = ( await res . json ( ) ) as any ;
522+ expect ( body . ok ) . toBe ( false ) ;
523+ expect ( body . error ) . toBe ( "user_not_in_channel" ) ;
524+ } ) ;
525+ } ) ;
526+
527+ describe ( "Slack plugin - scheduled messages" , ( ) => {
528+ let app : SlackTestApp [ "app" ] ;
529+ let store : Store ;
530+
531+ beforeEach ( ( ) => {
532+ ( { app, store } = createTestApp ( ) ) ;
533+ } ) ;
534+
535+ it ( "schedules, lists, and deletes a message" , async ( ) => {
536+ const ss = getSlackStore ( store ) ;
537+ const ch = ss . channels . all ( ) [ 0 ] ;
538+ const postAt = Math . floor ( Date . now ( ) / 1000 ) + 3600 ;
539+ const blocks = [ { type : "section" , text : { type : "plain_text" , text : "Scheduled block" } } ] ;
540+
541+ const scheduleRes = await app . request ( `${ base } /api/chat.scheduleMessage` , {
542+ method : "POST" ,
543+ headers : authHeaders ( ) ,
544+ body : JSON . stringify ( {
545+ channel : ch . channel_id ,
546+ text : "scheduled message" ,
547+ blocks,
548+ post_at : postAt ,
549+ } ) ,
550+ } ) ;
551+ const scheduled = ( await scheduleRes . json ( ) ) as any ;
552+ expect ( scheduled . ok ) . toBe ( true ) ;
553+ expect ( scheduled . channel ) . toBe ( ch . channel_id ) ;
554+ expect ( scheduled . scheduled_message_id ) . toMatch ( / ^ Q / ) ;
555+ expect ( scheduled . post_at ) . toBe ( postAt ) ;
556+ expect ( scheduled . message ) . toMatchObject ( {
557+ type : "delayed_message" ,
558+ subtype : "bot_message" ,
559+ text : "scheduled message" ,
560+ blocks,
561+ } ) ;
562+
563+ const listRes = await app . request ( `${ base } /api/chat.scheduledMessages.list` , {
564+ method : "POST" ,
565+ headers : authHeaders ( ) ,
566+ body : JSON . stringify ( { channel : ch . channel_id } ) ,
567+ } ) ;
568+ const list = ( await listRes . json ( ) ) as any ;
569+ expect ( list . ok ) . toBe ( true ) ;
570+ expect ( list . scheduled_messages ) . toEqual ( [
571+ expect . objectContaining ( {
572+ id : scheduled . scheduled_message_id ,
573+ channel_id : ch . channel_id ,
574+ post_at : postAt ,
575+ text : "scheduled message" ,
576+ } ) ,
577+ ] ) ;
578+
579+ const deleteRes = await app . request ( `${ base } /api/chat.deleteScheduledMessage` , {
580+ method : "POST" ,
581+ headers : authHeaders ( ) ,
582+ body : JSON . stringify ( { channel : ch . channel_id , scheduled_message_id : scheduled . scheduled_message_id } ) ,
583+ } ) ;
584+ expect ( ( ( await deleteRes . json ( ) ) as any ) . ok ) . toBe ( true ) ;
585+ expect ( ss . scheduledMessages . all ( ) ) . toEqual ( [ ] ) ;
586+ } ) ;
587+
588+ it ( "returns time_in_past for past scheduled messages" , async ( ) => {
589+ const ss = getSlackStore ( store ) ;
590+ const ch = ss . channels . all ( ) [ 0 ] ;
591+
592+ const res = await app . request ( `${ base } /api/chat.scheduleMessage` , {
593+ method : "POST" ,
594+ headers : authHeaders ( ) ,
595+ body : JSON . stringify ( {
596+ channel : ch . channel_id ,
597+ text : "too late" ,
598+ post_at : Math . floor ( Date . now ( ) / 1000 ) - 1 ,
599+ } ) ,
600+ } ) ;
601+ const body = ( await res . json ( ) ) as any ;
602+ expect ( body . ok ) . toBe ( false ) ;
603+ expect ( body . error ) . toBe ( "time_in_past" ) ;
604+ } ) ;
605+
606+ it ( "returns invalid_arguments for nonpositive scheduled list limits" , async ( ) => {
607+ const res = await app . request ( `${ base } /api/chat.scheduledMessages.list` , {
608+ method : "POST" ,
609+ headers : authHeaders ( ) ,
610+ body : JSON . stringify ( { limit : - 1 } ) ,
611+ } ) ;
612+ const body = ( await res . json ( ) ) as any ;
613+ expect ( body . ok ) . toBe ( false ) ;
614+ expect ( body . error ) . toBe ( "invalid_arguments" ) ;
615+ } ) ;
616+
617+ it ( "returns invalid_arguments for invalid scheduled list time filters" , async ( ) => {
618+ const res = await app . request ( `${ base } /api/chat.scheduledMessages.list` , {
619+ method : "POST" ,
620+ headers : authHeaders ( ) ,
621+ body : JSON . stringify ( { oldest : "not-a-time" } ) ,
622+ } ) ;
623+ const body = ( await res . json ( ) ) as any ;
624+ expect ( body . ok ) . toBe ( false ) ;
625+ expect ( body . error ) . toBe ( "invalid_arguments" ) ;
626+ } ) ;
627+
628+ it ( "returns invalid_cursor for unknown scheduled message list cursors" , async ( ) => {
629+ const res = await app . request ( `${ base } /api/chat.scheduledMessages.list` , {
630+ method : "POST" ,
631+ headers : authHeaders ( ) ,
632+ body : JSON . stringify ( { cursor : "Q000000999" } ) ,
633+ } ) ;
634+ const body = ( await res . json ( ) ) as any ;
635+ expect ( body . ok ) . toBe ( false ) ;
636+ expect ( body . error ) . toBe ( "invalid_cursor" ) ;
637+ } ) ;
638+
639+ it ( "returns invalid_scheduled_message_id for unknown scheduled messages" , async ( ) => {
640+ const ss = getSlackStore ( store ) ;
641+ const ch = ss . channels . all ( ) [ 0 ] ;
642+
643+ const res = await app . request ( `${ base } /api/chat.deleteScheduledMessage` , {
644+ method : "POST" ,
645+ headers : authHeaders ( ) ,
646+ body : JSON . stringify ( { channel : ch . channel_id , scheduled_message_id : "Q000000999" } ) ,
647+ } ) ;
648+ const body = ( await res . json ( ) ) as any ;
649+ expect ( body . ok ) . toBe ( false ) ;
650+ expect ( body . error ) . toBe ( "invalid_scheduled_message_id" ) ;
651+ } ) ;
652+ } ) ;
653+
445654describe ( "Slack plugin - conversations" , ( ) => {
446655 let app : SlackTestApp [ "app" ] ;
447656 let store : Store ;
0 commit comments