Full SDK workspace: core, contracts, sdk, cli, browser-host, browser-kit, federation, omega-core, oracle, self-healing, strategies, tools, emacs. Clean extraction from the development monorepo. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
274 lines
7.7 KiB
TypeScript
274 lines
7.7 KiB
TypeScript
/**
|
|
* Cross-Realm Two-Gateway Integration Test
|
|
*
|
|
* Tests actual cross-realm communication where:
|
|
* - Realm A's EdgeGateway sends a message
|
|
* - Backbone routes it to Realm B's mailbox
|
|
* - Realm B's EdgeGateway receives and processes it
|
|
* - Reply flows back to Realm A
|
|
*/
|
|
|
|
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('Cross-Realm Two Gateways', () => {
|
|
|
|
it('Realm A sends request to Realm B and receives reply', async () => {
|
|
const REALM_A = 'realm-alpha';
|
|
const REALM_B = 'realm-beta';
|
|
|
|
// Shared backbone broker (simulates HiveMQ)
|
|
const backbone = new InMemoryFilterBroker();
|
|
|
|
// Realm A's local bus
|
|
const localBusA = new InMemoryFilterBroker();
|
|
|
|
// Realm B's local bus
|
|
const localBusB = new InMemoryFilterBroker();
|
|
|
|
// Create EdgeGateway for Realm A
|
|
const gatewayA = new FederationPeer({
|
|
localRealm: REALM_A,
|
|
profile: WIRE_PROFILES.matrix3,
|
|
backbone,
|
|
localBus: localBusA,
|
|
internalTimeoutMs: 5000,
|
|
});
|
|
|
|
// Create EdgeGateway for Realm B
|
|
const gatewayB = new FederationPeer({
|
|
localRealm: REALM_B,
|
|
profile: WIRE_PROFILES.matrix3,
|
|
backbone,
|
|
localBus: localBusB,
|
|
internalTimeoutMs: 5000,
|
|
});
|
|
|
|
// Start both gateways
|
|
gatewayA.start();
|
|
gatewayB.start();
|
|
|
|
// Set up handler on Realm B's local bus
|
|
localBusB.subscribe(`calculator.accepts.add`, (topic, payload) => {
|
|
const req = JSON.parse(payload) as {
|
|
replyTo: string;
|
|
correlationId: string;
|
|
params: { a: number; b: number };
|
|
};
|
|
|
|
// Calculate result
|
|
const result = req.params.a + req.params.b;
|
|
|
|
// Send reply
|
|
localBusB.publish(req.replyTo, JSON.stringify({
|
|
ok: true,
|
|
correlationId: req.correlationId,
|
|
result: { sum: result },
|
|
}));
|
|
});
|
|
|
|
// Set up listener for reply on Realm A's mailbox
|
|
let receivedReply: unknown = null;
|
|
backbone.subscribe(getLegacyIngressTopic(REALM_A), (topic, payload) => {
|
|
const parsed = JSON.parse(payload);
|
|
if (parsed.ok !== undefined) { // It's a reply
|
|
receivedReply = parsed;
|
|
}
|
|
});
|
|
|
|
await new Promise(r => setTimeout(r, 100));
|
|
|
|
// Realm A sends request to Realm B
|
|
const request = createLegacyRequest({
|
|
toRealm: REALM_B,
|
|
toPath: 'calculator.accepts.add',
|
|
fromRealm: REALM_A,
|
|
replyTo: '_client.inbox',
|
|
correlationId: 'cross-realm-123',
|
|
body: { a: 10, b: 32 },
|
|
});
|
|
|
|
// Publish to Realm B's mailbox on backbone
|
|
backbone.publish(getLegacyIngressTopic(REALM_B), serializeLegacyEnvelope(request));
|
|
|
|
// Wait for round-trip
|
|
await new Promise(r => setTimeout(r, 500));
|
|
|
|
// Verify reply was received
|
|
assert.ok(receivedReply, 'Should receive reply from Realm B');
|
|
assert.strictEqual((receivedReply as { ok: boolean }).ok, true, 'Reply should be ok');
|
|
assert.deepStrictEqual(
|
|
(receivedReply as { body: { sum: number } }).body,
|
|
{ sum: 42 },
|
|
'Sum should be 42'
|
|
);
|
|
|
|
console.log('✓ Cross-realm request/reply successful: 10 + 32 = 42');
|
|
|
|
gatewayA.stop();
|
|
gatewayB.stop();
|
|
});
|
|
|
|
it('Three realms: A → B → C chain', async () => {
|
|
const REALM_A = 'realm-a';
|
|
const REALM_B = 'realm-b';
|
|
const REALM_C = 'realm-c';
|
|
|
|
const backbone = new InMemoryFilterBroker();
|
|
const localBusA = new InMemoryFilterBroker();
|
|
const localBusB = new InMemoryFilterBroker();
|
|
const localBusC = new InMemoryFilterBroker();
|
|
|
|
const gatewayA = new FederationPeer({
|
|
localRealm: REALM_A,
|
|
profile: WIRE_PROFILES.matrix3,
|
|
backbone,
|
|
localBus: localBusA,
|
|
internalTimeoutMs: 5000,
|
|
});
|
|
|
|
const gatewayB = new FederationPeer({
|
|
localRealm: REALM_B,
|
|
profile: WIRE_PROFILES.matrix3,
|
|
backbone,
|
|
localBus: localBusB,
|
|
internalTimeoutMs: 5000,
|
|
});
|
|
|
|
const gatewayC = new FederationPeer({
|
|
localRealm: REALM_C,
|
|
profile: WIRE_PROFILES.matrix3,
|
|
backbone,
|
|
localBus: localBusC,
|
|
internalTimeoutMs: 5000,
|
|
});
|
|
|
|
gatewayA.start();
|
|
gatewayB.start();
|
|
gatewayC.start();
|
|
|
|
// Handler on Realm C
|
|
localBusC.subscribe(`service.accepts.echo`, (topic, payload) => {
|
|
const req = JSON.parse(payload) as { replyTo: string; correlationId: string; params: { msg: string } };
|
|
localBusC.publish(req.replyTo, JSON.stringify({
|
|
ok: true,
|
|
correlationId: req.correlationId,
|
|
result: { echo: `Echo from C: ${req.params.msg}` },
|
|
}));
|
|
});
|
|
|
|
// Collect replies at Realm A
|
|
let replyFromC: unknown = null;
|
|
backbone.subscribe(getLegacyIngressTopic(REALM_A), (topic, payload) => {
|
|
const parsed = JSON.parse(payload);
|
|
if (parsed.ok !== undefined) {
|
|
replyFromC = parsed;
|
|
}
|
|
});
|
|
|
|
await new Promise(r => setTimeout(r, 100));
|
|
|
|
// A sends directly to C (skipping B - this tests that each realm has its own mailbox)
|
|
const request = createLegacyRequest({
|
|
toRealm: REALM_C,
|
|
toPath: 'service.accepts.echo',
|
|
fromRealm: REALM_A,
|
|
replyTo: '_client.inbox',
|
|
correlationId: 'a-to-c-456',
|
|
body: { msg: 'hello from A' },
|
|
});
|
|
|
|
backbone.publish(getLegacyIngressTopic(REALM_C), serializeLegacyEnvelope(request));
|
|
|
|
await new Promise(r => setTimeout(r, 500));
|
|
|
|
assert.ok(replyFromC, 'Should receive reply from Realm C');
|
|
assert.strictEqual((replyFromC as { ok: boolean }).ok, true);
|
|
assert.deepStrictEqual(
|
|
(replyFromC as { body: { echo: string } }).body,
|
|
{ echo: 'Echo from C: hello from A' }
|
|
);
|
|
|
|
console.log('✓ Three-realm test passed: A → C direct communication');
|
|
|
|
gatewayA.stop();
|
|
gatewayB.stop();
|
|
gatewayC.stop();
|
|
});
|
|
|
|
it('Realm isolation: B cannot intercept messages for A', async () => {
|
|
const REALM_A = 'secure-realm-a';
|
|
const REALM_B = 'secure-realm-b';
|
|
|
|
const backbone = new InMemoryFilterBroker();
|
|
const localBusA = new InMemoryFilterBroker();
|
|
const localBusB = new InMemoryFilterBroker();
|
|
|
|
const gatewayA = new FederationPeer({
|
|
localRealm: REALM_A,
|
|
profile: WIRE_PROFILES.matrix3,
|
|
backbone,
|
|
localBus: localBusA,
|
|
});
|
|
|
|
const gatewayB = new FederationPeer({
|
|
localRealm: REALM_B,
|
|
profile: WIRE_PROFILES.matrix3,
|
|
backbone,
|
|
localBus: localBusB,
|
|
});
|
|
|
|
gatewayA.start();
|
|
gatewayB.start();
|
|
|
|
// Track what each realm's local bus receives
|
|
let realmAReceived = 0;
|
|
let realmBReceived = 0;
|
|
|
|
localBusA.subscribe(`secret.accepts.data`, () => {
|
|
realmAReceived++;
|
|
});
|
|
|
|
localBusB.subscribe(`secret.accepts.data`, () => {
|
|
realmBReceived++;
|
|
});
|
|
|
|
// Also try subscribing B to A's internal topic (should never fire)
|
|
localBusB.subscribe(`secret.accepts.data`, () => {
|
|
realmBReceived += 100; // Should never happen
|
|
});
|
|
|
|
await new Promise(r => setTimeout(r, 100));
|
|
|
|
// Send message to Realm A's mailbox
|
|
const request = createLegacyRequest({
|
|
toRealm: REALM_A,
|
|
toPath: 'secret.accepts.data',
|
|
fromRealm: 'external',
|
|
replyTo: '_inbox',
|
|
correlationId: 'secret-msg',
|
|
body: { secret: 'classified' },
|
|
});
|
|
|
|
backbone.publish(getLegacyIngressTopic(REALM_A), serializeLegacyEnvelope(request));
|
|
|
|
await new Promise(r => setTimeout(r, 300));
|
|
|
|
assert.strictEqual(realmAReceived, 1, 'Realm A should receive the message');
|
|
assert.strictEqual(realmBReceived, 0, 'Realm B should NOT receive messages for Realm A');
|
|
|
|
console.log('✓ Realm isolation verified: B cannot intercept A\'s messages');
|
|
|
|
gatewayA.stop();
|
|
gatewayB.stop();
|
|
});
|
|
});
|