mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-17 02:13:53 +00:00
fix: normalize plugin metadata across shared launch flows
This commit is contained in:
1 parent
2cb5b4ebec
commit
1043a433f6
8 files changed
+431
-40
No files matched your search
@@ -7,14 +7,35 @@ import SharedManager from '../../src/management/shared-manager';
|
||||
|
||||
describe('InstanceManager MCP sync', () => {
|
||||
let tempRoot = '';
|
||||
let originalHome: string | undefined;
|
||||
let originalCcsHome: string | undefined;
|
||||
let originalCcsDir: string | undefined;
|
||||
|
||||
function writeMarketplaceRegistry(registryPath: string, installLocation: string): void {
|
||||
fs.mkdirSync(path.dirname(registryPath), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
registryPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
'claude-code-plugins': {
|
||||
installLocation,
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
'utf8'
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-instance-mcp-test-'));
|
||||
originalHome = process.env.HOME;
|
||||
originalCcsHome = process.env.CCS_HOME;
|
||||
originalCcsDir = process.env.CCS_DIR;
|
||||
|
||||
spyOn(os, 'homedir').mockReturnValue(tempRoot);
|
||||
process.env.HOME = tempRoot;
|
||||
process.env.CCS_HOME = tempRoot;
|
||||
delete process.env.CCS_DIR;
|
||||
});
|
||||
@@ -22,6 +43,9 @@ describe('InstanceManager MCP sync', () => {
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
|
||||
if (originalHome !== undefined) process.env.HOME = originalHome;
|
||||
else delete process.env.HOME;
|
||||
|
||||
if (originalCcsHome !== undefined) process.env.CCS_HOME = originalCcsHome;
|
||||
else delete process.env.CCS_HOME;
|
||||
|
||||
@@ -71,7 +95,9 @@ describe('InstanceManager MCP sync', () => {
|
||||
const synced = manager.syncMcpServers(instancePath);
|
||||
expect(synced).toBe(true);
|
||||
|
||||
const instanceContent = JSON.parse(fs.readFileSync(path.join(instancePath, '.claude.json'), 'utf8'));
|
||||
const instanceContent = JSON.parse(
|
||||
fs.readFileSync(path.join(instancePath, '.claude.json'), 'utf8')
|
||||
);
|
||||
expect(instanceContent.otherKey).toBe('keep-me');
|
||||
expect(instanceContent.mcpServers).toEqual({
|
||||
globalOnly: { command: 'global-cmd' },
|
||||
@@ -96,45 +122,118 @@ describe('InstanceManager MCP sync', () => {
|
||||
});
|
||||
|
||||
it('skips shared symlinks and MCP sync for bare instance creation', async () => {
|
||||
const linkSharedSpy = spyOn(SharedManager.prototype, 'linkSharedDirectories').mockImplementation(
|
||||
() => {}
|
||||
);
|
||||
const linkSharedSpy = spyOn(
|
||||
SharedManager.prototype,
|
||||
'linkSharedDirectories'
|
||||
).mockImplementation(() => {});
|
||||
spyOn(SharedManager.prototype, 'syncProjectContext').mockResolvedValue(undefined);
|
||||
spyOn(SharedManager.prototype, 'syncAdvancedContinuityArtifacts').mockResolvedValue(undefined);
|
||||
const normalizeSharedPluginMetadataSpy = spyOn(
|
||||
SharedManager.prototype,
|
||||
'normalizeSharedPluginMetadataPaths'
|
||||
).mockImplementation(() => {});
|
||||
const syncMcpSpy = spyOn(InstanceManager.prototype, 'syncMcpServers').mockImplementation(
|
||||
() => false
|
||||
);
|
||||
|
||||
const manager = new InstanceManager();
|
||||
const instancePath = manager.getInstancePath('sandbox');
|
||||
const sharedRegistryPath = path.join(tempRoot, '.claude', 'plugins', 'known_marketplaces.json');
|
||||
writeMarketplaceRegistry(
|
||||
sharedRegistryPath,
|
||||
path.join(
|
||||
tempRoot,
|
||||
'.ccs',
|
||||
'instances',
|
||||
'work',
|
||||
'plugins',
|
||||
'marketplaces',
|
||||
'claude-code-plugins'
|
||||
)
|
||||
);
|
||||
|
||||
await manager.ensureInstance('sandbox', { mode: 'isolated' }, { bare: true });
|
||||
|
||||
const normalized = JSON.parse(fs.readFileSync(sharedRegistryPath, 'utf8'));
|
||||
|
||||
expect(linkSharedSpy).not.toHaveBeenCalled();
|
||||
expect(normalizeSharedPluginMetadataSpy).not.toHaveBeenCalled();
|
||||
expect(fs.existsSync(instancePath)).toBe(true);
|
||||
expect(normalized['claude-code-plugins'].installLocation).toBe(
|
||||
path.join(tempRoot, '.claude', 'plugins', 'marketplaces', 'claude-code-plugins')
|
||||
);
|
||||
expect(fs.existsSync(path.join(instancePath, 'plugins', 'known_marketplaces.json'))).toBe(
|
||||
false
|
||||
);
|
||||
expect(syncMcpSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('normalizes shared plugin metadata for existing non-bare instances', async () => {
|
||||
spyOn(SharedManager.prototype, 'syncProjectContext').mockResolvedValue(undefined);
|
||||
spyOn(SharedManager.prototype, 'syncAdvancedContinuityArtifacts').mockResolvedValue(undefined);
|
||||
const normalizeSharedPluginMetadataSpy = spyOn(
|
||||
SharedManager.prototype,
|
||||
'normalizeSharedPluginMetadataPaths'
|
||||
).mockImplementation(() => {});
|
||||
const syncMcpSpy = spyOn(InstanceManager.prototype, 'syncMcpServers').mockImplementation(
|
||||
() => false
|
||||
);
|
||||
|
||||
const manager = new InstanceManager();
|
||||
const instancePath = manager.getInstancePath('work');
|
||||
fs.mkdirSync(instancePath, { recursive: true });
|
||||
writeMarketplaceRegistry(
|
||||
path.join(instancePath, 'plugins', 'known_marketplaces.json'),
|
||||
path.join(
|
||||
tempRoot,
|
||||
'.ccs',
|
||||
'instances',
|
||||
'work',
|
||||
'plugins',
|
||||
'marketplaces',
|
||||
'claude-code-plugins'
|
||||
)
|
||||
);
|
||||
|
||||
await manager.ensureInstance('work', { mode: 'isolated' });
|
||||
|
||||
expect(normalizeSharedPluginMetadataSpy).toHaveBeenCalledTimes(1);
|
||||
const normalized = JSON.parse(
|
||||
fs.readFileSync(path.join(instancePath, 'plugins', 'known_marketplaces.json'), 'utf8')
|
||||
);
|
||||
expect(normalized['claude-code-plugins'].installLocation).toBe(
|
||||
path.join(tempRoot, '.claude', 'plugins', 'marketplaces', 'claude-code-plugins')
|
||||
);
|
||||
expect(syncMcpSpy).toHaveBeenCalledWith(instancePath);
|
||||
});
|
||||
|
||||
it('normalizes shared plugin metadata during new non-bare instance creation', async () => {
|
||||
spyOn(SharedManager.prototype, 'syncProjectContext').mockResolvedValue(undefined);
|
||||
spyOn(SharedManager.prototype, 'syncAdvancedContinuityArtifacts').mockResolvedValue(undefined);
|
||||
const syncMcpSpy = spyOn(InstanceManager.prototype, 'syncMcpServers').mockImplementation(
|
||||
() => false
|
||||
);
|
||||
|
||||
const registryPath = path.join(tempRoot, '.claude', 'plugins', 'known_marketplaces.json');
|
||||
writeMarketplaceRegistry(
|
||||
registryPath,
|
||||
path.join(
|
||||
tempRoot,
|
||||
'.ccs',
|
||||
'instances',
|
||||
'work',
|
||||
'plugins',
|
||||
'marketplaces',
|
||||
'claude-code-plugins'
|
||||
)
|
||||
);
|
||||
|
||||
const manager = new InstanceManager();
|
||||
const instancePath = await manager.ensureInstance('work', { mode: 'isolated' });
|
||||
|
||||
const expected = path.join(
|
||||
tempRoot,
|
||||
'.claude',
|
||||
'plugins',
|
||||
'marketplaces',
|
||||
'claude-code-plugins'
|
||||
);
|
||||
const normalizedShared = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
||||
const normalizedInstance = JSON.parse(
|
||||
fs.readFileSync(path.join(instancePath, 'plugins', 'known_marketplaces.json'), 'utf8')
|
||||
);
|
||||
|
||||
expect(normalizedShared['claude-code-plugins'].installLocation).toBe(expected);
|
||||
expect(normalizedInstance['claude-code-plugins'].installLocation).toBe(expected);
|
||||
expect(syncMcpSpy).toHaveBeenCalledWith(instancePath);
|
||||
});
|
||||
});
|
||||
@@ -5,11 +5,13 @@ import { describe, it, expect, beforeEach, afterEach, mock, spyOn } from 'bun:te
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import SharedManager from '../../src/management/shared-manager';
|
||||
import SharedManager, {
|
||||
normalizePluginMetadataPathString,
|
||||
} from '../../src/management/shared-manager';
|
||||
|
||||
// Test the normalization regex pattern directly
|
||||
const normalizePluginPaths = (content: string): string => {
|
||||
return content.replace(/\/\.ccs\/instances\/[^/]+\//g, '/.claude/');
|
||||
return normalizePluginMetadataPathString(content);
|
||||
};
|
||||
|
||||
describe('SharedManager', () => {
|
||||
@@ -17,12 +19,14 @@ describe('SharedManager', () => {
|
||||
let originalHome: string | undefined;
|
||||
let originalCcsHome: string | undefined;
|
||||
let originalCcsDir: string | undefined;
|
||||
let originalPlatform: PropertyDescriptor | undefined;
|
||||
|
||||
beforeEach(() => {
|
||||
tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-shared-manager-test-'));
|
||||
originalHome = process.env.HOME;
|
||||
originalCcsHome = process.env.CCS_HOME;
|
||||
originalCcsDir = process.env.CCS_DIR;
|
||||
originalPlatform = Object.getOwnPropertyDescriptor(process, 'platform');
|
||||
|
||||
spyOn(os, 'homedir').mockReturnValue(tempRoot);
|
||||
process.env.HOME = tempRoot;
|
||||
@@ -42,6 +46,10 @@ describe('SharedManager', () => {
|
||||
if (originalCcsDir !== undefined) process.env.CCS_DIR = originalCcsDir;
|
||||
else delete process.env.CCS_DIR;
|
||||
|
||||
if (originalPlatform) {
|
||||
Object.defineProperty(process, 'platform', originalPlatform);
|
||||
}
|
||||
|
||||
if (tempRoot && fs.existsSync(tempRoot)) {
|
||||
fs.rmSync(tempRoot, { recursive: true, force: true });
|
||||
}
|
||||
@@ -149,9 +157,8 @@ describe('SharedManager', () => {
|
||||
});
|
||||
|
||||
it('should handle Windows-style paths (backslash)', () => {
|
||||
// Windows paths use backslashes, regex should not match
|
||||
const input = 'C:\\Users\\user\\.ccs\\instances\\ck\\plugins\\cache';
|
||||
expect(normalizePluginPaths(input)).toBe(input);
|
||||
expect(normalizePluginPaths(input)).toBe('C:\\Users\\user\\.claude\\plugins\\cache');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -185,5 +192,81 @@ describe('SharedManager', () => {
|
||||
'/home/kai/.claude/plugins/marketplaces/claude-code-plugins'
|
||||
);
|
||||
});
|
||||
|
||||
it('rewrites Windows-style known_marketplaces.json paths on disk', () => {
|
||||
const pluginsDir = path.join(tempRoot, '.claude', 'plugins');
|
||||
fs.mkdirSync(pluginsDir, { recursive: true });
|
||||
|
||||
const registryPath = path.join(pluginsDir, 'known_marketplaces.json');
|
||||
fs.writeFileSync(
|
||||
registryPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
'claude-code-plugins': {
|
||||
installLocation:
|
||||
'C:\\Users\\kai\\.ccs\\instances\\work\\plugins\\marketplaces\\claude-code-plugins',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
const manager = new SharedManager();
|
||||
manager.normalizeMarketplaceRegistryPaths();
|
||||
|
||||
const normalized = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
||||
expect(normalized['claude-code-plugins'].installLocation).toBe(
|
||||
'C:\\Users\\kai\\.claude\\plugins\\marketplaces\\claude-code-plugins'
|
||||
);
|
||||
});
|
||||
|
||||
it('normalizes copied shared and instance metadata under Windows fallback', () => {
|
||||
Object.defineProperty(process, 'platform', { value: 'win32' });
|
||||
spyOn(fs, 'symlinkSync').mockImplementation(() => {
|
||||
throw Object.assign(new Error('simulated symlink failure'), { code: 'EPERM' });
|
||||
});
|
||||
|
||||
const pluginsDir = path.join(tempRoot, '.claude', 'plugins');
|
||||
fs.mkdirSync(pluginsDir, { recursive: true });
|
||||
|
||||
const registryPath = path.join(pluginsDir, 'known_marketplaces.json');
|
||||
fs.writeFileSync(
|
||||
registryPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
'claude-code-plugins': {
|
||||
installLocation:
|
||||
'/home/kai/.ccs/instances/work/plugins/marketplaces/claude-code-plugins',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
const manager = new SharedManager();
|
||||
const instancePath = path.join(tempRoot, '.ccs', 'instances', 'personal');
|
||||
fs.mkdirSync(instancePath, { recursive: true });
|
||||
manager.linkSharedDirectories(instancePath);
|
||||
|
||||
const expected = '/home/kai/.claude/plugins/marketplaces/claude-code-plugins';
|
||||
const claudeRegistry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
||||
const sharedRegistry = JSON.parse(
|
||||
fs.readFileSync(
|
||||
path.join(tempRoot, '.ccs', 'shared', 'plugins', 'known_marketplaces.json'),
|
||||
'utf8'
|
||||
)
|
||||
);
|
||||
const instanceRegistry = JSON.parse(
|
||||
fs.readFileSync(path.join(instancePath, 'plugins', 'known_marketplaces.json'), 'utf8')
|
||||
);
|
||||
|
||||
expect(claudeRegistry['claude-code-plugins'].installLocation).toBe(expected);
|
||||
expect(sharedRegistry['claude-code-plugins'].installLocation).toBe(expected);
|
||||
expect(instanceRegistry['claude-code-plugins'].installLocation).toBe(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,14 @@
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, mock } from 'bun:test';
|
||||
import {
|
||||
afterAll,
|
||||
afterEach,
|
||||
beforeAll,
|
||||
beforeEach,
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
mock,
|
||||
spyOn,
|
||||
} from 'bun:test';
|
||||
import { EventEmitter } from 'events';
|
||||
import * as childProcess from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
@@ -115,6 +125,7 @@ preferences:
|
||||
let execClaude: typeof import('../../../src/utils/shell-executor').execClaude;
|
||||
let stripClaudeCodeEnv: typeof import('../../../src/utils/shell-executor').stripClaudeCodeEnv;
|
||||
let HeadlessExecutor: typeof import('../../../src/delegation/headless-executor').HeadlessExecutor;
|
||||
let SharedManager: typeof import('../../../src/management/shared-manager').default;
|
||||
|
||||
beforeAll(async () => {
|
||||
registerChildProcessMock();
|
||||
@@ -123,6 +134,9 @@ beforeAll(async () => {
|
||||
execClaude = shellExecutor.execClaude;
|
||||
stripClaudeCodeEnv = shellExecutor.stripClaudeCodeEnv;
|
||||
|
||||
const sharedManagerModule = await import('../../../src/management/shared-manager');
|
||||
SharedManager = sharedManagerModule.default;
|
||||
|
||||
const headless = await import('../../../src/delegation/headless-executor');
|
||||
HeadlessExecutor = headless.HeadlessExecutor;
|
||||
});
|
||||
@@ -242,6 +256,32 @@ describe('CLAUDECODE environment stripping', () => {
|
||||
expect(env.DISABLE_AUTOUPDATER).toBeUndefined();
|
||||
});
|
||||
|
||||
it('execClaude normalizes shared plugin metadata before default-profile launch', () => {
|
||||
const normalizeSpy = spyOn(
|
||||
SharedManager.prototype,
|
||||
'normalizeSharedPluginMetadataPaths'
|
||||
).mockImplementation(() => {});
|
||||
|
||||
execClaude('claude', ['--help'], { CCS_PROFILE_TYPE: 'default' });
|
||||
|
||||
expect(normalizeSpy).toHaveBeenCalledWith(undefined);
|
||||
});
|
||||
|
||||
it('execClaude normalizes shared plugin metadata using CLAUDE_CONFIG_DIR when provided', () => {
|
||||
const normalizeSpy = spyOn(
|
||||
SharedManager.prototype,
|
||||
'normalizeSharedPluginMetadataPaths'
|
||||
).mockImplementation(() => {});
|
||||
const instancePath = path.join(os.tmpdir(), 'ccs-shell-executor-instance');
|
||||
|
||||
execClaude('claude', ['--help'], {
|
||||
CCS_PROFILE_TYPE: 'settings',
|
||||
CLAUDE_CONFIG_DIR: instancePath,
|
||||
});
|
||||
|
||||
expect(normalizeSpy).toHaveBeenCalledWith(instancePath);
|
||||
});
|
||||
|
||||
it('headless executor spawn path strips CLAUDECODE before spawn', async () => {
|
||||
writeConfigWithAutoUpdatePreference(false);
|
||||
process.env.CLAUDECODE = 'nested';
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'bun:test';
|
||||
import {
|
||||
afterAll,
|
||||
afterEach,
|
||||
beforeAll,
|
||||
beforeEach,
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
mock,
|
||||
spyOn,
|
||||
} from 'bun:test';
|
||||
import express from 'express';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import type { Server } from 'http';
|
||||
import claudeExtensionRoutes from '../../../src/web-server/routes/claude-extension-routes';
|
||||
import SharedManager from '../../../src/management/shared-manager';
|
||||
import { createEmptyUnifiedConfig } from '../../../src/config/unified-config-types';
|
||||
import { saveUnifiedConfig } from '../../../src/config/unified-config-loader';
|
||||
|
||||
@@ -98,6 +109,8 @@ describe('web-server claude-extension-routes', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
|
||||
if (originalCcsHome !== undefined) process.env.CCS_HOME = originalCcsHome;
|
||||
else delete process.env.CCS_HOME;
|
||||
|
||||
@@ -143,6 +156,61 @@ describe('web-server claude-extension-routes', () => {
|
||||
expect(payload.sharedSettings.json).toContain('"env"');
|
||||
});
|
||||
|
||||
it('normalizes the effective profile CLAUDE_CONFIG_DIR for extension setup', async () => {
|
||||
const explicitConfigDir = path.join(tempHome, '.claude-profiles', 'glm');
|
||||
const glmSettingsPath = path.join(tempHome, '.ccs', 'glm.settings.json');
|
||||
const normalizeSpy = spyOn(
|
||||
SharedManager.prototype,
|
||||
'normalizeSharedPluginMetadataPaths'
|
||||
).mockImplementation(() => {});
|
||||
|
||||
fs.writeFileSync(
|
||||
glmSettingsPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
env: {
|
||||
ANTHROPIC_BASE_URL: 'https://api.example.test',
|
||||
ANTHROPIC_API_KEY: 'sk-ant-test-123456',
|
||||
ANTHROPIC_MODEL: 'claude-sonnet-4-5',
|
||||
CLAUDE_CONFIG_DIR: explicitConfigDir,
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
) + '\n'
|
||||
);
|
||||
|
||||
const config = createEmptyUnifiedConfig();
|
||||
config.profiles.glm = {
|
||||
type: 'api',
|
||||
settings: glmSettingsPath,
|
||||
};
|
||||
config.accounts.work = {
|
||||
created: '2026-03-15T00:00:00.000Z',
|
||||
last_used: null,
|
||||
context_mode: 'isolated',
|
||||
};
|
||||
config.default = 'work';
|
||||
config.continuity = {
|
||||
inherit_from_account: {
|
||||
glm: 'work',
|
||||
},
|
||||
};
|
||||
saveUnifiedConfig(config);
|
||||
|
||||
const response = await fetch(`${baseUrl}/api/claude-extension/setup?profile=glm&host=vscode`);
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const payload = (await response.json()) as {
|
||||
ideSettings: { json: string };
|
||||
};
|
||||
|
||||
expect(payload.ideSettings.json).toContain(explicitConfigDir);
|
||||
expect(normalizeSpy.mock.calls.some(([configDir]) => configDir === explicitConfigDir)).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it('renders Windsurf setup for default account resolution via CLAUDE_CONFIG_DIR', async () => {
|
||||
const response = await fetch(
|
||||
`${baseUrl}/api/claude-extension/setup?profile=default&host=windsurf`
|
||||
@@ -179,7 +247,11 @@ describe('web-server claude-extension-routes', () => {
|
||||
expect(createResponse.status).toBe(201);
|
||||
|
||||
const created = (await createResponse.json()) as {
|
||||
binding: { id: string; effectiveIdeSettingsPath: string; usesDefaultIdeSettingsPath: boolean };
|
||||
binding: {
|
||||
id: string;
|
||||
effectiveIdeSettingsPath: string;
|
||||
usesDefaultIdeSettingsPath: boolean;
|
||||
};
|
||||
};
|
||||
expect(created.binding.effectiveIdeSettingsPath).toBe(ideSettingsPath);
|
||||
expect(created.binding.usesDefaultIdeSettingsPath).toBe(false);
|
||||
@@ -418,7 +490,10 @@ describe('web-server claude-extension-routes', () => {
|
||||
);
|
||||
expect(applyResponse.status).toBe(200);
|
||||
|
||||
let ideSettings = JSON.parse(fs.readFileSync(ideSettingsPath, 'utf8')) as Record<string, unknown>;
|
||||
let ideSettings = JSON.parse(fs.readFileSync(ideSettingsPath, 'utf8')) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
const appliedEnv = ideSettings['claudeCode.environmentVariables'] as Array<{
|
||||
name: string;
|
||||
value: string;
|
||||
@@ -426,7 +501,9 @@ describe('web-server claude-extension-routes', () => {
|
||||
|
||||
expect(appliedEnv.some((entry) => entry.name === 'KEEP_ME' && entry.value === '1')).toBe(true);
|
||||
expect(
|
||||
appliedEnv.some((entry) => entry.name === 'ANTHROPIC_API_KEY' && entry.value === 'sk-ant-test-123456')
|
||||
appliedEnv.some(
|
||||
(entry) => entry.name === 'ANTHROPIC_API_KEY' && entry.value === 'sk-ant-test-123456'
|
||||
)
|
||||
).toBe(true);
|
||||
|
||||
const verifyAppliedResponse = await fetch(
|
||||
@@ -452,7 +529,9 @@ describe('web-server claude-extension-routes', () => {
|
||||
ideSettings = JSON.parse(fs.readFileSync(ideSettingsPath, 'utf8')) as Record<string, unknown>;
|
||||
expect(ideSettings['editor.tabSize']).toBe(2);
|
||||
expect(ideSettings['claudeCode.disableLoginPrompt']).toBeUndefined();
|
||||
expect(ideSettings['claudeCode.environmentVariables']).toEqual([{ name: 'KEEP_ME', value: '1' }]);
|
||||
expect(ideSettings['claudeCode.environmentVariables']).toEqual([
|
||||
{ name: 'KEEP_ME', value: '1' },
|
||||
]);
|
||||
|
||||
const verifyResetResponse = await fetch(
|
||||
`${baseUrl}/api/claude-extension/bindings/${created.binding.id}/verify`
|
||||
|
||||
Reference in new issue
Block a user