import { describe, expect, it, vi } from 'vitest' import { buildOpaqueProfileRoutes, buildRegistryProfileRoutes, isLocalEnumerationFailure, localRouteFallbackProfiles, type ProfileRouteConfig, registryGatewayWsUrl, undialedSshRouteSeeds } from './plugin-profile-routes' function config(overrides: Partial = {}): ProfileRouteConfig { return { cloudOrg: '', mode: 'local', remoteUrl: '', sshHost: '', sshPort: null, sshRemoteHermesPath: '', sshRemoteProfile: '', sshUser: '', ...overrides } } describe('buildOpaqueProfileRoutes', () => { it('groups SSH aliases by their effective route without exposing endpoint data', async () => { const configs = new Map([ [ 'research', config({ mode: 'ssh', sshHost: 'lab-a', sshRemoteHermesPath: '~/.hermes', sshRemoteProfile: 'remote-research' }) ], [ 'writing', config({ mode: 'ssh', sshHost: 'lab-b', sshRemoteHermesPath: '~/.hermes', sshRemoteProfile: 'remote-writing' }) ] ]) const resolveSsh = vi.fn(async () => ({ hostname: 'gateway.example', port: 22, user: 'hermes' })) const routes = await buildOpaqueProfileRoutes({ getProfileConfig: profile => configs.get(profile) ?? config(), globalConfig: config(), installationId: 'install-a-secret', primaryProfile: 'default', profileNames: ['default', 'research', 'writing'], resolveSsh }) expect( routes.map(route => ({ mode: route.mode, profile: route.profile, targetProfile: route.targetProfile })) ).toEqual([ { mode: 'local', profile: 'default', targetProfile: 'default' }, { mode: 'remote', profile: 'research', targetProfile: 'remote-research' }, { mode: 'remote', profile: 'writing', targetProfile: 'remote-writing' } ]) expect(routes[1].connectionId).toBe(routes[2].connectionId) expect(routes[0].connectionId).not.toBe(routes[1].connectionId) expect(JSON.stringify(routes)).not.toContain('gateway.example') expect(JSON.stringify(routes)).not.toContain('lab-a') expect(JSON.stringify(routes)).not.toContain('.hermes') }) it('changes opaque IDs when the effective SSH destination changes', async () => { const options = { getProfileConfig: () => config({ mode: 'ssh', sshHost: 'lab', sshRemoteHermesPath: '~/.hermes' }), globalConfig: config(), installationId: 'install-a-secret', primaryProfile: 'default', profileNames: ['default', 'worker'] } const before = await buildOpaqueProfileRoutes({ ...options, resolveSsh: async () => ({ hostname: 'old.example', port: 22, user: 'hermes' }) }) const after = await buildOpaqueProfileRoutes({ ...options, resolveSsh: async () => ({ hostname: 'new.example', port: 22, user: 'hermes' }) }) expect(before[1].connectionId).not.toBe(after[1].connectionId) }) it('keys IDs to the Desktop installation', async () => { const options = { getProfileConfig: () => config({ mode: 'remote', remoteUrl: 'https://gateway.example' }), globalConfig: config(), primaryProfile: 'default', profileNames: ['default', 'worker'], resolveSsh: vi.fn() } const first = await buildOpaqueProfileRoutes({ ...options, installationId: 'install-a-secret' }) const second = await buildOpaqueProfileRoutes({ ...options, installationId: 'install-b-secret' }) expect(first[1].connectionId).not.toBe(second[1].connectionId) }) it('isolates an SSH resolution failure to its configured route', async () => { const options = { getProfileConfig: (profile: string) => profile === 'broken' ? config({ mode: 'ssh', sshHost: 'unreachable', sshPort: 2222, sshUser: 'hermes' }) : config(), globalConfig: config(), installationId: 'install-a-secret', primaryProfile: 'default', profileNames: ['default', 'broken'] } const routes = await buildOpaqueProfileRoutes({ ...options, resolveSsh: async route => { if (route.sshHost === 'unreachable') { throw new Error('ssh -G timed out') } return { hostname: route.sshHost, port: route.sshPort, user: route.sshUser } } }) expect(routes).toHaveLength(2) expect(routes.find(route => route.profile === 'broken')).toMatchObject({ mode: 'remote', profile: 'broken' }) }) it('inherits the global remote gateway and deduplicates profile names', async () => { const routes = await buildOpaqueProfileRoutes({ getProfileConfig: () => config(), globalConfig: config({ mode: 'remote', remoteUrl: 'https://gateway.example/' }), installationId: 'install-a-secret', primaryProfile: 'default', profileNames: ['default', 'alpha', 'beta', 'alpha'], resolveSsh: vi.fn() }) expect(routes.map(route => route.profile)).toEqual(['default', 'alpha', 'beta']) expect(routes.map(route => route.targetProfile)).toEqual(['default', 'alpha', 'beta']) expect(routes.every(route => route.mode === 'remote')).toBe(true) expect(new Set(routes.map(route => route.connectionId))).toHaveLength(1) }) it('reports the backend root for a per-profile URL alias', async () => { const routes = await buildOpaqueProfileRoutes({ getProfileConfig: profile => profile === 'barry' ? config({ mode: 'remote', remoteUrl: 'https://tower.example' }) : config(), globalConfig: config(), installationId: 'install-a-secret', primaryProfile: 'default', profileNames: ['default', 'barry'], resolveSsh: vi.fn() }) expect(routes[1]).toMatchObject({ profile: 'barry', targetProfile: 'default' }) }) it('reports an explicit backend profile inherited from global SSH', async () => { const routes = await buildOpaqueProfileRoutes({ getProfileConfig: () => config(), globalConfig: config({ mode: 'ssh', sshHost: 'gateway', sshRemoteProfile: 'remote-primary' }), installationId: 'install-a-secret', primaryProfile: 'default', profileNames: ['default', 'desktop-alias'], resolveSsh: vi.fn(async () => ({ hostname: 'gateway.example', port: 22, user: 'hermes' })) }) expect(routes.map(route => route.targetProfile)).toEqual(['remote-primary', 'remote-primary']) }) it('keeps cloud organizations on one service URL in distinct groups', async () => { const routes = await buildOpaqueProfileRoutes({ getProfileConfig: profile => profile === 'org-a' || profile === 'org-b' ? config({ cloudOrg: profile, mode: 'cloud', remoteUrl: 'https://cloud.example' }) : config(), globalConfig: config(), installationId: 'install-a-secret', primaryProfile: 'default', profileNames: ['default', 'org-a', 'org-b'], resolveSsh: vi.fn() }) expect(new Set(routes.map(route => route.connectionId))).toHaveLength(3) expect(JSON.stringify(routes.map(({ connectionId, mode }) => ({ connectionId, mode })))).not.toContain('org-a') }) }) describe('buildRegistryProfileRoutes', () => { it('keeps duplicate profile names distinct by registry connection without exposing source details', () => { const routes = buildRegistryProfileRoutes({ agents: [ { connectionId: 'local', profile: 'research' }, { connectionId: 'homelab', profile: 'research' } ], legacyRoutes: [{ connectionId: 'legacy-hash', mode: 'local', profile: 'research', targetProfile: 'research' }], sources: [ { id: 'local', kind: 'local', label: 'This device' }, { authMode: 'token', host: 'private.lan', id: 'homelab', kind: 'ssh', keyPath: '/secret/id_ed25519', label: 'Homelab', remoteProfile: 'remote-research', token: 'encrypted-secret' } ] }) expect(routes).toEqual([ { connectionId: 'local', mode: 'local', profile: 'research', targetProfile: 'research' }, { connectionId: 'homelab', mode: 'remote', profile: 'research', targetProfile: 'remote-research' } ]) expect(JSON.stringify(routes)).not.toContain('private.lan') expect(JSON.stringify(routes)).not.toContain('id_ed25519') expect(JSON.stringify(routes)).not.toContain('encrypted-secret') expect(new Set(routes.map(route => `${route.connectionId}/${route.profile}`))).toHaveLength(2) }) it('keeps the registry local source genuinely local when legacy v1 routing is remote', () => { const routes = buildRegistryProfileRoutes({ agents: [{ connectionId: 'local', profile: 'barry' }], legacyRoutes: [{ connectionId: 'legacy-hash', mode: 'remote', profile: 'barry', targetProfile: 'default' }], sources: [{ id: 'local', kind: 'local', label: 'This device' }] }) expect(routes).toEqual([{ connectionId: 'local', mode: 'local', profile: 'barry', targetProfile: 'barry' }]) }) it('scopes registry-shared remote websocket URLs to the requested profile', () => { expect( registryGatewayWsUrl({ profile: 'research', sharedRemote: true }, 'wss://gateway.example/api/ws?token=secret') ).toBe('wss://gateway.example/api/ws?token=secret&profile=research') expect(registryGatewayWsUrl({ profile: 'research' }, 'ws://127.0.0.1:5151/api/ws?token=local')).toBe( 'ws://127.0.0.1:5151/api/ws?token=local' ) }) }) describe('isLocalEnumerationFailure', () => { it('does not treat an intentionally deferred local enumeration as a failure', () => { expect(isLocalEnumerationFailure('connect-on-demand')).toBe(false) }) it('treats any other enumeration error as a failure', () => { expect(isLocalEnumerationFailure('ECONNREFUSED')).toBe(true) }) it('treats a missing error as no failure', () => { expect(isLocalEnumerationFailure(undefined)).toBe(false) }) }) describe('localRouteFallbackProfiles', () => { it('restores failed local profiles when another source returned agents', () => { const agents = [{ connectionId: 'cloud-prod', profile: 'default' }] expect(localRouteFallbackProfiles(agents, 'local', ['default', 'venture'], true)).toEqual(['default', 'venture']) }) it('does not synthesize local routes after a successful local enumeration', () => { expect(localRouteFallbackProfiles([], 'local', ['default'], false)).toEqual([]) }) it('does not synthesize local routes for a deferred connect-on-demand enumeration', () => { expect( localRouteFallbackProfiles([], 'local', ['default'], isLocalEnumerationFailure('connect-on-demand')) ).toEqual([]) }) it('synthesizes local routes for a genuine local enumeration error', () => { expect(localRouteFallbackProfiles([], 'local', ['default'], isLocalEnumerationFailure('ECONNREFUSED'))).toEqual([ 'default' ]) }) }) describe('undialedSshRouteSeeds', () => { it('keeps a stable default route while retaining the configured backend target', () => { expect( undialedSshRouteSeeds( [], [ { id: 'homelab', kind: 'ssh', remoteProfile: 'venture' }, { id: 'cloud-prod', kind: 'cloud' } ] ) ).toEqual([{ connectionId: 'homelab', profile: 'default' }]) }) it('does not add a speculative seed after the source has roster agents', () => { expect( undialedSshRouteSeeds( [{ connectionId: 'homelab', profile: 'research' }], [{ id: 'homelab', kind: 'ssh', remoteProfile: 'venture' }] ) ).toEqual([]) }) })