172 lines
6.3 KiB
TypeScript
172 lines
6.3 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { discoverServices } from './services.js';
|
|
import { ChannelRegistry } from '../../channels/registry.js';
|
|
import type { Config } from '../../config/index.js';
|
|
import type { CronJobConfig } from '../../config/schema.js';
|
|
|
|
function withMutableConfig(config: Config): Config & Record<string, unknown> {
|
|
return config as Config & Record<string, unknown>;
|
|
}
|
|
|
|
function makeBaseConfig(): Config {
|
|
return {
|
|
server: { localhost: true, port: 18800 },
|
|
models: { default: { provider: 'anthropic', model: 'claude-sonnet-4', api_key: 'sk-test' }, fallback_chain: ['anthropic'] },
|
|
backends: { native: { enabled: true }, opencode: { enabled: false }, claude_code: { enabled: false } },
|
|
hooks: { confirm: [], log: [], silent: [] },
|
|
mcp: { servers: [] },
|
|
backup: {
|
|
enabled: false,
|
|
schedule: undefined,
|
|
interval: '24h',
|
|
run_on_start: false,
|
|
notify: undefined,
|
|
failure_threshold: 1,
|
|
notify_recovery: true,
|
|
local_dir: '~/.local/share/flynn/backups',
|
|
include_vectors: true,
|
|
minio: {
|
|
enabled: false,
|
|
endpoint: undefined,
|
|
access_key: undefined,
|
|
secret_key: undefined,
|
|
bucket: undefined,
|
|
prefix: 'flynn',
|
|
secure: true,
|
|
},
|
|
},
|
|
automation: {
|
|
cron: [],
|
|
webhooks: [],
|
|
gmail: undefined,
|
|
heartbeat: undefined,
|
|
gcal: undefined,
|
|
gdocs: undefined,
|
|
gdrive: undefined,
|
|
gtasks: undefined,
|
|
},
|
|
telegram: undefined,
|
|
discord: undefined,
|
|
slack: undefined,
|
|
whatsapp: undefined,
|
|
matrix: undefined,
|
|
signal: undefined,
|
|
mattermost: undefined,
|
|
teams: undefined,
|
|
google_chat: undefined,
|
|
bluebubbles: undefined,
|
|
line: undefined,
|
|
feishu: undefined,
|
|
zalo: undefined,
|
|
} as unknown as Config;
|
|
}
|
|
|
|
describe('discoverServices', () => {
|
|
it('includes known services and marks not_configured when disabled', () => {
|
|
const cfg = makeBaseConfig();
|
|
const reg = new ChannelRegistry();
|
|
|
|
const services = discoverServices(cfg, reg);
|
|
|
|
expect(services).toEqual(expect.arrayContaining([
|
|
expect.objectContaining({ name: 'telegram', status: 'not_configured' }),
|
|
expect.objectContaining({ name: 'matrix', status: 'not_configured' }),
|
|
expect.objectContaining({ name: 'signal', status: 'not_configured' }),
|
|
expect.objectContaining({ name: 'mattermost', status: 'not_configured' }),
|
|
expect.objectContaining({ name: 'teams', status: 'not_configured' }),
|
|
expect.objectContaining({ name: 'google_chat', status: 'not_configured' }),
|
|
expect.objectContaining({ name: 'bluebubbles', status: 'not_configured' }),
|
|
expect.objectContaining({ name: 'line', status: 'not_configured' }),
|
|
expect.objectContaining({ name: 'feishu', status: 'not_configured' }),
|
|
expect.objectContaining({ name: 'zalo', status: 'not_configured' }),
|
|
expect.objectContaining({ name: 'cron', status: 'not_configured' }),
|
|
expect.objectContaining({ name: 'daily_briefing', status: 'not_configured' }),
|
|
expect.objectContaining({ name: 'backup', status: 'not_configured' }),
|
|
expect.objectContaining({ name: 'mcp', status: 'not_configured' }),
|
|
expect.objectContaining({ name: 'web_search', status: 'configured' }),
|
|
expect.objectContaining({ name: 'audio_transcription', status: 'not_configured' }),
|
|
]));
|
|
});
|
|
|
|
it('marks configured channels as disconnected when adapter is not registered', () => {
|
|
const cfg = makeBaseConfig();
|
|
withMutableConfig(cfg).telegram = { bot_token: 'x', allowed_chat_ids: [123], require_mention: false };
|
|
|
|
const reg = new ChannelRegistry();
|
|
const services = discoverServices(cfg, reg);
|
|
|
|
expect(services.find(s => s.name === 'telegram')?.status).toBe('disconnected');
|
|
});
|
|
|
|
it('uses adapter status when channel adapter is registered', () => {
|
|
const cfg = makeBaseConfig();
|
|
withMutableConfig(cfg).telegram = { bot_token: 'x', allowed_chat_ids: [123], require_mention: false };
|
|
|
|
const reg = new ChannelRegistry();
|
|
reg.register({
|
|
name: 'telegram',
|
|
status: 'connected',
|
|
connect: async () => {},
|
|
disconnect: async () => {},
|
|
send: async () => {},
|
|
onMessage: () => {},
|
|
});
|
|
|
|
const services = discoverServices(cfg, reg);
|
|
expect(services.find(s => s.name === 'telegram')?.status).toBe('connected');
|
|
});
|
|
|
|
it('marks enabled automation subsystems as configured and carries item counts', () => {
|
|
const cfg = makeBaseConfig();
|
|
cfg.automation.cron = [
|
|
{ name: 'job', schedule: '0 0 * * *', message: 'hi', output: { channel: 'webchat', peer: 'x' }, enabled: true },
|
|
] as CronJobConfig[];
|
|
(cfg.automation as Record<string, unknown>).daily_briefing = {
|
|
enabled: true,
|
|
output: { channel: 'webchat', peer: 'x' },
|
|
};
|
|
cfg.mcp.servers = [{ name: 'srv', command: 'x', args: [] }];
|
|
cfg.backup.enabled = true;
|
|
cfg.backup.schedule = '0 2 * * *';
|
|
|
|
const reg = new ChannelRegistry();
|
|
const services = discoverServices(cfg, reg);
|
|
|
|
expect(services.find(s => s.name === 'cron')?.status).toBe('configured');
|
|
expect(services.find(s => s.name === 'cron')?.itemCount).toBe(2);
|
|
expect(services.find(s => s.name === 'daily_briefing')?.status).toBe('configured');
|
|
expect(services.find(s => s.name === 'backup')?.status).toBe('configured');
|
|
expect(services.find(s => s.name === 'backup')?.metadata).toMatchObject({ schedule: '0 2 * * *' });
|
|
expect(services.find(s => s.name === 'mcp')?.metadata).toEqual({ serverCount: 1 });
|
|
});
|
|
|
|
it('marks audio transcription as configured and includes endpoint metadata', () => {
|
|
const cfg = makeBaseConfig();
|
|
withMutableConfig(cfg).audio = {
|
|
enabled: true,
|
|
provider: {
|
|
type: 'custom',
|
|
endpoint: 'http://localhost:18801/v1/audio/transcriptions',
|
|
model: 'whisper-1',
|
|
},
|
|
talk_mode: {
|
|
enabled: false,
|
|
wake_phrase: 'hey flynn',
|
|
timeout_ms: 120000,
|
|
allow_manual_toggle: true,
|
|
},
|
|
};
|
|
|
|
const reg = new ChannelRegistry();
|
|
const services = discoverServices(cfg, reg);
|
|
|
|
const audio = services.find(s => s.name === 'audio_transcription');
|
|
expect(audio?.status).toBe('configured');
|
|
expect(audio?.metadata).toMatchObject({
|
|
provider: 'custom',
|
|
endpoint: 'http://localhost:18801/v1/audio/transcriptions',
|
|
model: 'whisper-1',
|
|
});
|
|
});
|
|
});
|