Files
clide/test/builtin/claude/session_naming_test.dart
T
jpmschweitzerandClaude Opus 4.7 39f9ed018c drive the Claude pane over stream-json with native prompts
Replaces the Claude pane's tmux-TUI + transcript-tail backend with
Claude Code's stream-json control protocol (D-77/D-78). A
StreamJsonSession owns the `claude` process: its event stream feeds the
existing ConversationController, and permission / AskUserQuestion
prompts arrive as can_use_tool control_requests. Those surface as a
ToolPrompt in the composer zone — the pane swaps the text input for an
Allow/Deny card or an option picker while a prompt is open, so
interaction stays out of the conversation stream and the prompt buttons
don't fight the message-card hover chrome. The decision is written back
as a control_response (allow echoes updatedInput; AskUserQuestion
answers go in updatedInput.answers). Unsupported control subtypes are
answered with an error so a turn never hangs.

Session continuity is --resume (existing transcript) vs --session-id
(new); /clear and /resume respawn the process. The transcript reader
still backs the sidebar/status/team surfaces. T-165, T-166.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 08:40:18 +02:00

92 lines
3.4 KiB
Dart

import 'package:clide/builtin/claude/src/session_naming.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('claude session naming', () {
test('primary name is deterministic per repo path', () {
final a = primarySessionName('/home/me/clide');
final b = primarySessionName('/home/me/clide');
expect(a, b);
expect(a, startsWith('clide-claude-'));
});
test('different repos yield different primaries', () {
final a = primarySessionName('/home/me/clide');
final b = primarySessionName('/home/me/other');
expect(a, isNot(b));
});
test('secondary names carry the N suffix', () {
final p = primarySessionName('/home/me/clide');
final s1 = secondarySessionName('/home/me/clide', 1);
final s2 = secondarySessionName('/home/me/clide', 2);
expect(s1, '$p-1');
expect(s2, '$p-2');
});
test('a HOME-relative path collapses the HOME prefix in the slug', () {
// Forces the `p.startsWith(home)` branch.
final home = const String.fromEnvironment('HOME');
// Use a path we know lives under the platform HOME so the branch fires.
// In test environments HOME is set; the path /tmp may or may not be
// under it. Use a synthesized HOME path so the assert holds regardless.
final fake = '${home.isEmpty ? '/home/test' : home}/projects/clide';
final name = primarySessionName(fake);
expect(name, contains('projects-clide'));
});
test('path of only "/" slugifies to "root"', () {
// Exercises the "strip leading/trailing '-' then fall back" branch.
expect(primarySessionName('/'), 'clide-claude-root');
});
test('path longer than the slug cap hashes to 8 hex chars', () {
final long = '/${'segment/' * 30}leaf';
final name = primarySessionName(long);
// Hash form: clide-claude-<8 hex>.
expect(name, matches(RegExp(r'^clide-claude-[0-9a-f]{8}$')));
});
test('the same long path produces a stable hash', () {
final long = '/${'a/' * 200}';
expect(primarySessionName(long), primarySessionName(long));
});
});
group('claude session ids (T-146)', () {
final uuidRe = RegExp(r'^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$');
test('primary id is a valid v4-format UUID', () {
expect(primarySessionId('/home/me/clide'), matches(uuidRe));
});
test('primary id is deterministic per repo (resumes across restarts)', () {
expect(primarySessionId('/home/me/clide'), primarySessionId('/home/me/clide'));
});
test('different repos get different primary ids', () {
expect(primarySessionId('/home/me/clide'), isNot(primarySessionId('/home/me/other')));
});
test('fresh ids are valid UUIDs and unique per call (clean secondaries)', () {
final a = freshSessionId();
final b = freshSessionId();
expect(a, matches(uuidRe));
expect(b, matches(uuidRe));
expect(a, isNot(b));
});
});
group('claudeLaunchArgs (T-161)', () {
test('resumes an existing session with --resume, not --session-id', () {
// --session-id refuses an existing id ("already in use"), so resuming
// (transcript on disk) must use --resume.
expect(claudeLaunchArgs('abc', resume: true), ['--resume', 'abc']);
});
test('creates a new session with --session-id', () {
expect(claudeLaunchArgs('abc', resume: false), ['--session-id', 'abc']);
});
});
}