rhizome-node/__tests__/run/002-two-nodes-orchestrated.ts
Lentil Hoffman 675a0656c6
feat: Refactor Docker orchestrator and enhance test utilities
This commit includes a major refactoring of the Docker orchestrator implementation
along with improvements to the testing infrastructure:

- Refactored Docker orchestrator to handle dynamic port assignment
- Added comprehensive test utilities in docker-test-utils.ts
- Improved error handling and resource cleanup in test environments
- Enhanced NodeStatus interface with containerId and networkId
- Added support for different storage types in NodeConfig
- Fixed request port handling in TestOrchestrator
- Added proper cleanup method to NodeOrchestrator interface

The changes ensure more reliable container management and better test isolation
while maintaining backward compatibility with existing implementations.

BREAKING CHANGE: The NodeOrchestrator interface now requires a cleanup() method.
2025-06-18 19:56:09 -05:00

135 lines
3.9 KiB
TypeScript

import Debug from 'debug';
import { createOrchestrator } from '../../src/orchestration';
import type { NodeConfig, NodeHandle } from '../../src/orchestration';
// Increase test timeout to 30 seconds
jest.setTimeout(30000);
const debug = Debug('test:two-orchestrated');
describe('Run (Two Nodes Orchestrated)', () => {
const orchestrator = createOrchestrator('in-memory');
// Define a type that includes all required methods
type FullNodeHandle = NodeHandle & {
getRequestPort: () => number;
getApiUrl: () => string;
};
const nodes: FullNodeHandle[] = [];
const nodeIds = ['app-002-A', 'app-002-B'];
beforeAll(async () => {
console.time('Test setup');
// Start first node
console.time('Create node1 config');
const node1Config: NodeConfig = {
id: nodeIds[0],
};
console.timeEnd('Create node1 config');
console.time('Start node1');
const node1 = (await orchestrator.startNode(node1Config)) as FullNodeHandle;
console.timeEnd('Start node1');
// Start second node with first node as bootstrap peer
console.time('Create node2 config');
const node2Config: NodeConfig = {
id: nodeIds[1],
network: {
bootstrapPeers: [`localhost:${node1.getRequestPort()}`],
},
};
console.timeEnd('Create node2 config');
console.time('Start node2');
const node2 = (await orchestrator.startNode(node2Config)) as FullNodeHandle;
console.timeEnd('Start node2');
nodes.push(node1, node2);
// Connect the nodes
console.time('Connect nodes');
await orchestrator.connectNodes(node1, node2);
console.timeEnd('Connect nodes');
console.timeEnd('Test setup');
}, 120000); // Increase timeout to 120s for this hook
afterAll(async () => {
// Stop all nodes in parallel
await Promise.all(nodes.map(node => node && orchestrator.stopNode(node)));
});
it('can create a record on node0 and read it from node1', async () => {
const [node0, node1] = nodes;
const node0Url = node0.getApiUrl();
const node1Url = node1.getApiUrl();
debug(`Node 0 URL: ${node0Url}`);
debug(`Node 1 URL: ${node1Url}`);
// Create a new record on node0
const createResponse = await fetch(`${node0Url}/user`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: 'peon-1',
properties: {
name: 'Peon',
age: 741,
},
}),
});
const createdUser = await createResponse.json();
expect(createdUser).toMatchObject({
id: 'peon-1',
properties: {
name: 'Peon',
age: 741,
},
});
// Small delay to allow for synchronization
await new Promise(resolve => setTimeout(resolve, 100));
// Read the record from node1
const getResponse = await fetch(`${node1Url}/user/peon-1`);
const fetchedUser = await getResponse.json();
expect(fetchedUser).toMatchObject({
id: 'peon-1',
properties: {
name: 'Peon',
age: 741,
},
});
});
it.skip('can demonstrate network partitioning', async () => {
// This test shows how we can simulate network partitions
// For now, it's just a placeholder since we'd need to implement
// the actual partitioning logic in the InMemoryOrchestrator
const [node0, node1] = nodes;
// Simulate partition (actual implementation would use orchestrator.partitionNetwork)
debug('Simulating network partition between nodes');
// await orchestrator.partitionNetwork({
// groups: [[node0.id], [node1.id]]
// });
// Test behavior during partition...
// Heal partition
// await orchestrator.partitionNetwork({
// groups: [[node0.id, node1.id]]
// });
// Test behavior after healing...
// Mark test as passed (remove once actual test is implemented)
expect(true).toBe(true);
});
});