agent-ecosystem/test/main/services/team/TeamProvisioningDirectRestart.test.ts

152 lines
6.6 KiB
TypeScript

import {
buildDirectTmuxRestartCommand,
buildDirectTmuxRestartEnvAssignments,
hasAnthropicCompatibleAuthTokenEnv,
isAnthropicCompatibleBaseUrl,
isInteractiveShellCommand,
shellQuote,
} from '@main/services/team/provisioning/TeamProvisioningDirectRestart';
import { describe, expect, it } from 'vitest';
describe('TeamProvisioningDirectRestart', () => {
it('quotes shell values without losing apostrophes or empty strings', () => {
expect(shellQuote('')).toBe("''");
expect(shellQuote('/tmp/demo path')).toBe("'/tmp/demo path'");
expect(shellQuote("worker's path")).toBe("'worker'\\''s path'");
});
it('detects interactive shell pane commands by basename', () => {
expect(isInteractiveShellCommand('/bin/zsh')).toBe(true);
expect(isInteractiveShellCommand(' FISH ')).toBe(true);
expect(isInteractiveShellCommand('node')).toBe(false);
expect(isInteractiveShellCommand(undefined)).toBe(false);
});
it('classifies Anthropic-compatible base URLs without accepting first-party or credential URLs', () => {
expect(isAnthropicCompatibleBaseUrl('http://localhost:1234')).toBe(true);
expect(isAnthropicCompatibleBaseUrl('https://proxy.example.test')).toBe(true);
expect(isAnthropicCompatibleBaseUrl('https://api.anthropic.com')).toBe(false);
expect(isAnthropicCompatibleBaseUrl('https://api-staging.anthropic.com')).toBe(false);
expect(isAnthropicCompatibleBaseUrl('http://token@localhost:1234')).toBe(false);
expect(isAnthropicCompatibleBaseUrl('not a url')).toBe(false);
expect(isAnthropicCompatibleBaseUrl('')).toBe(false);
});
it('requires both compatible base URL and auth token for compatible auth token env', () => {
expect(
hasAnthropicCompatibleAuthTokenEnv({
ANTHROPIC_BASE_URL: 'http://localhost:1234',
ANTHROPIC_AUTH_TOKEN: 'local-token',
})
).toBe(true);
expect(
hasAnthropicCompatibleAuthTokenEnv({
ANTHROPIC_BASE_URL: 'http://localhost:1234',
ANTHROPIC_AUTH_TOKEN: ' ',
})
).toBe(false);
expect(
hasAnthropicCompatibleAuthTokenEnv({
ANTHROPIC_BASE_URL: 'https://api.anthropic.com',
ANTHROPIC_AUTH_TOKEN: 'stale-token',
})
).toBe(false);
});
it('preserves provider-specific direct restart env while resetting provider selection flags', () => {
const assignments = buildDirectTmuxRestartEnvAssignments(
{
CODEX_HOME: '/tmp/codex home',
CODEX_CLI_PATH: '/opt/codex/bin/codex',
CLAUDE_CODE_USE_GEMINI: '1',
CLAUDE_CODE_ENTRY_PROVIDER: 'gemini',
CLAUDE_CODE_CODEX_BACKEND: 'codex-native',
CLAUDE_CODE_CODEX_FORCED_LOGIN_METHOD: 'chatgpt',
CLAUDE_TEAM_RUNTIME_SETTINGS_PATH: '/tmp/runtime-settings.json',
},
'codex'
);
expect(assignments).toContain("CLAUDECODE='1'");
expect(assignments).toContain("CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS='1'");
expect(assignments).toContain("CODEX_HOME='/tmp/codex home'");
expect(assignments).toContain("CODEX_CLI_PATH='/opt/codex/bin/codex'");
expect(assignments).toContain("CLAUDE_CODE_USE_GEMINI=''");
expect(assignments).toContain("CLAUDE_CODE_ENTRY_PROVIDER='codex'");
expect(assignments).toContain("CLAUDE_CODE_CODEX_BACKEND='codex-native'");
expect(assignments).toContain("CLAUDE_CODE_CODEX_FORCED_LOGIN_METHOD='chatgpt'");
expect(assignments).toContain("CLAUDE_TEAM_RUNTIME_SETTINGS_PATH='/tmp/runtime-settings.json'");
expect(assignments).toContain("CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST='1'");
});
it('preserves Anthropic-compatible tokens but blanks stale first-party auth tokens', () => {
const compatibleAssignments = buildDirectTmuxRestartEnvAssignments(
{
ANTHROPIC_BASE_URL: ' http://localhost:1234 ',
ANTHROPIC_AUTH_TOKEN: ' local-token ',
ANTHROPIC_API_KEY: '',
},
'anthropic'
);
expect(compatibleAssignments).toContain("ANTHROPIC_BASE_URL='http://localhost:1234'");
expect(compatibleAssignments).toContain("ANTHROPIC_AUTH_TOKEN='local-token'");
expect(compatibleAssignments).toContain("ANTHROPIC_API_KEY=''");
const firstPartyAssignments = buildDirectTmuxRestartEnvAssignments(
{
ANTHROPIC_BASE_URL: 'https://api.anthropic.com',
ANTHROPIC_AUTH_TOKEN: 'stale-token',
},
'anthropic'
);
expect(firstPartyAssignments).toContain("ANTHROPIC_BASE_URL='https://api.anthropic.com'");
expect(firstPartyAssignments).toContain("ANTHROPIC_AUTH_TOKEN=''");
expect(firstPartyAssignments).not.toContain('stale-token');
});
it('blanks competing Anthropic helper auth carriers for direct restart helper mode', () => {
const assignments = buildDirectTmuxRestartEnvAssignments(
{
CLAUDE_TEAM_ANTHROPIC_AUTH_MODE: 'api_key_helper',
CLAUDE_TEAM_ANTHROPIC_API_KEY_HELPER_SETTINGS_PATH:
'/tmp/team-runtime-auth/demo/runtime-settings-anthropic.json',
ANTHROPIC_API_KEY: 'sk-ant-direct-restart-should-not-leak',
ANTHROPIC_AUTH_TOKEN: 'direct-restart-token-should-not-leak',
CLAUDE_CODE_API_KEY_FILE_DESCRIPTOR: '3',
CLAUDE_CODE_OAUTH_TOKEN: 'direct-restart-oauth-token-should-not-leak',
CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR: '4',
},
'anthropic'
);
expect(assignments).toContain("CLAUDE_TEAM_ANTHROPIC_AUTH_MODE='api_key_helper'");
expect(assignments).toContain(
"CLAUDE_TEAM_ANTHROPIC_API_KEY_HELPER_SETTINGS_PATH='/tmp/team-runtime-auth/demo/runtime-settings-anthropic.json'"
);
expect(assignments).toContain("ANTHROPIC_API_KEY=''");
expect(assignments).toContain("ANTHROPIC_AUTH_TOKEN=''");
expect(assignments).toContain("CLAUDE_CODE_API_KEY_FILE_DESCRIPTOR=''");
expect(assignments).toContain("CLAUDE_CODE_OAUTH_TOKEN=''");
expect(assignments).toContain("CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR=''");
expect(assignments).not.toContain('sk-ant-direct-restart-should-not-leak');
expect(assignments).not.toContain('direct-restart-token-should-not-leak');
expect(assignments).not.toContain('direct-restart-oauth-token-should-not-leak');
});
it('builds a restart command that preserves cwd, binary and args quoting', () => {
const command = buildDirectTmuxRestartCommand({
cwd: '/tmp/team work',
env: { CODEX_HOME: '/tmp/codex' },
providerId: 'codex',
binaryPath: '/usr/local/bin/claude',
args: ['--model', "gpt worker's model"],
});
expect(command).toContain("cd '/tmp/team work' && env");
expect(command).toContain("CODEX_HOME='/tmp/codex'");
expect(command).toContain("'/usr/local/bin/claude' '--model' 'gpt worker'\\''s model'");
expect(command).toContain('__CLAUDE_TEAMMATE_EXIT__:%s');
});
});