This commit is contained in:
95
packages/agent/tests/connector-routing.test.ts
Normal file
95
packages/agent/tests/connector-routing.test.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { CapabilityRouter, type CapabilityRouterDeps } from '../src/capability-router.js';
|
||||
|
||||
function createDeps(overrides?: Partial<CapabilityRouterDeps>): CapabilityRouterDeps {
|
||||
return {
|
||||
toolNames: ['search_memory', 'save_memory', 'bash'],
|
||||
skills: [{ name: 'research-workflow', content: 'research and investigate topics' }],
|
||||
plugins: [],
|
||||
mcpServers: [],
|
||||
subAgentRoles: ['researcher', 'writer'],
|
||||
connectors: [
|
||||
{ id: 'github', name: 'GitHub', service: 'github.com', connected: true, actions: ['list_repos', 'create_issue', 'list_prs'] },
|
||||
{ id: 'slack', name: 'Slack', service: 'slack.com', connected: true, actions: ['send_message', 'list_channels'] },
|
||||
{ id: 'jira', name: 'Jira', service: 'atlassian.net', connected: false, actions: ['create_issue', 'list_issues'] },
|
||||
{ id: 'email', name: 'Email (SendGrid)', service: 'sendgrid.com', connected: true, actions: ['send_email'] },
|
||||
],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe('Connector routing in CapabilityRouter', () => {
|
||||
it('resolve("jira") returns connector source when jira is registered', () => {
|
||||
const router = new CapabilityRouter(createDeps());
|
||||
const routes = router.resolve('jira');
|
||||
const connectorRoute = routes.find(r => r.source === 'connector');
|
||||
|
||||
expect(connectorRoute).toBeDefined();
|
||||
expect(connectorRoute!.name).toBe('jira');
|
||||
expect(connectorRoute!.confidence).toBe(0.75);
|
||||
expect(connectorRoute!.available).toBe(false); // Jira is disconnected
|
||||
expect(connectorRoute!.suggestion).toContain('not connected');
|
||||
});
|
||||
|
||||
it('resolve("github") returns connector with available=true when connected', () => {
|
||||
const router = new CapabilityRouter(createDeps());
|
||||
const routes = router.resolve('github');
|
||||
const connectorRoute = routes.find(r => r.source === 'connector');
|
||||
|
||||
expect(connectorRoute).toBeDefined();
|
||||
expect(connectorRoute!.available).toBe(true);
|
||||
expect(connectorRoute!.suggestion).toBeUndefined();
|
||||
});
|
||||
|
||||
it('resolve("create github issue") matches via action name', () => {
|
||||
const router = new CapabilityRouter(createDeps());
|
||||
const routes = router.resolve('create issue');
|
||||
// Both GitHub and Jira have "create_issue" action
|
||||
const connectorRoutes = routes.filter(r => r.source === 'connector');
|
||||
expect(connectorRoutes.length).toBeGreaterThanOrEqual(2);
|
||||
expect(connectorRoutes.some(r => r.name === 'github')).toBe(true);
|
||||
expect(connectorRoutes.some(r => r.name === 'jira')).toBe(true);
|
||||
});
|
||||
|
||||
it('resolve("send email") returns email connector', () => {
|
||||
const router = new CapabilityRouter(createDeps());
|
||||
const routes = router.resolve('send email');
|
||||
const emailRoute = routes.find(r => r.source === 'connector' && r.name === 'email');
|
||||
expect(emailRoute).toBeDefined();
|
||||
expect(emailRoute!.available).toBe(true);
|
||||
});
|
||||
|
||||
it('connector routes rank at 0.75 between native (0.8-1.0) and skill (0.5-0.7)', () => {
|
||||
const router = new CapabilityRouter(createDeps());
|
||||
const routes = router.resolve('slack');
|
||||
const connectorRoute = routes.find(r => r.source === 'connector');
|
||||
expect(connectorRoute!.confidence).toBe(0.75);
|
||||
|
||||
// Verify ordering: native first (if any), then connector, then skill
|
||||
const nativeIdx = routes.findIndex(r => r.source === 'native');
|
||||
const connectorIdx = routes.findIndex(r => r.source === 'connector');
|
||||
if (nativeIdx >= 0) {
|
||||
expect(routes[nativeIdx].confidence).toBeGreaterThanOrEqual(0.75);
|
||||
}
|
||||
// Connector should come before skill-level entries
|
||||
const skillIdx = routes.findIndex(r => r.source === 'skill');
|
||||
if (skillIdx >= 0 && connectorIdx >= 0) {
|
||||
expect(connectorIdx).toBeLessThan(skillIdx);
|
||||
}
|
||||
});
|
||||
|
||||
it('works with no connectors (backward compatible)', () => {
|
||||
const router = new CapabilityRouter(createDeps({ connectors: undefined }));
|
||||
const routes = router.resolve('github');
|
||||
const connectorRoutes = routes.filter(r => r.source === 'connector');
|
||||
expect(connectorRoutes).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('missing route suggestion mentions connectors', () => {
|
||||
const router = new CapabilityRouter(createDeps({ connectors: [] }));
|
||||
const routes = router.resolve('xyznonexistent');
|
||||
const missing = routes.find(r => r.source === 'missing');
|
||||
expect(missing).toBeDefined();
|
||||
expect(missing!.suggestion).toContain('Connectors');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user