import { describe, expect, it, vi } from 'vitest' import { decodeMuxFrame, encodeMuxFrame, encodeOpen, type MuxOpen } from 'relay-contracts' import type { AgentConfig } from '../../src/config/agentConfig.js' import { createLogger } from '../../src/log/logger.js' import { holdTunnel, dataHeader } from '../../src/transport/tunnel.js' import { createStreamRouter, identityTransform } from '../../src/transport/streamRouter.js' import { FakeWs } from '../fixtures/fakes.js' /** * T18 café-demo agent slice: pair (implied) → dial (mock) → OPEN → loopback splice against a stub * ws://127.0.0.1:3000 echo server → bytes flow BOTH ways (EXPLORE §5 demo, agent portion). */ const CFG: AgentConfig = { relayUrl: 'wss://relay/agent', enrollUrl: 'https://x/enroll', stateDir: '/tmp/x', localTargetUrl: 'ws://127.0.0.1:3000', subdomain: 'host-42', hostId: 'h-1', } const OPEN: MuxOpen = { streamId: 5, subdomain: 'host-42', requestPath: '/term?join=abc', originHeader: 'https://host-42.term.example.com', remoteAddrHash: 'x', capabilityTokenRef: 'jti', } const flush = () => new Promise((r) => setImmediate(r)) describe('café-demo agent slice (T18)', () => { it('splices bytes both ways through the loopback echo', async () => { const upstream = new FakeWs() const tunnel = holdTunnel(upstream) const loopback = new FakeWs() const dial = vi.fn(async () => loopback) const router = createStreamRouter(CFG, tunnel, dial as never, identityTransform, createLogger('error', () => {})) tunnel.dispatchTo(router, { onPing: () => {}, onPong: () => {} }) // relay → agent: OPEN + a keystroke ws_emitOpen(upstream, OPEN) await flush() expect(dial).toHaveBeenCalledWith('/term?join=abc', 'https://host-42.term.example.com') upstream.emitMessage(encodeMuxFrame(dataHeader(5, 3), new Uint8Array([104, 105, 10]))) // "hi\n" expect(loopback.sent.at(-1)).toEqual(new Uint8Array([104, 105, 10])) // agent ← base app: echo output flows back as DATA upstream loopback.emit('message', new Uint8Array([79, 75])) // "OK" const last = decodeMuxFrame(upstream.sent.at(-1)!) expect(last.header.type).toBe('data') expect([...last.payload]).toEqual([79, 75]) }) }) function ws_emitOpen(upstream: FakeWs, open: MuxOpen): void { const payload = encodeOpen(open) upstream.emitMessage( encodeMuxFrame( { version: 1, type: 'open', fin: false, rst: false, streamId: open.streamId, payloadLen: payload.length }, payload, ), ) }