Files
ccs/tests/integration/codex-auth/legacy-fallback.test.ts
T

101 lines
4.3 KiB
TypeScript

/**
* Integration tests: legacy fallback when no profiles are registered.
*
* Verifies that with no codex-auth profiles and no CCS_CODEX_PROFILE env set,
* resolveActiveProfile returns null — allowing codex to fall back to ~/.codex
* (legacy mode). This guarantees zero behaviour change for users who never
* run `ccsx auth create`.
*
* Cases:
* - Empty registry → resolveActiveProfile returns null (legacy mode)
* - Missing registry file → returns null (no registry = legacy mode)
* - CCS_CODEX_PROFILE set but registry missing or unmatched → throws to avoid unsafe fallback
*/
import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
let tempDir: string;
let ccsHome: string;
const ORIG_CCS_HOME = process.env.CCS_HOME;
const ORIG_CCS_CODEX_PROFILE = process.env.CCS_CODEX_PROFILE;
beforeEach(() => {
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-legacy-fallback-'));
ccsHome = path.join(tempDir, 'ccs');
fs.mkdirSync(path.join(ccsHome, '.ccs'), { recursive: true });
process.env.CCS_HOME = ccsHome;
delete process.env.CCS_CODEX_PROFILE;
});
afterEach(() => {
if (ORIG_CCS_HOME === undefined) delete process.env.CCS_HOME;
else process.env.CCS_HOME = ORIG_CCS_HOME;
if (ORIG_CCS_CODEX_PROFILE === undefined) delete process.env.CCS_CODEX_PROFILE;
else process.env.CCS_CODEX_PROFILE = ORIG_CCS_CODEX_PROFILE;
fs.rmSync(tempDir, { recursive: true, force: true });
});
// ─────────────────────────────────────────────────────────────────────────────
describe('legacy fallback — no registry file', () => {
it('returns null when registry file does not exist (CODEX_HOME stays unset)', async () => {
// CCS_HOME points to empty temp dir — no codex-profiles.yaml created
const registryPath = path.join(ccsHome, '.ccs', 'codex-profiles.yaml');
expect(fs.existsSync(registryPath)).toBe(false);
const { resolveActiveProfile } = await import('../../../src/codex-auth/resolve-active-profile');
const result = resolveActiveProfile({});
expect(result).toBeNull();
// When null: caller (codex-runtime.ts) leaves CODEX_HOME unset → Codex uses ~/.codex
});
});
describe('legacy fallback — empty registry', () => {
it('returns null when registry exists but has no profiles and no default', async () => {
const { CodexProfileRegistry } = await import('../../../src/codex-auth/codex-profile-registry');
// Touch registry by constructing (which cleans orphan tmps but doesn't write)
// Write an empty registry manually
const registryPath = path.join(ccsHome, '.ccs', 'codex-profiles.yaml');
fs.mkdirSync(path.dirname(registryPath), { recursive: true });
fs.writeFileSync(registryPath, 'version: "1.0"\ndefault: null\nprofiles: {}\n', {
mode: 0o600,
});
const { resolveActiveProfile } = await import('../../../src/codex-auth/resolve-active-profile');
const result = resolveActiveProfile({});
expect(result).toBeNull();
// Registry exists but no profiles → legacy mode
void new CodexProfileRegistry(); // verify registry reads cleanly
});
});
describe('legacy fallback — CCS_CODEX_PROFILE set but no matching profile', () => {
it('throws when env points to non-existent profile', async () => {
// Create registry with no profiles
const registryPath = path.join(ccsHome, '.ccs', 'codex-profiles.yaml');
fs.mkdirSync(path.dirname(registryPath), { recursive: true });
fs.writeFileSync(registryPath, 'version: "1.0"\ndefault: null\nprofiles: {}\n', {
mode: 0o600,
});
const { resolveActiveProfile } = await import('../../../src/codex-auth/resolve-active-profile');
expect(() => resolveActiveProfile({ CCS_CODEX_PROFILE: 'ghost-profile' })).toThrow(
/ghost-profile/
);
});
it('throws when env is set but registry file is missing', async () => {
const registryPath = path.join(ccsHome, '.ccs', 'codex-profiles.yaml');
expect(fs.existsSync(registryPath)).toBe(false);
const { resolveActiveProfile } = await import('../../../src/codex-auth/resolve-active-profile');
expect(() => resolveActiveProfile({ CCS_CODEX_PROFILE: 'ghost-profile' })).toThrow(
/does not exist/
);
});
});