2026-02-13 20:34:53 +01:00
import { beforeEach , describe , expect , it , vi } from "vitest" ;
2026-03-20 18:50:25 +00:00
import type { PluginRuntime , RuntimeEnv } from "../runtime-api.js" ;
2026-01-16 22:22:23 +00:00
import { matrixPlugin } from "./channel.js" ;
2026-03-19 01:53:57 -04:00
import { resolveMatrixAccount } from "./matrix/accounts.js" ;
import { resolveMatrixConfigForAccount } from "./matrix/client/config.js" ;
2026-01-18 11:00:19 +00:00
import { setMatrixRuntime } from "./runtime.js" ;
2026-02-18 01:34:35 +00:00
import type { CoreConfig } from "./types.js" ;
2026-01-16 22:22:23 +00:00
describe ( "matrix directory" , ( ) = > {
2026-03-19 01:53:57 -04:00
const runtimeEnv : RuntimeEnv = {
log : vi.fn ( ) ,
error : vi.fn ( ) ,
exit : vi.fn ( ( code : number ) : never = > {
throw new Error ( ` exit ${ code } ` ) ;
} ) ,
} ;
2026-02-17 10:12:34 +09:00
2026-01-18 11:00:19 +00:00
beforeEach ( ( ) = > {
2026-01-18 13:55:56 +00:00
setMatrixRuntime ( {
state : {
2026-02-17 10:12:34 +09:00
resolveStateDir : ( _env , homeDir ) = > ( homeDir ? ? ( ( ) = > "/tmp" ) ) ( ) ,
2026-01-18 13:55:56 +00:00
} ,
} as PluginRuntime ) ;
2026-01-18 11:00:19 +00:00
} ) ;
2026-01-16 22:22:23 +00:00
it ( "lists peers and groups from config" , async ( ) = > {
const cfg = {
channels : {
matrix : {
dm : { allowFrom : [ "matrix:@alice:example.org" , "bob" ] } ,
2026-01-20 09:37:27 +01:00
groupAllowFrom : [ "@dana:example.org" ] ,
groups : {
2026-01-16 22:22:23 +00:00
"!room1:example.org" : { users : [ "@carol:example.org" ] } ,
"#alias:example.org" : { users : [ ] } ,
} ,
} ,
} ,
} as unknown as CoreConfig ;
expect ( matrixPlugin . directory ) . toBeTruthy ( ) ;
expect ( matrixPlugin . directory ? . listPeers ) . toBeTruthy ( ) ;
expect ( matrixPlugin . directory ? . listGroups ) . toBeTruthy ( ) ;
await expect (
2026-02-17 10:12:34 +09:00
matrixPlugin . directory ! . listPeers ! ( {
2026-01-31 21:13:13 +09:00
cfg ,
accountId : undefined ,
query : undefined ,
limit : undefined ,
2026-02-17 10:12:34 +09:00
runtime : runtimeEnv ,
2026-01-31 21:13:13 +09:00
} ) ,
2026-01-16 22:22:23 +00:00
) . resolves . toEqual (
expect . arrayContaining ( [
{ kind : "user" , id : "user:@alice:example.org" } ,
{ kind : "user" , id : "bob" , name : "incomplete id; expected @user:server" } ,
{ kind : "user" , id : "user:@carol:example.org" } ,
2026-01-20 09:37:27 +01:00
{ kind : "user" , id : "user:@dana:example.org" } ,
2026-01-16 22:22:23 +00:00
] ) ,
) ;
await expect (
2026-02-17 10:12:34 +09:00
matrixPlugin . directory ! . listGroups ! ( {
2026-01-31 21:13:13 +09:00
cfg ,
accountId : undefined ,
query : undefined ,
limit : undefined ,
2026-02-17 10:12:34 +09:00
runtime : runtimeEnv ,
2026-01-31 21:13:13 +09:00
} ) ,
2026-01-16 22:22:23 +00:00
) . resolves . toEqual (
expect . arrayContaining ( [
{ kind : "group" , id : "room:!room1:example.org" } ,
{ kind : "group" , id : "#alias:example.org" } ,
] ) ,
) ;
} ) ;
2026-02-13 20:34:53 +01:00
it ( "resolves replyToMode from account config" , ( ) = > {
const cfg = {
channels : {
matrix : {
replyToMode : "off" ,
accounts : {
Assistant : {
replyToMode : "all" ,
} ,
} ,
} ,
} ,
} as unknown as CoreConfig ;
expect ( matrixPlugin . threading ? . resolveReplyToMode ) . toBeTruthy ( ) ;
expect (
matrixPlugin . threading ? . resolveReplyToMode ? . ( {
cfg ,
accountId : "assistant" ,
chatType : "direct" ,
} ) ,
) . toBe ( "all" ) ;
expect (
matrixPlugin . threading ? . resolveReplyToMode ? . ( {
cfg ,
accountId : "default" ,
chatType : "direct" ,
} ) ,
) . toBe ( "off" ) ;
} ) ;
2026-03-19 01:53:57 -04:00
it ( "only exposes real Matrix thread ids in tool context" , ( ) = > {
expect (
matrixPlugin . threading ? . buildToolContext ? . ( {
cfg : { } as CoreConfig ,
context : {
To : "room:!room:example.org" ,
ReplyToId : "$reply" ,
} ,
hasRepliedRef : { value : false } ,
} ) ,
) . toEqual ( {
currentChannelId : "room:!room:example.org" ,
currentThreadTs : undefined ,
hasRepliedRef : { value : false } ,
} ) ;
expect (
matrixPlugin . threading ? . buildToolContext ? . ( {
cfg : { } as CoreConfig ,
context : {
To : "room:!room:example.org" ,
ReplyToId : "$reply" ,
MessageThreadId : "$thread" ,
} ,
hasRepliedRef : { value : true } ,
} ) ,
) . toEqual ( {
currentChannelId : "room:!room:example.org" ,
currentThreadTs : "$thread" ,
hasRepliedRef : { value : true } ,
} ) ;
} ) ;
it ( "exposes Matrix direct user id in dm tool context" , ( ) = > {
expect (
matrixPlugin . threading ? . buildToolContext ? . ( {
cfg : { } as CoreConfig ,
context : {
From : "matrix:@alice:example.org" ,
To : "room:!dm:example.org" ,
ChatType : "direct" ,
MessageThreadId : "$thread" ,
} ,
hasRepliedRef : { value : false } ,
} ) ,
) . toEqual ( {
currentChannelId : "room:!dm:example.org" ,
currentThreadTs : "$thread" ,
currentDirectUserId : "@alice:example.org" ,
hasRepliedRef : { value : false } ,
} ) ;
} ) ;
it ( "accepts raw room ids when inferring Matrix direct user ids" , ( ) = > {
expect (
matrixPlugin . threading ? . buildToolContext ? . ( {
cfg : { } as CoreConfig ,
context : {
From : "user:@alice:example.org" ,
To : "!dm:example.org" ,
ChatType : "direct" ,
} ,
hasRepliedRef : { value : false } ,
} ) ,
) . toEqual ( {
currentChannelId : "!dm:example.org" ,
currentThreadTs : undefined ,
currentDirectUserId : "@alice:example.org" ,
hasRepliedRef : { value : false } ,
} ) ;
} ) ;
2026-02-13 20:34:53 +01:00
it ( "resolves group mention policy from account config" , ( ) = > {
const cfg = {
channels : {
matrix : {
groups : {
"!room:example.org" : { requireMention : true } ,
} ,
accounts : {
Assistant : {
groups : {
"!room:example.org" : { requireMention : false } ,
} ,
} ,
} ,
} ,
} ,
} as unknown as CoreConfig ;
2026-02-17 10:12:34 +09:00
expect ( matrixPlugin . groups ! . resolveRequireMention ! ( { cfg , groupId : "!room:example.org" } ) ) . toBe (
2026-02-13 20:34:53 +01:00
true ,
) ;
expect (
2026-02-17 10:12:34 +09:00
matrixPlugin . groups ! . resolveRequireMention ! ( {
2026-02-13 20:34:53 +01:00
cfg ,
accountId : "assistant" ,
groupId : "!room:example.org" ,
} ) ,
) . toBe ( false ) ;
2026-03-19 01:53:57 -04:00
expect (
matrixPlugin . groups ! . resolveRequireMention ! ( {
cfg ,
accountId : "assistant" ,
groupId : "matrix:room:!room:example.org" ,
} ) ,
) . toBe ( false ) ;
} ) ;
it ( "matches prefixed Matrix aliases in group context" , ( ) = > {
const cfg = {
channels : {
matrix : {
groups : {
"#ops:example.org" : { requireMention : false } ,
} ,
} ,
} ,
} as unknown as CoreConfig ;
expect (
matrixPlugin . groups ! . resolveRequireMention ! ( {
cfg ,
groupId : "matrix:room:!room:example.org" ,
groupChannel : "matrix:channel:#ops:example.org" ,
} ) ,
) . toBe ( false ) ;
} ) ;
it ( "reports room access warnings against the active Matrix config path" , ( ) = > {
expect (
matrixPlugin . security ? . collectWarnings ? . ( {
cfg : {
channels : {
matrix : {
groupPolicy : "open" ,
} ,
} ,
} as CoreConfig ,
account : resolveMatrixAccount ( {
cfg : {
channels : {
matrix : {
groupPolicy : "open" ,
} ,
} ,
} as CoreConfig ,
accountId : "default" ,
} ) ,
} ) ,
) . toEqual ( [
'- Matrix rooms: groupPolicy="open" allows any room to trigger (mention-gated). Set channels.matrix.groupPolicy="allowlist" + channels.matrix.groups (and optionally channels.matrix.groupAllowFrom) to restrict rooms.' ,
] ) ;
expect (
matrixPlugin . security ? . collectWarnings ? . ( {
cfg : {
channels : {
matrix : {
defaultAccount : "assistant" ,
accounts : {
assistant : {
groupPolicy : "open" ,
} ,
} ,
} ,
} ,
} as CoreConfig ,
account : resolveMatrixAccount ( {
cfg : {
channels : {
matrix : {
defaultAccount : "assistant" ,
accounts : {
assistant : {
groupPolicy : "open" ,
} ,
} ,
} ,
} ,
} as CoreConfig ,
accountId : "assistant" ,
} ) ,
} ) ,
) . toEqual ( [
'- Matrix rooms: groupPolicy="open" allows any room to trigger (mention-gated). Set channels.matrix.accounts.assistant.groupPolicy="allowlist" + channels.matrix.accounts.assistant.groups (and optionally channels.matrix.accounts.assistant.groupAllowFrom) to restrict rooms.' ,
] ) ;
} ) ;
it ( "reports invite auto-join warnings only when explicitly enabled" , ( ) = > {
expect (
matrixPlugin . security ? . collectWarnings ? . ( {
cfg : {
channels : {
matrix : {
groupPolicy : "allowlist" ,
autoJoin : "always" ,
} ,
} ,
} as CoreConfig ,
account : resolveMatrixAccount ( {
cfg : {
channels : {
matrix : {
groupPolicy : "allowlist" ,
autoJoin : "always" ,
} ,
} ,
} as CoreConfig ,
accountId : "default" ,
} ) ,
} ) ,
) . toEqual ( [
'- Matrix invites: autoJoin="always" joins any invited room before message policy applies. Set channels.matrix.autoJoin="allowlist" + channels.matrix.autoJoinAllowlist (or channels.matrix.autoJoin="off") to restrict joins.' ,
] ) ;
} ) ;
it ( "writes matrix non-default account credentials under channels.matrix.accounts" , ( ) = > {
const cfg = {
channels : {
matrix : {
homeserver : "https://default.example.org" ,
accessToken : "default-token" ,
deviceId : "DEFAULTDEVICE" ,
avatarUrl : "mxc://server/avatar" ,
encryption : true ,
threadReplies : "inbound" ,
groups : {
"!room:example.org" : { requireMention : true } ,
} ,
} ,
} ,
} as unknown as CoreConfig ;
const updated = matrixPlugin . setup ! . applyAccountConfig ( {
cfg ,
accountId : "ops" ,
input : {
homeserver : "https://matrix.example.org" ,
userId : "@ops:example.org" ,
accessToken : "ops-token" ,
} ,
} ) as CoreConfig ;
expect ( updated . channels ? . [ "matrix" ] ? . accessToken ) . toBeUndefined ( ) ;
expect ( updated . channels ? . [ "matrix" ] ? . deviceId ) . toBeUndefined ( ) ;
expect ( updated . channels ? . [ "matrix" ] ? . avatarUrl ) . toBeUndefined ( ) ;
expect ( updated . channels ? . [ "matrix" ] ? . accounts ? . default ) . toMatchObject ( {
accessToken : "default-token" ,
homeserver : "https://default.example.org" ,
deviceId : "DEFAULTDEVICE" ,
avatarUrl : "mxc://server/avatar" ,
encryption : true ,
threadReplies : "inbound" ,
groups : {
"!room:example.org" : { requireMention : true } ,
} ,
} ) ;
expect ( updated . channels ? . [ "matrix" ] ? . accounts ? . ops ) . toMatchObject ( {
enabled : true ,
homeserver : "https://matrix.example.org" ,
userId : "@ops:example.org" ,
accessToken : "ops-token" ,
} ) ;
expect ( resolveMatrixConfigForAccount ( updated , "ops" , { } ) ) . toMatchObject ( {
homeserver : "https://matrix.example.org" ,
userId : "@ops:example.org" ,
accessToken : "ops-token" ,
deviceId : undefined ,
} ) ;
} ) ;
it ( "writes default matrix account credentials under channels.matrix.accounts.default" , ( ) = > {
const cfg = {
channels : {
matrix : {
homeserver : "https://legacy.example.org" ,
accessToken : "legacy-token" ,
} ,
} ,
} as unknown as CoreConfig ;
const updated = matrixPlugin . setup ! . applyAccountConfig ( {
cfg ,
accountId : "default" ,
input : {
homeserver : "https://matrix.example.org" ,
userId : "@bot:example.org" ,
accessToken : "bot-token" ,
} ,
} ) as CoreConfig ;
expect ( updated . channels ? . [ "matrix" ] ) . toMatchObject ( {
enabled : true ,
homeserver : "https://matrix.example.org" ,
userId : "@bot:example.org" ,
accessToken : "bot-token" ,
} ) ;
expect ( updated . channels ? . [ "matrix" ] ? . accounts ) . toBeUndefined ( ) ;
} ) ;
it ( "requires account-scoped env vars when --use-env is set for non-default accounts" , ( ) = > {
const envKeys = [
"MATRIX_OPS_HOMESERVER" ,
"MATRIX_OPS_USER_ID" ,
"MATRIX_OPS_ACCESS_TOKEN" ,
"MATRIX_OPS_PASSWORD" ,
] as const ;
const previousEnv = Object . fromEntries ( envKeys . map ( ( key ) = > [ key , process . env [ key ] ] ) ) as Record <
( typeof envKeys ) [ number ] ,
string | undefined
> ;
for ( const key of envKeys ) {
delete process . env [ key ] ;
}
try {
const error = matrixPlugin . setup ! . validateInput ? . ( {
cfg : { } as CoreConfig ,
accountId : "ops" ,
input : { useEnv : true } ,
} ) ;
expect ( error ) . toBe (
'Set per-account env vars for "ops" (for example MATRIX_OPS_HOMESERVER + MATRIX_OPS_ACCESS_TOKEN or MATRIX_OPS_USER_ID + MATRIX_OPS_PASSWORD).' ,
) ;
} finally {
for ( const key of envKeys ) {
if ( previousEnv [ key ] === undefined ) {
delete process . env [ key ] ;
} else {
process . env [ key ] = previousEnv [ key ] ;
}
}
}
} ) ;
it ( "accepts --use-env for non-default account when scoped env vars are present" , ( ) = > {
const envKeys = {
MATRIX_OPS_HOMESERVER : process.env.MATRIX_OPS_HOMESERVER ,
MATRIX_OPS_ACCESS_TOKEN : process.env.MATRIX_OPS_ACCESS_TOKEN ,
} ;
process . env . MATRIX_OPS_HOMESERVER = "https://ops.example.org" ;
process . env . MATRIX_OPS_ACCESS_TOKEN = "ops-token" ;
try {
const error = matrixPlugin . setup ! . validateInput ? . ( {
cfg : { } as CoreConfig ,
accountId : "ops" ,
input : { useEnv : true } ,
} ) ;
expect ( error ) . toBeNull ( ) ;
} finally {
for ( const [ key , value ] of Object . entries ( envKeys ) ) {
if ( value === undefined ) {
delete process . env [ key ] ;
} else {
process . env [ key ] = value ;
}
}
}
} ) ;
it ( "clears stored auth fields when switching a Matrix account to env-backed auth" , ( ) = > {
const envKeys = {
MATRIX_OPS_HOMESERVER : process.env.MATRIX_OPS_HOMESERVER ,
MATRIX_OPS_ACCESS_TOKEN : process.env.MATRIX_OPS_ACCESS_TOKEN ,
MATRIX_OPS_DEVICE_ID : process.env.MATRIX_OPS_DEVICE_ID ,
MATRIX_OPS_DEVICE_NAME : process.env.MATRIX_OPS_DEVICE_NAME ,
} ;
process . env . MATRIX_OPS_HOMESERVER = "https://ops.env.example.org" ;
process . env . MATRIX_OPS_ACCESS_TOKEN = "ops-env-token" ;
process . env . MATRIX_OPS_DEVICE_ID = "OPSENVDEVICE" ;
process . env . MATRIX_OPS_DEVICE_NAME = "Ops Env Device" ;
try {
const cfg = {
channels : {
matrix : {
accounts : {
ops : {
homeserver : "https://ops.inline.example.org" ,
userId : "@ops:inline.example.org" ,
accessToken : "ops-inline-token" ,
password : "ops-inline-password" , // pragma: allowlist secret
deviceId : "OPSINLINEDEVICE" ,
deviceName : "Ops Inline Device" ,
encryption : true ,
} ,
} ,
} ,
} ,
} as unknown as CoreConfig ;
const updated = matrixPlugin . setup ! . applyAccountConfig ( {
cfg ,
accountId : "ops" ,
input : {
useEnv : true ,
name : "Ops" ,
} ,
} ) as CoreConfig ;
expect ( updated . channels ? . [ "matrix" ] ? . accounts ? . ops ) . toMatchObject ( {
name : "Ops" ,
enabled : true ,
encryption : true ,
} ) ;
expect ( updated . channels ? . [ "matrix" ] ? . accounts ? . ops ? . homeserver ) . toBeUndefined ( ) ;
expect ( updated . channels ? . [ "matrix" ] ? . accounts ? . ops ? . userId ) . toBeUndefined ( ) ;
expect ( updated . channels ? . [ "matrix" ] ? . accounts ? . ops ? . accessToken ) . toBeUndefined ( ) ;
expect ( updated . channels ? . [ "matrix" ] ? . accounts ? . ops ? . password ) . toBeUndefined ( ) ;
expect ( u pdated . channels ? . [ "matrix" ] ? . accounts ? . ops ? . deviceId ) . toBeUndefined ( ) ;
expect ( updated . channels ? . [ "matrix" ] ? . accounts ? . ops ? . deviceName ) . toBeUndefined ( ) ;
expect ( resolveMatrixConfigForAccount ( updated , "ops" , process . env ) ) . toMatchObject ( {
homeserver : "https://ops.env.example.org" ,
accessToken : "ops-env-token" ,
deviceId : "OPSENVDEVICE" ,
deviceName : "Ops Env Device" ,
} ) ;
} finally {
for ( const [ key , value ] of Object . entries ( envKeys ) ) {
if ( value === undefined ) {
delete process . env [ key ] ;
} else {
process . env [ key ] = value ;
}
}
}
} ) ;
it ( "resolves account id from input name when explicit account id is missing" , ( ) = > {
const accountId = matrixPlugin . setup ! . resolveAccountId ? . ( {
cfg : { } as CoreConfig ,
accountId : undefined ,
input : { name : "Main Bot" } ,
} ) ;
expect ( accountId ) . toBe ( "main-bot" ) ;
} ) ;
it ( "resolves binding account id from agent id when omitted" , ( ) = > {
const accountId = matrixPlugin . setup ! . resolveBindingAccountId ? . ( {
cfg : { } as CoreConfig ,
agentId : "Ops" ,
accountId : undefined ,
} ) ;
expect ( accountId ) . toBe ( "ops" ) ;
} ) ;
it ( "clears stale access token when switching an account to password auth" , ( ) = > {
const cfg = {
channels : {
matrix : {
accounts : {
default : {
homeserver : "https://matrix.example.org" ,
accessToken : "old-token" ,
} ,
} ,
} ,
} ,
} as unknown as CoreConfig ;
const updated = matrixPlugin . setup ! . applyAccountConfig ( {
cfg ,
accountId : "default" ,
input : {
homeserver : "https://matrix.example.org" ,
userId : "@bot:example.org" ,
password : "new-password" , // pragma: allowlist secret
} ,
} ) as CoreConfig ;
expect ( updated . channels ? . [ "matrix" ] ? . accounts ? . default ? . password ) . toBe ( "new-password" ) ;
expect ( updated . channels ? . [ "matrix" ] ? . accounts ? . default ? . accessToken ) . toBeUndefined ( ) ;
} ) ;
it ( "clears stale password when switching an account to token auth" , ( ) = > {
const cfg = {
channels : {
matrix : {
accounts : {
default : {
homeserver : "https://matrix.example.org" ,
userId : "@bot:example.org" ,
password : "old-password" , // pragma: allowlist secret
} ,
} ,
} ,
} ,
} as unknown as CoreConfig ;
const updated = matrixPlugin . setup ! . applyAccountConfig ( {
cfg ,
accountId : "default" ,
input : {
homeserver : "https://matrix.example.org" ,
accessToken : "new-token" ,
} ,
} ) as CoreConfig ;
expect ( updated . channels ? . [ "matrix" ] ? . accounts ? . default ? . accessToken ) . toBe ( "new-token" ) ;
expect ( updated . channels ? . [ "matrix" ] ? . accounts ? . default ? . password ) . toBeUndefined ( ) ;
2026-02-13 20:34:53 +01:00
} ) ;
2026-01-16 22:22:23 +00:00
} ) ;