Skip to main content
Glama
connection.test.ts6.28 kB
import { afterEach, beforeEach, describe, expect, it } from 'bun:test'; import { TiltConnection } from '../../src/tilt/connection.ts'; import { TiltCommandTimeoutError, TiltNotInstalledError, TiltNotRunningError, } from '../../src/tilt/errors.ts'; import { createTiltCliFixture, type TiltCliFixture, } from '../fixtures/tilt-cli-fixture.ts'; describe('TiltConnection', () => { let fixture: TiltCliFixture | undefined; const buildConnection = ( overrides: Partial<ConstructorParameters<typeof TiltConnection>[0]> = {}, ) => { if (!fixture) { throw new Error('Fixture not initialized'); } return new TiltConnection({ port: fixture.port, host: fixture.host, timeout: 500, binaryPath: fixture.tiltBinary, cacheIntervalMs: 25, ...overrides, }); }; beforeEach(async () => { fixture = await createTiltCliFixture(); }); afterEach(() => { fixture?.cleanup(); fixture = undefined; }); describe('Session detection', () => { it('detects active Tilt session', async () => { const connection = buildConnection(); const result = await connection.checkSession(); expect(result).toBe(true); const events = fixture?.readEvents(); expect(events.spawns.length).toBe(1); }); it('throws TiltNotInstalledError when tilt binary is missing', async () => { const connection = new TiltConnection({ port: fixture?.port, host: fixture?.host, timeout: 100, binaryPath: '/path/that/does/not/exist/tilt', }); await expect(connection.checkSession()).rejects.toThrow( TiltNotInstalledError, ); }); it('throws TiltNotRunningError when connection is refused', async () => { fixture?.setBehavior('refused'); const connection = buildConnection(); await expect(connection.checkSession()).rejects.toThrow( TiltNotRunningError, ); const events = fixture?.readEvents(); expect(events.spawns.length).toBe(1); }); }); describe('Cache behavior', () => { it('uses cached result within interval', async () => { const connection = buildConnection({ cacheIntervalMs: 50 }); await connection.checkSession(); await connection.checkSession(); const events = fixture?.readEvents(); expect(events.spawns.length).toBe(1); }); it('refreshes after cache expiration', async () => { const connection = buildConnection({ cacheIntervalMs: 30 }); await connection.checkSession(); await new Promise((resolve) => setTimeout(resolve, 40)); await connection.checkSession(); const events = fixture?.readEvents(); expect(events.spawns.length).toBe(2); }); it('forceRefresh bypasses cache', async () => { const connection = buildConnection(); await connection.checkSession(); await connection.checkSession(true); const events = fixture?.readEvents(); expect(events.spawns.length).toBe(2); }); }); describe('Explicit cache invalidation', () => { it('invalidateCache forces next check to query', async () => { const connection = buildConnection(); await connection.checkSession(); connection.invalidateCache(); await connection.checkSession(); const events = fixture?.readEvents(); expect(events.spawns.length).toBe(2); }); it('invalidates cache on error and retries on next call', async () => { const connection = buildConnection(); await connection.checkSession(); fixture?.setBehavior('refused'); await expect(connection.checkSession(true)).rejects.toThrow( TiltNotRunningError, ); fixture?.setBehavior('healthy'); await connection.checkSession(true); const events = fixture?.readEvents(); expect(events.spawns.length).toBe(3); }); }); describe('Connection info', () => { it('returns connection configuration including binary and cache interval', () => { const connection = buildConnection({ cacheIntervalMs: 45, timeout: 500 }); const info = connection.getConnectionInfo(); expect(info).toEqual({ port: fixture?.port, host: fixture?.host, timeout: 500, binaryPath: fixture?.tiltBinary, cacheIntervalMs: 45, }); }); it('requires env configuration when host/port are not provided', () => { const savedPort = process.env.TILT_PORT; const savedHost = process.env.TILT_HOST; delete process.env.TILT_PORT; delete process.env.TILT_HOST; try { expect(() => new TiltConnection()).toThrow(/TILT_PORT is not set/); } finally { if (savedPort !== undefined) process.env.TILT_PORT = savedPort; if (savedHost !== undefined) process.env.TILT_HOST = savedHost; } }); it('uses TILT_PORT and TILT_HOST env vars when set', () => { // Save original env vars const savedPort = process.env.TILT_PORT; const savedHost = process.env.TILT_HOST; try { process.env.TILT_PORT = '17350'; process.env.TILT_HOST = '192.168.1.50'; const connection = new TiltConnection(); const info = connection.getConnectionInfo(); expect(info.port).toBe(17350); expect(info.host).toBe('192.168.1.50'); } finally { // Restore env vars if (savedPort !== undefined) { process.env.TILT_PORT = savedPort; } else { delete process.env.TILT_PORT; } if (savedHost !== undefined) { process.env.TILT_HOST = savedHost; } else { delete process.env.TILT_HOST; } } }); }); describe('Timeout handling', () => { it('kills process on timeout and throws TiltCommandTimeoutError', async () => { fixture?.setBehavior('hang', { hangMs: 20000 }); const connection = buildConnection({ timeout: 500 }); await expect(connection.checkSession()).rejects.toThrow( TiltCommandTimeoutError, ); const events = fixture?.readEvents(); expect(events.spawns.length).toBe(1); expect(events.signals.some((event) => event.signal === 'SIGTERM')).toBe( true, ); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/0xBigBoss/tilt-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server