Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { BackendDialClaims } from './backend-dial-claim'
|
||||
import { parseBackendScopeKey } from './connection-registry'
|
||||
|
||||
const here = path.dirname(fileURLToPath(import.meta.url))
|
||||
const mainSource = fs.readFileSync(path.join(here, 'main.ts'), 'utf8').replace(/\r\n/g, '\n')
|
||||
|
||||
describe('BackendDialClaims (#90812)', () => {
|
||||
it('coalesces two concurrent dials for the same (connectionId, profile) onto ONE backend spawn', async () => {
|
||||
const claims = new BackendDialClaims()
|
||||
let spawns = 0
|
||||
let resolveSpawn: ((value: { baseUrl: string }) => void) | undefined
|
||||
|
||||
const dial = vi.fn(() => {
|
||||
spawns += 1
|
||||
|
||||
return new Promise<{ baseUrl: string }>(resolve => {
|
||||
resolveSpawn = resolve
|
||||
})
|
||||
})
|
||||
|
||||
// Two renderer windows race the same reconnect: reconnectGateway()'s
|
||||
// in-flight lock is per-renderer, so BOTH invoke the main-process dial.
|
||||
const first = claims.run('conn:office-ssh::default', dial)
|
||||
const second = claims.run('conn:office-ssh::default', dial)
|
||||
|
||||
expect(spawns).toBe(1)
|
||||
|
||||
resolveSpawn?.({ baseUrl: 'http://127.0.0.1:53150' })
|
||||
|
||||
const [firstResult, secondResult] = await Promise.all([first, second])
|
||||
|
||||
// The second caller receives the FIRST dial's result, not its own spawn.
|
||||
expect(firstResult).toBe(secondResult)
|
||||
expect(firstResult).toEqual({ baseUrl: 'http://127.0.0.1:53150' })
|
||||
expect(dial).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('scopes claims by key: different (connectionId, profile) pairs dial independently', async () => {
|
||||
const claims = new BackendDialClaims()
|
||||
const dialA = vi.fn(async () => 'a')
|
||||
const dialB = vi.fn(async () => 'b')
|
||||
|
||||
const [a, b] = await Promise.all([
|
||||
claims.run('conn:office-ssh::default', dialA),
|
||||
claims.run('conn:office-ssh::work', dialB)
|
||||
])
|
||||
|
||||
expect(a).toBe('a')
|
||||
expect(b).toBe('b')
|
||||
expect(dialA).toHaveBeenCalledTimes(1)
|
||||
expect(dialB).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('releases the claim once the dial settles so a later reconnect can dial again (bounded, not latched)', async () => {
|
||||
const claims = new BackendDialClaims()
|
||||
const dial = vi.fn(async () => 'fresh')
|
||||
|
||||
await claims.run('default', dial)
|
||||
expect(claims.inFlight('default')).toBe(false)
|
||||
|
||||
await claims.run('default', dial)
|
||||
expect(dial).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('propagates a failed dial to every coalesced waiter and never caches the rejection', async () => {
|
||||
const claims = new BackendDialClaims()
|
||||
let rejectSpawn: ((error: Error) => void) | undefined
|
||||
|
||||
const failingDial = vi.fn(
|
||||
() =>
|
||||
new Promise<never>((_resolve, reject) => {
|
||||
rejectSpawn = reject
|
||||
})
|
||||
)
|
||||
|
||||
const first = claims.run('conn:office-ssh::default', failingDial)
|
||||
const second = claims.run('conn:office-ssh::default', failingDial)
|
||||
expect(failingDial).toHaveBeenCalledTimes(1)
|
||||
|
||||
rejectSpawn?.(new Error('ssh dial failed'))
|
||||
|
||||
await expect(first).rejects.toThrow('ssh dial failed')
|
||||
await expect(second).rejects.toThrow('ssh dial failed')
|
||||
|
||||
// Fail closed but not latched: the NEXT dial attempt runs fresh.
|
||||
const recovered = vi.fn(async () => 'recovered')
|
||||
await expect(claims.run('conn:office-ssh::default', recovered)).resolves.toBe('recovered')
|
||||
expect(recovered).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('a synchronously-throwing dial rejects the claim instead of escaping the coalescing seam', async () => {
|
||||
const claims = new BackendDialClaims()
|
||||
|
||||
await expect(
|
||||
claims.run('default', () => {
|
||||
throw new Error('spawn refused')
|
||||
})
|
||||
).rejects.toThrow('spawn refused')
|
||||
|
||||
expect(claims.inFlight('default')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('parseBackendScopeKey (#90812/#93910)', () => {
|
||||
it('round-trips the composite pool key back to (connectionId, profile)', () => {
|
||||
expect(parseBackendScopeKey('conn:office-ssh::default')).toEqual({
|
||||
connectionId: 'office-ssh',
|
||||
profile: 'default'
|
||||
})
|
||||
expect(parseBackendScopeKey('conn:office-ssh::work')).toEqual({ connectionId: 'office-ssh', profile: 'work' })
|
||||
})
|
||||
|
||||
it('treats a bare profile key as the local/primary scope', () => {
|
||||
expect(parseBackendScopeKey('default')).toEqual({ connectionId: null, profile: 'default' })
|
||||
expect(parseBackendScopeKey('work')).toEqual({ connectionId: null, profile: 'work' })
|
||||
})
|
||||
})
|
||||
|
||||
describe('main.ts wiring for #90812', () => {
|
||||
it('routes the profile-scoped dial IPC through the single-owner claim', () => {
|
||||
const handlerStart = mainSource.indexOf("ipcMain.handle('hermes:connection', ")
|
||||
expect(handlerStart).toBeGreaterThan(-1)
|
||||
const body = mainSource.slice(handlerStart, handlerStart + 900)
|
||||
|
||||
expect(body).toContain('backendDialClaims.run(')
|
||||
expect(body).toContain('ensureBackend(profile)')
|
||||
})
|
||||
|
||||
it('routes the registry-scoped dial IPC through the claim keyed by backendScopeKey(connectionId, profile)', () => {
|
||||
const handlerStart = mainSource.indexOf("ipcMain.handle('hermes:connection:for', ")
|
||||
expect(handlerStart).toBeGreaterThan(-1)
|
||||
const body = mainSource.slice(handlerStart, handlerStart + 1_200)
|
||||
|
||||
expect(body).toContain('backendDialClaims.run(backendScopeKey(id, profile)')
|
||||
expect(body).toContain('ensureRegistryBackend(id, profile)')
|
||||
})
|
||||
|
||||
// The four IPC/probe surfaces below call ensureRegistryBackend()/ensureBackend()
|
||||
// directly, bypassing backendDialClaims entirely — so a renderer's guarded
|
||||
// reconnect dial and one of these can independently race the SAME
|
||||
// ensureRegistryBackend() await-before-pool-check window (main.ts) and each
|
||||
// bootstrap its own SSH tunnel / remote dashboard for the same
|
||||
// (connectionId, profile) scope.
|
||||
|
||||
it('routes a media-stream connection resolve through the single-owner claim', () => {
|
||||
const handlerStart = mainSource.indexOf('resolveRemoteConnection: ({ connectionId, profile }) =>')
|
||||
expect(handlerStart).toBeGreaterThan(-1)
|
||||
const body = mainSource.slice(handlerStart, handlerStart + 300)
|
||||
|
||||
expect(body).toContain('backendDialClaims.run(backendScopeKey(connectionId, profile)')
|
||||
expect(body).toContain('ensureRegistryBackend(connectionId, profile)')
|
||||
expect(body).toContain('ensureBackend(profile)')
|
||||
})
|
||||
|
||||
it('routes a terminal-pane backend resolve through the single-owner claim on both the registry and local branches', () => {
|
||||
const handlerStart = mainSource.indexOf('async function ensureTerminalBackend(webContentsId: number) {')
|
||||
expect(handlerStart).toBeGreaterThan(-1)
|
||||
const body = mainSource.slice(handlerStart, handlerStart + 900)
|
||||
|
||||
expect(body).toContain('backendDialClaims.run(backendScopeKey(windowRoute.connectionId, windowRoute.profile)')
|
||||
expect(body).toContain('ensureRegistryBackend(windowRoute.connectionId, windowRoute.profile)')
|
||||
expect(body).toContain('backendDialClaims.run(backendScopeKey(null, profile)')
|
||||
expect(body).toContain('ensureBackend(profile)')
|
||||
})
|
||||
|
||||
it('routes the roster-enumeration probe through the single-owner claim', () => {
|
||||
const handlerStart = mainSource.indexOf('async function enumerateRegistryAgentSources')
|
||||
expect(handlerStart).toBeGreaterThan(-1)
|
||||
const body = mainSource.slice(handlerStart, handlerStart + 3_700)
|
||||
|
||||
expect(body).toContain('backendDialClaims.run(backendScopeKey(connection.id, null)')
|
||||
expect(body).toContain('ensureRegistryBackend(connection.id, null)')
|
||||
expect(body).toContain("getJsonForBackend(descriptor, '/api/profiles'")
|
||||
})
|
||||
|
||||
it('routes the connections update-all dispatch through the single-owner claim', () => {
|
||||
const handlerStart = mainSource.indexOf("ipcMain.handle('hermes:connections:update-all',")
|
||||
expect(handlerStart).toBeGreaterThan(-1)
|
||||
// The handler grew on main (renderer-side exclusions + the managed-SSH
|
||||
// dispatch branch) — keep the scan window comfortably past the dial.
|
||||
const body = mainSource.slice(handlerStart, handlerStart + 3_000)
|
||||
|
||||
expect(body).toContain('backendDialClaims.run(backendScopeKey(connection.id, null)')
|
||||
expect(body).toContain('ensureRegistryBackend(connection.id, null)')
|
||||
expect(body).toContain("postJsonForBackend(descriptor, '/api/hermes/update'")
|
||||
})
|
||||
|
||||
it('routes every registry-scoped REST dispatch (hermes:api) through the single-owner claim', () => {
|
||||
const handlerStart = mainSource.indexOf('async function dispatchRegistryApiRequest(')
|
||||
expect(handlerStart).toBeGreaterThan(-1)
|
||||
const body = mainSource.slice(handlerStart, handlerStart + 900)
|
||||
|
||||
expect(body).toContain('backendDialClaims.run(backendScopeKey(registryConnectionId, routeProfile)')
|
||||
expect(body).toContain('ensureRegistryBackend(registryConnectionId, routeProfile)')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user