Files
clide/test/builtin/claude/agent_bootstrap_test.dart
T
jpmschweitzerandClaude Opus 4.8 6d0ebab721 chore: adopt Dart 3.9 toolchain — honest floor + tall-style reformat (T-353)
Raise the declared minimums in pubspec.yaml to what our deps already
require: Flutter >=3.35.0 / Dart >=3.9.0 (was 3.19.0 / 3.5.0). alchemist
0.12 needs Flutter 3.32; Dart 3.9 first ships in Flutter 3.35, so 3.35 is
the binding floor. Pin the exact build toolchain in .fvmrc (Flutter
3.44.1).

Moving to the Dart 3.9 language level switches `dart format` to the new
"tall" style and enables two new lints. This commit is the resulting
mechanical churn, isolated from any behaviour change:
  - whole-tree `dart format` reformat (tall style)
  - `dart fix` for unnecessary_underscores + use_null_aware_elements

No runtime behaviour change; `make test` green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 12:11:53 +02:00

101 lines
4.2 KiB
Dart

/// Tests for the clide-hosted agent bootstrap (Epic B / T-215..T-217, D-83).
library;
import 'package:clide/builtin/claude/src/agent_bootstrap.dart';
import 'package:clide/src/ipc/paths.dart' show workspaceSocketPath;
import 'package:test/test.dart';
void main() {
group('clideContextNote (T-216)', () {
test('names clide, the clide CLI, the env vars, and the parity contract', () {
final note = clideContextNote('/home/dev/proj');
expect(note, contains('inside clide'));
expect(note, contains('clide <subsystem> <verb>'));
expect(note, contains('CLIDE_WORKSPACE'));
expect(note, contains('CLIDE_SOCK'));
expect(note, contains('/home/dev/proj'));
expect(note, contains('D-6'));
});
test('lists only subsystems that dispatch today; not the unfinished status snapshot', () {
final note = clideContextNote('/repo');
expect(note, contains('git status'));
expect(note, contains('editor active'));
// `clide status` (T-221) and live pane reflection (Epic C) are not wired
// yet — the note must not point the agent at a command that returns
// nothing, mirroring the T-213 "never mislead an agent" guard.
expect(note, isNot(contains('clide status')));
});
});
group('clideBashAllowRule (T-217)', () {
test('is the command-scoped Bash rule and rides on --allowedTools', () {
expect(clideBashAllowRule, 'Bash(clide:*)');
expect(clideAllowedToolsArgs, ['--allowedTools', 'Bash(clide:*)']);
});
});
group('agentEnvDelta (T-215)', () {
test('always exports CLIDE_SOCK + CLIDE_WORKSPACE', () {
final d = agentEnvDelta(workspaceRoot: '/repo', socketPath: '/run/clide/abc.sock', currentPath: '/usr/bin', clideCliDir: null);
expect(d['CLIDE_WORKSPACE'], '/repo');
expect(d['CLIDE_SOCK'], '/run/clide/abc.sock');
});
test('leaves PATH untouched when clide is already resolvable (clideCliDir null)', () {
final d = agentEnvDelta(workspaceRoot: '/repo', socketPath: '/s.sock', currentPath: '/usr/bin', clideCliDir: null);
expect(d.containsKey('PATH'), isFalse);
});
test('prepends the cli dir to PATH when given', () {
final d = agentEnvDelta(workspaceRoot: '/repo', socketPath: '/s.sock', currentPath: '/usr/bin:/bin', clideCliDir: '/home/dev/.local/bin');
expect(d['PATH'], '/home/dev/.local/bin:/usr/bin:/bin');
});
test('sets PATH to just the cli dir when there is no current PATH', () {
final d = agentEnvDelta(workspaceRoot: '/repo', socketPath: '/s.sock', currentPath: null, clideCliDir: '/opt/clide/bin');
expect(d['PATH'], '/opt/clide/bin');
});
});
group('resolveClideCliDir (T-215)', () {
test('returns null when clide already resolves on PATH (no PATH change needed)', () {
final dir = resolveClideCliDir(
currentPath: '/usr/bin:/home/dev/.local/bin',
candidateDirs: const ['/repo/native/linux-x64'],
isExecutableFile: (p) => p == '/home/dev/.local/bin/clide',
);
expect(dir, isNull);
});
test('returns the first candidate holding an executable clide when not on PATH', () {
final dir = resolveClideCliDir(
currentPath: '/usr/bin',
candidateDirs: const ['/nope', '/repo/native/linux-x64', '/also'],
isExecutableFile: (p) => p == '/repo/native/linux-x64/clide',
);
expect(dir, '/repo/native/linux-x64');
});
test('returns null when nothing holds clide', () {
final dir = resolveClideCliDir(currentPath: '/usr/bin', candidateDirs: const ['/a', '/b'], isExecutableFile: (_) => false);
expect(dir, isNull);
});
});
group('agentBootstrap (IO wrapper)', () {
test('socket in the delta matches workspaceSocketPath; allow rule in extraArgs', () {
final b = agentBootstrap('/some/workspace');
expect(b.envDelta['CLIDE_SOCK'], workspaceSocketPath('/some/workspace'));
expect(b.envDelta['CLIDE_WORKSPACE'], '/some/workspace');
expect(b.extraArgs, ['--allowedTools', 'Bash(clide:*)']);
});
test('merges over the provided base env', () {
final b = agentBootstrap('/ws', base: {'FOO': 'bar'});
expect(b.envDelta['FOO'], 'bar');
expect(b.envDelta['CLIDE_WORKSPACE'], '/ws');
});
});
}