fix(profile): close km legacy kimi compatibility gaps

This commit is contained in:
Tam Nhu Tran committed 2026-02-19 13:02:56 +07:00
1 parent cf8070b5f0
commit 0bf00b23d9
9 files changed
+343 -66

No files matched your search

+10
View File
@@ -12,6 +12,16 @@ describe('provider-presets', () => {
expect(preset?.id).toBe('km');
});
it('resolves preset id with extra whitespace', () => {
const preset = getPresetById(' km ');
expect(preset?.id).toBe('km');
});
it('resolves uppercase legacy alias', () => {
const preset = getPresetById('KIMI');
expect(preset?.id).toBe('km');
});
it('treats legacy kimi alias as a valid preset id', () => {
expect(isValidPresetId('kimi')).toBe(true);
});
+135
View File
@@ -0,0 +1,135 @@
import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { loadMigrationCheckData, migrate } from '../../../src/config/migration-manager';
import { saveUnifiedConfig } from '../../../src/config/unified-config-loader';
import { createEmptyUnifiedConfig } from '../../../src/config/unified-config-types';
describe('migration-manager legacy kimi compatibility', () => {
let tempHome: string;
let ccsDir: string;
let originalCcsHome: string | undefined;
beforeEach(() => {
tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-migration-manager-test-'));
originalCcsHome = process.env.CCS_HOME;
process.env.CCS_HOME = tempHome;
ccsDir = path.join(tempHome, '.ccs');
fs.mkdirSync(ccsDir, { recursive: true });
});
afterEach(() => {
if (originalCcsHome !== undefined) {
process.env.CCS_HOME = originalCcsHome;
} else {
delete process.env.CCS_HOME;
}
if (fs.existsSync(tempHome)) {
fs.rmSync(tempHome, { recursive: true, force: true });
}
});
it('prefers explicit canonical km profile over legacy kimi when both exist', async () => {
const kmSettingsPath = path.join(ccsDir, 'km.settings.json');
const kimiSettingsPath = path.join(ccsDir, 'kimi.settings.json');
fs.writeFileSync(kmSettingsPath, JSON.stringify({ env: { ANTHROPIC_AUTH_TOKEN: 'sk-km' } }));
fs.writeFileSync(
kimiSettingsPath,
JSON.stringify({ env: { ANTHROPIC_AUTH_TOKEN: 'sk-kimi' } })
);
// Intentionally place legacy alias first to verify deterministic behavior.
fs.writeFileSync(
path.join(ccsDir, 'config.json'),
JSON.stringify(
{
profiles: {
kimi: kimiSettingsPath,
km: kmSettingsPath,
},
},
null,
2
)
);
const result = await migrate(true);
expect(result.success).toBe(true);
expect(
result.migratedFiles.some((entry) =>
entry.includes(`config.json.profiles.km → config.yaml.profiles.km (settings: ${kmSettingsPath})`)
)
).toBe(true);
expect(
result.migratedFiles.some((entry) =>
entry.includes(`(settings: ${kimiSettingsPath})`)
)
).toBe(false);
expect(
result.warnings.some((warning) =>
warning.includes(
'Skipped kimi: canonical profile "km" exists in config.json with different settings'
)
)
).toBe(true);
});
it('renames case-variant legacy Kimi profile key to km', async () => {
const kimiSettingsPath = path.join(ccsDir, 'kimi.settings.json');
fs.writeFileSync(
kimiSettingsPath,
JSON.stringify({ env: { ANTHROPIC_AUTH_TOKEN: 'sk-kimi-case-variant' } })
);
fs.writeFileSync(
path.join(ccsDir, 'config.json'),
JSON.stringify(
{
profiles: {
Kimi: kimiSettingsPath,
},
},
null,
2
)
);
const result = await migrate(true);
expect(result.success).toBe(true);
expect(
result.migratedFiles.some((entry) =>
entry.includes('config.json.profiles.Kimi → config.yaml.profiles.km')
)
).toBe(true);
});
it('treats legacy kimi profile as migrated when unified config already has km', () => {
const unifiedConfig = createEmptyUnifiedConfig();
unifiedConfig.profiles.km = {
type: 'api',
settings: '~/.ccs/km.settings.json',
};
saveUnifiedConfig(unifiedConfig);
fs.writeFileSync(
path.join(ccsDir, 'config.json'),
JSON.stringify(
{
profiles: {
kimi: '~/.ccs/kimi.settings.json',
},
},
null,
2
)
);
const checkData = loadMigrationCheckData();
expect(checkData.needsMigration).toBe(false);
});
});
+26 -1
View File
@@ -1,5 +1,9 @@
import { describe, expect, it } from 'bun:test';
import { getProfileLookupCandidates, isLegacyProfileAlias } from '../../../src/utils/profile-compat';
import {
getProfileLookupCandidates,
isLegacyProfileAlias,
resolveAliasToCanonical,
} from '../../../src/utils/profile-compat';
describe('profile-compat', () => {
describe('getProfileLookupCandidates', () => {
@@ -14,6 +18,15 @@ describe('profile-compat', () => {
it('normalizes uppercase input and still resolves aliases', () => {
expect(getProfileLookupCandidates('KM')).toEqual(['KM', 'km', 'kimi']);
});
it('handles surrounding whitespace', () => {
expect(getProfileLookupCandidates(' km ')).toEqual(['km', 'kimi']);
});
it('returns empty candidates for empty input', () => {
expect(getProfileLookupCandidates('')).toEqual([]);
expect(getProfileLookupCandidates(' ')).toEqual([]);
});
});
describe('isLegacyProfileAlias', () => {
@@ -30,4 +43,16 @@ describe('profile-compat', () => {
expect(isLegacyProfileAlias('glm', 'kimi')).toBe(false);
});
});
describe('resolveAliasToCanonical', () => {
it('maps legacy kimi alias to canonical km', () => {
expect(resolveAliasToCanonical('kimi')).toBe('km');
expect(resolveAliasToCanonical('KIMI')).toBe('km');
});
it('keeps canonical and non-aliased names', () => {
expect(resolveAliasToCanonical('km')).toBe('km');
expect(resolveAliasToCanonical('glm')).toBe('glm');
});
});
});
@@ -0,0 +1,49 @@
import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { checkSettingsFiles } from '../../../src/web-server/health/config-checks';
describe('web-server config-checks settings compatibility', () => {
let tempHome: string;
let ccsDir: string;
let originalCcsHome: string | undefined;
beforeEach(() => {
tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-config-checks-test-'));
originalCcsHome = process.env.CCS_HOME;
process.env.CCS_HOME = tempHome;
ccsDir = path.join(tempHome, '.ccs');
fs.mkdirSync(ccsDir, { recursive: true });
});
afterEach(() => {
if (originalCcsHome !== undefined) {
process.env.CCS_HOME = originalCcsHome;
} else {
delete process.env.CCS_HOME;
}
if (fs.existsSync(tempHome)) {
fs.rmSync(tempHome, { recursive: true, force: true });
}
});
it('reports km as configured when only legacy kimi.settings.json exists', () => {
fs.writeFileSync(
path.join(ccsDir, 'kimi.settings.json'),
JSON.stringify({
env: {
ANTHROPIC_AUTH_TOKEN: 'sk-live-kimi-compat',
},
})
);
const checks = checkSettingsFiles(ccsDir);
const kmCheck = checks.find((check) => check.id === 'settings-km');
expect(kmCheck).toBeDefined();
expect(kmCheck?.status).toBe('ok');
expect(kmCheck?.name).toBe('kimi.settings.json');
});
});