/** * FlowPad Cross-Realm Integration Test * * This test verifies the EXACT pattern FlowPad uses for cross-realm communication: * 1. FlowPad (browser) creates CrossRealmRouter with tab realm * 2. FlowPad calls viaRemote("daemon@matrix-3/daemon.matrix-3", "introspect") * 3. CrossRealmRouter routes to CrossRealmClient (cross-realm) * 4. CrossRealmClient publishes to daemon's mailbox topic * 5. Daemon's EdgeGateway receives, routes to internal handler * 6. Handler responds, EdgeGateway sends reply * 7. FlowPad's CrossRealmClient receives reply * * This is a LOOPBACK test - both FlowPad and daemon are simulated in-process * connected via a shared InMemory backbone. */ import { test } from 'node:test'; import assert from 'node:assert/strict'; import { FederationPeer, TabInstanceResolver } from '@open-matrix/federation/federation_peer'; import { InMemoryFilterBroker } from '@open-matrix/federation/transport/inmem'; import { WIRE_PROFILES } from '@open-matrix/federation/wire_profile'; import { getInternalTopic } from '@open-matrix/federation/topics'; /** * Test the exact FlowPad pattern: viaRemote to daemon@matrix-3 */ test('FlowPad cross-realm to daemon@matrix-3 (full loopback)', async () => { // ═══════════════════════════════════════════════════════════════════════════ // BACKBONE: Shared InMemory broker simulating HiveMQ // ═══════════════════════════════════════════════════════════════════════════ const backbone = new InMemoryFilterBroker(); // ═══════════════════════════════════════════════════════════════════════════ // FLOWPAD SIDE: CrossRealmRouter with tab realm // ═══════════════════════════════════════════════════════════════════════════ const flowpadLocalBus = new InMemoryFilterBroker(); // FlowPad's tab-isolated realm const flowpadTabRealm = 'flowpad.tab-abc123'; const flowpadRouter = new FederationPeer({ localRealm: flowpadTabRealm, localBus: flowpadLocalBus, instanceResolver: new TabInstanceResolver(flowpadTabRealm), backbone: backbone, // Connect to shared backbone }); // ═══════════════════════════════════════════════════════════════════════════ // DAEMON SIDE: EdgeGateway processing incoming requests // ═══════════════════════════════════════════════════════════════════════════ const daemonLocalBus = new InMemoryFilterBroker(); const daemonRealm = 'daemon@matrix-3'; // Create EdgeGateway for daemon realm const daemonEdge = new FederationPeer({ localRealm: daemonRealm, backbone, localBus: daemonLocalBus, profile: WIRE_PROFILES.mxenv1, internalTimeoutMs: 3000, }); // Start daemon EdgeGateway (subscribes to mailbox topics) daemonEdge.start(); // Set up daemon's internal handler for introspect const daemonInternalTopic = getInternalTopic(daemonRealm, 'daemon.matrix-3.accepts.introspect'); console.log(`[Test] Daemon subscribing to: ${daemonInternalTopic}`); daemonLocalBus.subscribe(daemonInternalTopic, (topic, payload) => { console.log(`[Test] Daemon received on ${topic}:`, payload); const request = JSON.parse(payload); // Send reply to the replyTo topic const reply = { correlationId: request.correlationId, ok: true, result: { type: 'DaemonProject', name: 'matrix-3', children: ['file1.ts', 'file2.ts'], }, }; console.log(`[Test] Daemon replying to: ${request.replyTo}`); daemonLocalBus.publish(request.replyTo, JSON.stringify(reply)); }); // ═══════════════════════════════════════════════════════════════════════════ // TEST: FlowPad calls viaRemote to daemon // ═══════════════════════════════════════════════════════════════════════════ console.log(`[Test] FlowPad invoking: daemon@matrix-3/daemon.matrix-3.accepts.introspect`); const result = await flowpadRouter.invoke<{ type: string; name: string; children: string[] }>( 'daemon@matrix-3/daemon.matrix-3.accepts.introspect', {}, 5000 ); console.log(`[Test] Result:`, result); // Verify result assert.equal(result.type, 'DaemonProject'); assert.equal(result.name, 'matrix-3'); assert.deepEqual(result.children, ['file1.ts', 'file2.ts']); // Cleanup daemonEdge.stop(); flowpadRouter.stop(); }); /** * Test that base realm matching works: flowpad → flowpad.tab-xxx is LOCAL */ test('FlowPad local routing: flowpad → flowpad.tab-xxx is LOCAL', async () => { const localBus = new InMemoryFilterBroker(); const tabRealm = 'flowpad.tab-abc123'; const router = new FederationPeer({ localRealm: tabRealm, localBus: localBus, instanceResolver: new TabInstanceResolver(tabRealm), }); // Set up local handler on the tab realm topic using browser local bus format. // TabInstanceResolver returns semantic path for browser local bus. const localTopic = `${tabRealm}.tree.accepts.introspect`; console.log(`[Test] Local handler subscribing to: ${localTopic}`); localBus.subscribe(localTopic, (topic, payload) => { console.log(`[Test] Local handler received on ${topic}`); const request = JSON.parse(payload); const reply = { ok: true, result: { type: 'FractalTree', local: true }, }; localBus.publish(request.replyTo, JSON.stringify(reply)); }); // FlowPad uses base realm "flowpad" in the URI (not the full tab realm) // The router should still route locally because "flowpad" is the base of "flowpad.tab-abc123" const result = await router.invoke<{ type: string; local: boolean }>( 'flowpad/tree.accepts.introspect', {}, 1000 ); console.log(`[Test] Result:`, result); assert.equal(result.type, 'FractalTree'); assert.equal(result.local, true); router.stop(); }); /** * Test different realm = CROSS-REALM (not local) */ test('FlowPad to daemon is CROSS-REALM (not local)', async () => { const localBus = new InMemoryFilterBroker(); const tabRealm = 'flowpad.tab-abc123'; const router = new FederationPeer({ localRealm: tabRealm, localBus: localBus, instanceResolver: new TabInstanceResolver(tabRealm), // No backbone transport - should throw on cross-realm }); // daemon@matrix-3 is a DIFFERENT realm - should require backbone await assert.rejects( () => router.invoke('daemon@matrix-3/daemon.matrix-3.accepts.introspect', {}, 1000), /No backbone transport/ ); router.stop(); });