feat(ipcclient): Add clientOnly mode to prevent clients from auto-starting servers and improve registration/reconnect behavior

This commit is contained in:
2025-08-29 08:48:38 +00:00
parent fd3fc7518b
commit fa53dcfc4f
8 changed files with 178 additions and 40 deletions

View File

@@ -192,6 +192,61 @@ tap.test('Client retry should work with delayed server', async () => {
await server.stop();
});
// Test 7: clientOnly prevents client from auto-starting a server
tap.test('clientOnly should prevent auto-start and fail fast', async () => {
const uniqueSocketPath = path.join(os.tmpdir(), `smartipc-clientonly-${Date.now()}.sock`);
const client = smartipc.SmartIpc.createClient({
id: 'clientonly-test',
socketPath: uniqueSocketPath,
clientId: 'co-client-1',
clientOnly: true,
connectRetry: { enabled: false }
});
let failed = false;
try {
await client.connect();
} catch (err: any) {
failed = true;
expect(err.message).toContain('clientOnly prevents auto-start');
}
expect(failed).toBeTrue();
// Ensure no server-side socket was created
expect(fs.existsSync(uniqueSocketPath)).toBeFalse();
});
// Test 8: env SMARTIPC_CLIENT_ONLY enforces clientOnly behavior
tap.test('SMARTIPC_CLIENT_ONLY=1 should enforce clientOnly', async () => {
const uniqueSocketPath = path.join(os.tmpdir(), `smartipc-clientonly-env-${Date.now()}.sock`);
const prev = process.env.SMARTIPC_CLIENT_ONLY;
process.env.SMARTIPC_CLIENT_ONLY = '1';
const client = smartipc.SmartIpc.createClient({
id: 'clientonly-test-env',
socketPath: uniqueSocketPath,
clientId: 'co-client-2',
connectRetry: { enabled: false }
});
let failed = false;
try {
await client.connect();
} catch (err: any) {
failed = true;
expect(err.message).toContain('clientOnly prevents auto-start');
}
expect(failed).toBeTrue();
expect(fs.existsSync(uniqueSocketPath)).toBeFalse();
// restore env
if (prev === undefined) {
delete process.env.SMARTIPC_CLIENT_ONLY;
} else {
process.env.SMARTIPC_CLIENT_ONLY = prev;
}
});
// Cleanup
tap.test('Cleanup test socket', async () => {
try {
@@ -201,4 +256,4 @@ tap.test('Cleanup test socket', async () => {
}
});
export default tap.start();
export default tap.start();

View File

@@ -2,6 +2,10 @@ import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as smartipc from '../ts/index.js';
import * as smartdelay from '@push.rocks/smartdelay';
import * as smartpromise from '@push.rocks/smartpromise';
import * as path from 'path';
import * as os from 'os';
const testSocketPath = path.join(os.tmpdir(), `test-smartipc-${Date.now()}.sock`);
let server: smartipc.IpcServer;
let client1: smartipc.IpcClient;
@@ -11,12 +15,13 @@ let client2: smartipc.IpcClient;
tap.test('should create and start an IPC server', async () => {
server = smartipc.SmartIpc.createServer({
id: 'test-server',
socketPath: '/tmp/test-smartipc.sock',
socketPath: testSocketPath,
autoCleanupSocketFile: true,
heartbeat: true,
heartbeatInterval: 2000
});
await server.start();
await server.start({ readyWhen: 'accepting' });
expect(server.getStats().isRunning).toBeTrue();
});
@@ -24,11 +29,12 @@ tap.test('should create and start an IPC server', async () => {
tap.test('should create and connect a client', async () => {
client1 = smartipc.SmartIpc.createClient({
id: 'test-server',
socketPath: '/tmp/test-smartipc.sock',
socketPath: testSocketPath,
clientId: 'client-1',
metadata: { name: 'Test Client 1' },
autoReconnect: true,
heartbeat: true
heartbeat: true,
clientOnly: true
});
await client1.connect();
@@ -76,9 +82,10 @@ tap.test('should handle request/response pattern', async () => {
tap.test('should handle multiple clients', async () => {
client2 = smartipc.SmartIpc.createClient({
id: 'test-server',
socketPath: '/tmp/test-smartipc.sock',
socketPath: testSocketPath,
clientId: 'client-2',
metadata: { name: 'Test Client 2' }
metadata: { name: 'Test Client 2' },
clientOnly: true
});
await client2.connect();
@@ -296,4 +303,4 @@ tap.test('should cleanup and close all connections', async () => {
expect(client1.getIsConnected()).toBeFalse();
});
export default tap.start();
export default tap.start();