@@ -432,6 +432,117 @@ describe("mattermost inbound user posts", () => {
432432 expect ( ctx ?. Provider ) . toBe ( "mattermost" ) ;
433433 } ) ;
434434
435+ it ( "uses websocket channel type when REST channel lookup fails" , async ( ) => {
436+ const socket = new FakeWebSocket ( ) ;
437+ const abortController = new AbortController ( ) ;
438+ mockState . abortController = abortController ;
439+ const runtimeCore = createRuntimeCore ( testConfig ) ;
440+ mockState . runtimeCore = runtimeCore ;
441+ mockState . resolveChannelInfo . mockResolvedValue ( null ) ;
442+
443+ const monitor = monitorMattermostProvider ( {
444+ config : testConfig ,
445+ runtime : testRuntime ( ) ,
446+ abortSignal : abortController . signal ,
447+ webSocketFactory : ( ) => socket ,
448+ } ) ;
449+
450+ await vi . waitFor ( ( ) => {
451+ expect ( socket . openListenerCount ) . toBeGreaterThan ( 0 ) ;
452+ } ) ;
453+ socket . emitOpen ( ) ;
454+
455+ await socket . emitMessage ( {
456+ event : "posted" ,
457+ data : {
458+ channel_id : "chan-1" ,
459+ channel_name : "town-square" ,
460+ channel_display_name : "Town Square" ,
461+ channel_type : "O" ,
462+ sender_name : "alice" ,
463+ post : JSON . stringify ( {
464+ id : "post-ws-kind" ,
465+ channel_id : "chan-1" ,
466+ user_id : "user-1" ,
467+ message : "hello with websocket kind" ,
468+ create_at : 1_714_000_000_000 ,
469+ } ) ,
470+ } ,
471+ broadcast : {
472+ channel_id : "chan-1" ,
473+ user_id : "user-1" ,
474+ } ,
475+ } ) ;
476+ socket . emitClose ( 1000 ) ;
477+ await monitor ;
478+
479+ expect ( mockState . dispatchReplyFromConfig ) . toHaveBeenCalledTimes ( 1 ) ;
480+ const ctx = mockState . dispatchReplyFromConfig . mock . calls . at ( 0 ) ?. [ 0 ] . ctx ;
481+ expect ( ctx ?. BodyForAgent ) . toBe ( "hello with websocket kind" ) ;
482+ expect ( ctx ?. ChatType ) . toBe ( "channel" ) ;
483+ expect ( ctx ?. ConversationLabel ) . toBe ( "Town Square id:chan-1" ) ;
484+ expect ( runtimeCore . channel . session . recordInboundSession ) . toHaveBeenCalledTimes ( 1 ) ;
485+ } ) ;
486+
487+ it ( "drops posts when neither REST nor websocket channel type can be resolved" , async ( ) => {
488+ const socket = new FakeWebSocket ( ) ;
489+ const abortController = new AbortController ( ) ;
490+ mockState . abortController = abortController ;
491+ const channelTypeConfig : OpenClawConfig = {
492+ channels : {
493+ mattermost : {
494+ enabled : true ,
495+ baseUrl : "https://mattermost.example.com" ,
496+ botToken : "bot-token" ,
497+ chatmode : "onmessage" ,
498+ dmPolicy : "allowlist" ,
499+ groupPolicy : "open" ,
500+ allowFrom : [ "trusted-user" ] ,
501+ } ,
502+ } ,
503+ } ;
504+ const runtimeCore = createRuntimeCore ( channelTypeConfig ) ;
505+ mockState . runtimeCore = runtimeCore ;
506+ mockState . resolveChannelInfo . mockResolvedValue ( null ) ;
507+
508+ const monitor = monitorMattermostProvider ( {
509+ config : channelTypeConfig ,
510+ runtime : testRuntime ( ) ,
511+ abortSignal : abortController . signal ,
512+ webSocketFactory : ( ) => socket ,
513+ } ) ;
514+
515+ await vi . waitFor ( ( ) => {
516+ expect ( socket . openListenerCount ) . toBeGreaterThan ( 0 ) ;
517+ } ) ;
518+ socket . emitOpen ( ) ;
519+
520+ await socket . emitMessage ( {
521+ event : "posted" ,
522+ data : {
523+ channel_id : "dm-1" ,
524+ sender_name : "mallory" ,
525+ post : JSON . stringify ( {
526+ id : "post-missing-kind" ,
527+ channel_id : "dm-1" ,
528+ user_id : "new-user" ,
529+ message : "hello" ,
530+ create_at : 1_714_000_000_000 ,
531+ } ) ,
532+ } ,
533+ broadcast : {
534+ channel_id : "dm-1" ,
535+ user_id : "new-user" ,
536+ } ,
537+ } ) ;
538+ abortController . abort ( ) ;
539+ socket . emitClose ( 1000 ) ;
540+ await monitor ;
541+
542+ expect ( mockState . dispatchReplyFromConfig ) . not . toHaveBeenCalled ( ) ;
543+ expect ( runtimeCore . channel . session . recordInboundSession ) . not . toHaveBeenCalled ( ) ;
544+ } ) ;
545+
435546 it ( "pins direct-message main route updates to the configured owner" , async ( ) => {
436547 const socket = new FakeWebSocket ( ) ;
437548 const abortController = new AbortController ( ) ;
0 commit comments