First slice of T-99 (the D-56-path-a IPC server). What this lands: * lib/src/ipc/paths.dart rewritten — `workspaceSocketPath(root)` returns the per-workspace path per D-70 (FNV-1a 64-bit hash, hex, no crypto dep — D-70 amended in this commit to record the hash choice). Old `defaultSocketPath()` removed; the lone fallback in facade.dart kept with a clear placeholder pending T-127. * lib/src/ipc/server.dart — IpcServer class. ServerSocket.listen accept loop (D-72), 0600 socket + 0700 parent (D-71), stale-node probe + unlink on start, refuses to clobber a live listener. * lib/main.dart — IpcServer started after the first dispatcher is built and swapped on project open (workspace path changes). Failure logged but non-fatal so the UI still works without IPC. * 11 server tests + 5 path tests cover socket modes, multi-conn, stale unlink, live-conflict, idempotent start/stop. T-99 children downstream of T-124 (T-125 / T-126 / T-127 / T-130) are now unblocked. Co-Authored-By: Claude <noreply@anthropic.com>
48 lines
1.5 KiB
Dart
48 lines
1.5 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:clide/src/ipc/paths.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
group('workspaceSocketPath (D-70)', () {
|
|
test('returns the FNV-1a hashed path under the socket directory', () {
|
|
final p = workspaceSocketPath('/home/me/projects/clide');
|
|
expect(p, startsWith('${socketDirectory()}/'));
|
|
expect(p, endsWith('.sock'));
|
|
// 16-char hex hash.
|
|
final hash = p.split('/').last.replaceAll('.sock', '');
|
|
expect(hash, matches(RegExp(r'^[0-9a-f]{16}$')));
|
|
});
|
|
|
|
test('is deterministic for the same input', () {
|
|
expect(
|
|
workspaceSocketPath('/home/me/repo'),
|
|
workspaceSocketPath('/home/me/repo'),
|
|
);
|
|
});
|
|
|
|
test('different workspace roots hash to different paths', () {
|
|
expect(
|
|
workspaceSocketPath('/home/me/repo-a'),
|
|
isNot(workspaceSocketPath('/home/me/repo-b')),
|
|
);
|
|
});
|
|
|
|
test('socketDirectory uses XDG_RUNTIME_DIR on Linux when set', () {
|
|
if (Platform.isMacOS) return;
|
|
final xdg = Platform.environment['XDG_RUNTIME_DIR'];
|
|
if (xdg != null && xdg.isNotEmpty) {
|
|
expect(socketDirectory(), '$xdg/clide');
|
|
} else {
|
|
expect(socketDirectory(), '/tmp/clide');
|
|
}
|
|
});
|
|
|
|
test('socketDirectory uses ~/Library/Caches on macOS', () {
|
|
if (!Platform.isMacOS) return;
|
|
final home = Platform.environment['HOME']!;
|
|
expect(socketDirectory(), '$home/Library/Caches/clide');
|
|
});
|
|
});
|
|
}
|