hivecast-sdk/packages/federation/tests/integration/component-topology-scenarios.spec.ts

623 lines
20 KiB
TypeScript
Raw Normal View History

/**
* Matrix Component Topology Scenarios
*
* Tests ALL possible locations where Matrix components can be relative to each other:
*
* TOPOLOGY 1: Same Realm, Same Process
* - Component A and B in same daemon
* - Communication via local message bus
*
* TOPOLOGY 2: Same Realm, Different Process (Browser Daemon)
* - Browser component talks to daemon component
* - Both in same realm, communication via local broker
*
* TOPOLOGY 3: Cross-Realm (Daemon A Daemon B)
* - Daemon in realm-alpha talks to daemon in realm-beta
* - Communication via backbone (NATS leaf nodes)
*
* TOPOLOGY 4: Cross-Realm Browser to Remote Daemon
* - Browser in realm-alpha talks to daemon in realm-beta
* - Browser local daemon (gateway) backbone remote daemon
*
* TOPOLOGY 5: Supervision Across Realms
* - Parent in realm-alpha supervises child in realm-beta
* - $join/$join_ack flow across federation
*/
import { describe, it } from 'node:test';
import { strict as assert } from 'node:assert';
import {
FederationPeer, ExactInstanceResolver,
WIRE_PROFILES,
InMemoryFilterBroker,
getLegacyIngressTopic,
createLegacyRequest,
serializeLegacyEnvelope,
} from '@open-matrix/federation';
describe('Matrix Component Topology Scenarios', () => {
// ===========================================================================
// TOPOLOGY 1: Same Realm, Same Process
// ===========================================================================
describe('Topology 1: Same Realm, Same Process', () => {
it('component A calls component B within same daemon (local bus only)', async () => {
const REALM = 'local-realm';
// Simulates the local bus inside a single daemon
const localBus = new InMemoryFilterBroker();
// Component B: Calculator service
localBus.subscribe(`calculator.accepts.multiply`, (topic, payload) => {
const req = JSON.parse(payload) as {
replyTo: string;
correlationId: string;
params: { a: number; b: number };
};
const result = req.params.a * req.params.b;
localBus.publish(req.replyTo, JSON.stringify({
ok: true,
correlationId: req.correlationId,
result: { product: result },
}));
});
// Component A: Calls calculator
let reply: unknown = null;
const correlationId = 'local-call-123';
const replyTopic = `_client.emits.reply-${correlationId}`;
localBus.subscribe(replyTopic, (topic, payload) => {
reply = JSON.parse(payload);
});
await new Promise(r => setTimeout(r, 50));
// Component A sends request to Component B
localBus.publish(`calculator.accepts.multiply`, JSON.stringify({
correlationId,
replyTo: replyTopic,
params: { a: 7, b: 6 },
}));
await new Promise(r => setTimeout(r, 100));
assert.ok(reply, 'Component A should receive reply from Component B');
assert.deepStrictEqual(
(reply as { result: { product: number } }).result,
{ product: 42 }
);
console.log('✓ Topology 1: Same realm/process - 7 × 6 = 42');
});
});
// ===========================================================================
// TOPOLOGY 2: Same Realm, Different Process (Browser ↔ Daemon)
// ===========================================================================
describe('Topology 2: Same Realm, Browser ↔ Daemon', () => {
it('browser component calls daemon component via local broker', async () => {
const REALM = 'my-app-realm';
// Local broker shared between browser and daemon
const localBroker = new InMemoryFilterBroker();
// Daemon component: FileService
localBroker.subscribe(`daemon.file.accepts.list`, (topic, payload) => {
const req = JSON.parse(payload) as {
replyTo: string;
correlationId: string;
params: { path: string };
};
// Simulated file listing
localBroker.publish(req.replyTo, JSON.stringify({
ok: true,
correlationId: req.correlationId,
result: {
files: ['README.md', 'package.json', 'src/'],
path: req.params.path,
},
}));
});
// Browser component: File Explorer
let browserReceivedReply: unknown = null;
const correlationId = 'browser-call-456';
const replyTopic = `_browser.explorer.emits.reply-${correlationId}`;
localBroker.subscribe(replyTopic, (topic, payload) => {
browserReceivedReply = JSON.parse(payload);
});
await new Promise(r => setTimeout(r, 50));
// Browser sends request to daemon
localBroker.publish(`daemon.file.accepts.list`, JSON.stringify({
correlationId,
replyTo: replyTopic,
params: { path: '/' },
}));
await new Promise(r => setTimeout(r, 100));
assert.ok(browserReceivedReply, 'Browser should receive reply from daemon');
assert.deepStrictEqual(
(browserReceivedReply as { result: { files: string[] } }).result.files,
['README.md', 'package.json', 'src/']
);
console.log('✓ Topology 2: Browser ↔ Daemon (same realm) - file listing');
});
});
// ===========================================================================
// TOPOLOGY 3: Cross-Realm Daemon to Daemon
// ===========================================================================
describe('Topology 3: Cross-Realm Daemon ↔ Daemon', () => {
it('daemon in realm-alpha queries daemon in realm-beta', async () => {
const REALM_ALPHA = 'company-a.matrix';
const REALM_BETA = 'company-b.matrix';
// Backbone (simulates HiveMQ)
const backbone = new InMemoryFilterBroker();
// Local buses for each daemon
const localBusAlpha = new InMemoryFilterBroker();
const localBusBeta = new InMemoryFilterBroker();
// EdgeGateway for each realm
const gatewayAlpha = new FederationPeer({
localRealm: REALM_ALPHA,
profile: WIRE_PROFILES.matrix3,
backbone,
localBus: localBusAlpha,
internalTimeoutMs: 5000,
});
const gatewayBeta = new FederationPeer({
localRealm: REALM_BETA,
profile: WIRE_PROFILES.matrix3,
backbone,
localBus: localBusBeta,
internalTimeoutMs: 5000,
});
gatewayAlpha.start();
gatewayBeta.start();
// Daemon Beta: User Directory Service
localBusBeta.subscribe(`directory.user.accepts.lookup`, (topic, payload) => {
const req = JSON.parse(payload) as {
replyTo: string;
correlationId: string;
params: { userId: string };
};
// Simulated user lookup
localBusBeta.publish(req.replyTo, JSON.stringify({
ok: true,
correlationId: req.correlationId,
result: {
userId: req.params.userId,
name: 'Alice Smith',
email: 'alice@company-b.com',
realm: REALM_BETA,
},
}));
});
// Daemon Alpha: Receives reply
let crossRealmReply: unknown = null;
backbone.subscribe(getLegacyIngressTopic(REALM_ALPHA), (topic, payload) => {
const parsed = JSON.parse(payload);
if (parsed.ok !== undefined) {
crossRealmReply = parsed;
}
});
await new Promise(r => setTimeout(r, 100));
// Daemon Alpha requests user from Daemon Beta
const request = createLegacyRequest({
toRealm: REALM_BETA,
toPath: 'directory.user.accepts.lookup',
fromRealm: REALM_ALPHA,
replyTo: '_daemon.client',
correlationId: 'cross-daemon-789',
body: { userId: 'user-123' },
});
backbone.publish(getLegacyIngressTopic(REALM_BETA), serializeLegacyEnvelope(request));
await new Promise(r => setTimeout(r, 500));
assert.ok(crossRealmReply, 'Alpha should receive reply from Beta');
assert.strictEqual(
(crossRealmReply as { body: { name: string } }).body.name,
'Alice Smith'
);
console.log('✓ Topology 3: Daemon(Alpha) → Daemon(Beta) - user lookup');
gatewayAlpha.stop();
gatewayBeta.stop();
});
});
// ===========================================================================
// TOPOLOGY 4: Cross-Realm Browser to Remote Daemon
// ===========================================================================
describe('Topology 4: Cross-Realm Browser → Remote Daemon', () => {
it('browser in realm-alpha calls service in realm-beta via federation', async () => {
const REALM_ALPHA = 'browser-user-realm';
const REALM_BETA = 'backend-service-realm';
// Infrastructure
const backbone = new InMemoryFilterBroker();
const localBusAlpha = new InMemoryFilterBroker();
const localBusBeta = new InMemoryFilterBroker();
// Both realms have EdgeGateways
const gatewayAlpha = new FederationPeer({
localRealm: REALM_ALPHA,
profile: WIRE_PROFILES.matrix3,
backbone,
localBus: localBusAlpha,
internalTimeoutMs: 5000,
});
const gatewayBeta = new FederationPeer({
localRealm: REALM_BETA,
profile: WIRE_PROFILES.matrix3,
backbone,
localBus: localBusBeta,
internalTimeoutMs: 5000,
});
gatewayAlpha.start();
gatewayBeta.start();
// Remote daemon (realm-beta): Weather service
localBusBeta.subscribe(`weather.service.accepts.forecast`, (topic, payload) => {
const req = JSON.parse(payload) as {
replyTo: string;
correlationId: string;
params: { city: string };
};
localBusBeta.publish(req.replyTo, JSON.stringify({
ok: true,
correlationId: req.correlationId,
result: {
city: req.params.city,
forecast: 'Sunny',
temperature: 72,
unit: 'F',
},
}));
});
// Browser (realm-alpha): Weather widget
// In real scenario, browser talks to local daemon which forwards via EdgeGateway
// Here we simulate the full flow
let browserReply: unknown = null;
backbone.subscribe(getLegacyIngressTopic(REALM_ALPHA), (topic, payload) => {
const parsed = JSON.parse(payload);
if (parsed.ok !== undefined) {
browserReply = parsed;
}
});
await new Promise(r => setTimeout(r, 100));
// Browser's request routed through local EdgeGateway to remote realm
const request = createLegacyRequest({
toRealm: REALM_BETA,
toPath: 'weather.service.accepts.forecast',
fromRealm: REALM_ALPHA,
replyTo: '_browser.widget.reply',
correlationId: 'browser-to-remote-001',
body: { city: 'Seattle' },
});
backbone.publish(getLegacyIngressTopic(REALM_BETA), serializeLegacyEnvelope(request));
await new Promise(r => setTimeout(r, 500));
assert.ok(browserReply, 'Browser should receive cross-realm reply');
assert.strictEqual(
(browserReply as { body: { forecast: string } }).body.forecast,
'Sunny'
);
console.log('✓ Topology 4: Browser(Alpha) → Remote Daemon(Beta) - weather forecast');
gatewayAlpha.stop();
gatewayBeta.stop();
});
});
// ===========================================================================
// TOPOLOGY 5: Supervision Across Realms
// ===========================================================================
describe('Topology 5: Cross-Realm Supervision', () => {
it('parent in realm-alpha supervises child in realm-beta ($join flow)', async () => {
const REALM_ALPHA = 'supervisor-realm';
const REALM_BETA = 'worker-realm';
const backbone = new InMemoryFilterBroker();
const localBusAlpha = new InMemoryFilterBroker();
const localBusBeta = new InMemoryFilterBroker();
const gatewayAlpha = new FederationPeer({
localRealm: REALM_ALPHA,
profile: WIRE_PROFILES.matrix3,
backbone,
localBus: localBusAlpha,
internalTimeoutMs: 5000,
});
const gatewayBeta = new FederationPeer({
localRealm: REALM_BETA,
profile: WIRE_PROFILES.matrix3,
backbone,
localBus: localBusBeta,
internalTimeoutMs: 5000,
});
gatewayAlpha.start();
gatewayBeta.start();
// Parent component in Alpha: handles $join requests
let joinRequestReceived = false;
localBusAlpha.subscribe(`parent.supervisor.accepts.$join`, (topic, payload) => {
const req = JSON.parse(payload) as {
replyTo: string;
correlationId: string;
params: { childId: string; childRealm: string };
};
joinRequestReceived = true;
// Acknowledge the join
localBusAlpha.publish(req.replyTo, JSON.stringify({
ok: true,
correlationId: req.correlationId,
result: {
status: 'joined',
supervisorId: 'parent.supervisor',
childId: req.params.childId,
},
}));
});
// Child sends $join from realm-beta to parent in realm-alpha
let joinAckReceived: unknown = null;
backbone.subscribe(getLegacyIngressTopic(REALM_BETA), (topic, payload) => {
const parsed = JSON.parse(payload);
if (parsed.ok !== undefined) {
joinAckReceived = parsed;
}
});
await new Promise(r => setTimeout(r, 100));
// Child component in Beta sends $join to parent in Alpha
const joinRequest = createLegacyRequest({
toRealm: REALM_ALPHA,
toPath: 'parent.supervisor.accepts.$join',
fromRealm: REALM_BETA,
replyTo: '_child.worker.reply',
correlationId: 'join-request-001',
body: {
childId: 'child.worker',
childRealm: REALM_BETA,
componentClass: 'WorkerComponent',
},
});
backbone.publish(getLegacyIngressTopic(REALM_ALPHA), serializeLegacyEnvelope(joinRequest));
await new Promise(r => setTimeout(r, 500));
assert.ok(joinRequestReceived, 'Parent should receive $join request');
assert.ok(joinAckReceived, 'Child should receive $join_ack');
assert.strictEqual(
(joinAckReceived as { body: { status: string } }).body.status,
'joined'
);
console.log('✓ Topology 5: Cross-realm supervision - $join flow');
gatewayAlpha.stop();
gatewayBeta.stop();
});
it('parent receives $exit notification from remote child', async () => {
const REALM_ALPHA = 'supervisor-realm-2';
const REALM_BETA = 'worker-realm-2';
const backbone = new InMemoryFilterBroker();
const localBusAlpha = new InMemoryFilterBroker();
const localBusBeta = new InMemoryFilterBroker();
const gatewayAlpha = new FederationPeer({
localRealm: REALM_ALPHA,
profile: WIRE_PROFILES.matrix3,
backbone,
localBus: localBusAlpha,
internalTimeoutMs: 5000,
});
const gatewayBeta = new FederationPeer({
localRealm: REALM_BETA,
profile: WIRE_PROFILES.matrix3,
backbone,
localBus: localBusBeta,
internalTimeoutMs: 5000,
});
gatewayAlpha.start();
gatewayBeta.start();
// Parent subscribes to $exit events from child
let exitEventReceived = false;
let exitReason: string | undefined;
localBusAlpha.subscribe(`parent.supervisor.accepts.$exit`, (topic, payload) => {
const req = JSON.parse(payload) as {
replyTo: string;
correlationId: string;
params: { childId: string; reason: string };
};
exitEventReceived = true;
exitReason = req.params.reason;
// Acknowledge receipt
localBusAlpha.publish(req.replyTo, JSON.stringify({
ok: true,
correlationId: req.correlationId,
}));
});
await new Promise(r => setTimeout(r, 100));
// Child in Beta sends $exit to parent in Alpha
const exitNotification = createLegacyRequest({
toRealm: REALM_ALPHA,
toPath: 'parent.supervisor.accepts.$exit',
fromRealm: REALM_BETA,
replyTo: '_child.worker.reply',
correlationId: 'exit-notify-001',
body: {
childId: 'child.worker',
reason: 'completed',
result: { itemsProcessed: 100 },
},
});
backbone.publish(getLegacyIngressTopic(REALM_ALPHA), serializeLegacyEnvelope(exitNotification));
await new Promise(r => setTimeout(r, 500));
assert.ok(exitEventReceived, 'Parent should receive $exit notification');
assert.strictEqual(exitReason, 'completed');
console.log('✓ Topology 5: Cross-realm supervision - $exit notification');
gatewayAlpha.stop();
gatewayBeta.stop();
});
});
// ===========================================================================
// BONUS: Complete Flow - Browser → Local Daemon → Remote Daemon → Response
// ===========================================================================
describe('Complete Flow: Full Stack Federation', () => {
it('browser request travels through full federation stack', async () => {
const BROWSER_REALM = 'end-user-app';
const LOCAL_DAEMON_REALM = 'local-proxy';
const REMOTE_SERVICE_REALM = 'cloud-api';
const backbone = new InMemoryFilterBroker();
const localBusBrowser = new InMemoryFilterBroker(); // Browser's local connection
const localBusLocalDaemon = new InMemoryFilterBroker();
const localBusRemote = new InMemoryFilterBroker();
// Gateway for each realm
const gatewayBrowser = new FederationPeer({
localRealm: BROWSER_REALM,
profile: WIRE_PROFILES.matrix3,
backbone,
localBus: localBusBrowser,
internalTimeoutMs: 5000,
});
const gatewayLocalDaemon = new FederationPeer({
localRealm: LOCAL_DAEMON_REALM,
profile: WIRE_PROFILES.matrix3,
backbone,
localBus: localBusLocalDaemon,
internalTimeoutMs: 5000,
});
const gatewayRemote = new FederationPeer({
localRealm: REMOTE_SERVICE_REALM,
profile: WIRE_PROFILES.matrix3,
backbone,
localBus: localBusRemote,
internalTimeoutMs: 5000,
});
gatewayBrowser.start();
gatewayLocalDaemon.start();
gatewayRemote.start();
// Remote cloud service: AI processing
localBusRemote.subscribe(`ai.service.accepts.analyze`, (topic, payload) => {
const req = JSON.parse(payload) as {
replyTo: string;
correlationId: string;
params: { text: string };
};
localBusRemote.publish(req.replyTo, JSON.stringify({
ok: true,
correlationId: req.correlationId,
result: {
sentiment: 'positive',
confidence: 0.92,
processedBy: REMOTE_SERVICE_REALM,
},
}));
});
// Track the response
let finalResponse: unknown = null;
backbone.subscribe(getLegacyIngressTopic(BROWSER_REALM), (topic, payload) => {
const parsed = JSON.parse(payload);
if (parsed.ok !== undefined) {
finalResponse = parsed;
}
});
await new Promise(r => setTimeout(r, 100));
// Browser initiates request to remote AI service
const request = createLegacyRequest({
toRealm: REMOTE_SERVICE_REALM,
toPath: 'ai.service.accepts.analyze',
fromRealm: BROWSER_REALM,
replyTo: '_browser.app.reply',
correlationId: 'full-stack-001',
body: { text: 'Matrix federation is working great!' },
});
backbone.publish(getLegacyIngressTopic(REMOTE_SERVICE_REALM), serializeLegacyEnvelope(request));
await new Promise(r => setTimeout(r, 500));
assert.ok(finalResponse, 'Browser should receive response from remote cloud service');
assert.strictEqual(
(finalResponse as { body: { sentiment: string } }).body.sentiment,
'positive'
);
console.log('✓ Complete Flow: Browser → Local → Remote → Response (sentiment: positive)');
gatewayBrowser.stop();
gatewayLocalDaemon.stop();
gatewayRemote.stop();
});
});
});