test: isolate shared-state test suites for CI stability

- add dependency injection to export-command, binary-manager,
  codex-plan-compatibility, config-command, and usage-syncer
- override process.exit in api-export test to prevent silent
  termination in Bun's parallel test runner
- harden test-environment bootstrap with process.env isolation
- fix auth middleware to avoid config upgrade during checks
This commit is contained in:
Tam Nhu Tran committed 2026-03-25 16:41:41 -04:00
1 parent 58891d368d
commit 3af554275e
24 files changed
+764 -614

No files matched your search

@@ -1,10 +1,6 @@
import { afterEach, describe, expect, it, mock } from 'bun:test';
import { describe, expect, it } from 'bun:test';
describe('installCliproxyVersion', () => {
afterEach(() => {
mock.restore();
});
it('attempts to stop the proxy even when there is no tracked running session', async () => {
const calls = {
stopProxy: 0,
@@ -13,83 +9,33 @@ describe('installCliproxyVersion', () => {
ensureBinary: 0,
};
mock.module('../../../src/utils/ui', () => ({
info: (message: string) => message,
warn: (message: string) => message,
}));
mock.module('../../../src/cliproxy/config-generator', () => ({
getBinDir: () => '/tmp/ccs-bin',
CLIPROXY_DEFAULT_PORT: 8317,
}));
mock.module('../../../src/cliproxy/platform-detector', () => ({
DEFAULT_BACKEND: 'plus',
CLIPROXY_MAX_STABLE_VERSION: '9.9.999-0',
BACKEND_CONFIG: {
plus: {
fallbackVersion: '6.6.80',
repo: 'router-for-me/CLIProxyAPIPlus',
},
original: {
fallbackVersion: '0.0.0',
repo: 'router-for-me/CLIProxyAPI',
},
},
}));
mock.module('../../../src/cliproxy/services/proxy-lifecycle-service', () => ({
stopProxy: async () => {
calls.stopProxy += 1;
return { stopped: false, error: 'No active CLIProxy session found' };
},
}));
mock.module('../../../src/utils/port-utils', () => ({
waitForPortFree: async () => {
calls.waitForPortFree += 1;
return true;
},
}));
mock.module('../../../src/config/unified-config-loader', () => ({
loadOrCreateUnifiedConfig: () => ({
cliproxy: { backend: 'plus' },
}),
}));
mock.module('../../../src/cliproxy/binary', () => ({
checkForUpdates: async () => ({
hasUpdate: false,
currentVersion: '6.6.80',
latestVersion: '6.6.80',
fromCache: false,
checkedAt: Date.now(),
}),
deleteBinary: () => {
calls.deleteBinary += 1;
},
getBinaryPath: () => '/tmp/ccs-bin/plus/cliproxy',
isBinaryInstalled: () => false,
getBinaryInfo: async () => null,
getPinnedVersion: () => null,
savePinnedVersion: () => {},
clearPinnedVersion: () => {},
isVersionPinned: () => false,
getVersionPinPath: () => '/tmp/ccs-bin/plus/.version-pin',
readInstalledVersion: () => '6.6.80',
ensureBinary: async () => {
calls.ensureBinary += 1;
return '/tmp/ccs-bin/plus/cliproxy';
},
migrateVersionPin: () => {},
}));
const binaryManager = await import(
`../../../src/cliproxy/binary-manager?binary-manager-install=${Date.now()}`
);
await binaryManager.installCliproxyVersion('6.7.1', false, 'plus');
await binaryManager.installCliproxyVersion('6.7.1', false, 'plus', {
createManager: () => ({
isBinaryInstalled: () => false,
deleteBinary: () => {
calls.deleteBinary += 1;
},
ensureBinary: async () => {
calls.ensureBinary += 1;
return '/tmp/ccs-bin/plus/cliproxy';
},
}),
stopProxyFn: async () => {
calls.stopProxy += 1;
return { stopped: false, error: 'No active CLIProxy session found' };
},
waitForPortFreeFn: async () => {
calls.waitForPortFree += 1;
return true;
},
formatInfo: (message: string) => message,
formatWarn: (message: string) => message,
getInstalledVersion: () => '6.6.80',
});
expect(calls.stopProxy).toBe(1);
expect(calls.waitForPortFree).toBe(0);
@@ -42,38 +42,36 @@ async function importCompatibilityModule(cacheTag: string) {
return import(`../../../src/cliproxy/codex-plan-compatibility?${cacheTag}=${Date.now()}`);
}
const identity = (message: string) => message;
describe('codex plan compatibility reconcile', () => {
it('repairs stale paid-only Codex settings for free-plan accounts before launch', async () => {
const { tmpDir, settingsPath } = createCodexSettingsFixture('gpt-5.3-codex-spark');
mock.module('../../../src/cliproxy/account-manager', () => ({
getDefaultAccount: () => ({ id: 'free@example.com' }),
}));
mock.module('../../../src/cliproxy/quota-fetcher-codex', () => ({
fetchCodexQuota: async () => ({
success: true,
windows: [],
coreUsage: { fiveHour: null, weekly: null },
planType: 'free',
lastUpdated: Date.now(),
accountId: 'free@example.com',
}),
}));
mock.module('../../../src/utils/ui', () => ({
info: (message: string) => message,
warn: (message: string) => message,
}));
const errorSpy = spyOn(console, 'error').mockImplementation(() => {});
try {
const { reconcileCodexModelForActivePlan } = await importCompatibilityModule('free-plan');
await reconcileCodexModelForActivePlan({
settingsPath,
currentModel: 'gpt-5.3-codex',
verbose: false,
});
await reconcileCodexModelForActivePlan(
{
settingsPath,
currentModel: 'gpt-5.3-codex',
verbose: false,
},
{
getDefaultAccount: () => ({ id: 'free@example.com' }) as never,
fetchCodexQuota: async () => ({
success: true,
windows: [],
coreUsage: { fiveHour: null, weekly: null },
planType: 'free',
lastUpdated: Date.now(),
accountId: 'free@example.com',
}),
formatInfo: identity,
formatWarn: identity,
}
);
const repaired = JSON.parse(fs.readFileSync(settingsPath, 'utf-8')) as {
env: Record<string, string>;
@@ -92,31 +90,27 @@ describe('codex plan compatibility reconcile', () => {
it('warns and leaves settings untouched when no default Codex account is available', async () => {
const { tmpDir, settingsPath } = createCodexSettingsFixture();
mock.module('../../../src/cliproxy/account-manager', () => ({
getDefaultAccount: () => null,
}));
mock.module('../../../src/cliproxy/quota-fetcher-codex', () => ({
fetchCodexQuota: async () => {
throw new Error('should not fetch quota without a default account');
},
}));
mock.module('../../../src/utils/ui', () => ({
info: (message: string) => message,
warn: (message: string) => message,
}));
const errorSpy = spyOn(console, 'error').mockImplementation(() => {});
try {
const { reconcileCodexModelForActivePlan } =
await importCompatibilityModule('missing-default-account');
await reconcileCodexModelForActivePlan({
settingsPath,
currentModel: 'gpt-5.3-codex',
verbose: false,
});
await reconcileCodexModelForActivePlan(
{
settingsPath,
currentModel: 'gpt-5.3-codex',
verbose: false,
},
{
getDefaultAccount: () => null,
fetchCodexQuota: async () => {
throw new Error('should not fetch quota without a default account');
},
formatInfo: identity,
formatWarn: identity,
}
);
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8')) as {
env: Record<string, string>;
@@ -133,35 +127,31 @@ describe('codex plan compatibility reconcile', () => {
it('keeps paid-plan Codex settings unchanged for plus and team accounts', async () => {
for (const planType of ['plus', 'team'] as const) {
const { tmpDir, settingsPath } = createCodexSettingsFixture();
mock.module('../../../src/cliproxy/account-manager', () => ({
getDefaultAccount: () => ({ id: `${planType}@example.com` }),
}));
mock.module('../../../src/cliproxy/quota-fetcher-codex', () => ({
fetchCodexQuota: async () => ({
success: true,
windows: [],
coreUsage: { fiveHour: null, weekly: null },
planType,
lastUpdated: Date.now(),
accountId: `${planType}@example.com`,
}),
}));
mock.module('../../../src/utils/ui', () => ({
info: (message: string) => message,
warn: (message: string) => message,
}));
const errorSpy = spyOn(console, 'error').mockImplementation(() => {});
try {
const { reconcileCodexModelForActivePlan } = await importCompatibilityModule(planType);
await reconcileCodexModelForActivePlan({
settingsPath,
currentModel: 'gpt-5.3-codex',
verbose: false,
});
await reconcileCodexModelForActivePlan(
{
settingsPath,
currentModel: 'gpt-5.3-codex',
verbose: false,
},
{
getDefaultAccount: () => ({ id: `${planType}@example.com` }) as never,
fetchCodexQuota: async () => ({
success: true,
windows: [],
coreUsage: { fiveHour: null, weekly: null },
planType,
lastUpdated: Date.now(),
accountId: `${planType}@example.com`,
}),
formatInfo: identity,
formatWarn: identity,
}
);
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8')) as {
env: Record<string, string>;
@@ -177,36 +167,32 @@ describe('codex plan compatibility reconcile', () => {
it('warns and keeps settings unchanged when Codex plan verification fails', async () => {
const { tmpDir, settingsPath } = createCodexSettingsFixture();
mock.module('../../../src/cliproxy/account-manager', () => ({
getDefaultAccount: () => ({ id: 'unknown@example.com' }),
}));
mock.module('../../../src/cliproxy/quota-fetcher-codex', () => ({
fetchCodexQuota: async () => ({
success: false,
windows: [],
coreUsage: { fiveHour: null, weekly: null },
planType: null,
lastUpdated: Date.now(),
accountId: 'unknown@example.com',
error: 'network timeout',
}),
}));
mock.module('../../../src/utils/ui', () => ({
info: (message: string) => message,
warn: (message: string) => message,
}));
const errorSpy = spyOn(console, 'error').mockImplementation(() => {});
try {
const { reconcileCodexModelForActivePlan } = await importCompatibilityModule('unknown-plan');
await reconcileCodexModelForActivePlan({
settingsPath,
currentModel: 'gpt-5.3-codex',
verbose: false,
});
await reconcileCodexModelForActivePlan(
{
settingsPath,
currentModel: 'gpt-5.3-codex',
verbose: false,
},
{
getDefaultAccount: () => ({ id: 'unknown@example.com' }) as never,
fetchCodexQuota: async () => ({
success: false,
windows: [],
coreUsage: { fiveHour: null, weekly: null },
planType: null,
lastUpdated: Date.now(),
accountId: 'unknown@example.com',
error: 'network timeout',
}),
formatInfo: identity,
formatWarn: identity,
}
);
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8')) as {
env: Record<string, string>;
@@ -222,36 +208,32 @@ describe('codex plan compatibility reconcile', () => {
it('warns and keeps settings unchanged when quota succeeds without a plan type', async () => {
const { tmpDir, settingsPath } = createCodexSettingsFixture();
mock.module('../../../src/cliproxy/account-manager', () => ({
getDefaultAccount: () => ({ id: 'missing-plan@example.com' }),
}));
mock.module('../../../src/cliproxy/quota-fetcher-codex', () => ({
fetchCodexQuota: async () => ({
success: true,
windows: [],
coreUsage: { fiveHour: null, weekly: null },
planType: null,
lastUpdated: Date.now(),
accountId: 'missing-plan@example.com',
}),
}));
mock.module('../../../src/utils/ui', () => ({
info: (message: string) => message,
warn: (message: string) => message,
}));
const errorSpy = spyOn(console, 'error').mockImplementation(() => {});
try {
const { reconcileCodexModelForActivePlan } =
await importCompatibilityModule('missing-plan-type');
await reconcileCodexModelForActivePlan({
settingsPath,
currentModel: 'gpt-5.3-codex',
verbose: false,
});
await reconcileCodexModelForActivePlan(
{
settingsPath,
currentModel: 'gpt-5.3-codex',
verbose: false,
},
{
getDefaultAccount: () => ({ id: 'missing-plan@example.com' }) as never,
fetchCodexQuota: async () => ({
success: true,
windows: [],
coreUsage: { fiveHour: null, weekly: null },
planType: null,
lastUpdated: Date.now(),
accountId: 'missing-plan@example.com',
}),
formatInfo: identity,
formatWarn: identity,
}
);
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8')) as {
env: Record<string, string>;
@@ -7,11 +7,13 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { afterEach, beforeEach, describe, expect, it, mock } from 'bun:test';
import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
import { getCapturedFetchRequests, mockFetch, restoreFetch } from '../../mocks';
describe('Gemini CLI Quota Fetcher', () => {
let tempHome: string;
let originalCcsHome: string | undefined;
let originalCcsDir: string | undefined;
let originalGeminiClientId: string | undefined;
let originalGeminiClientSecret: string | undefined;
let moduleVersion = 0;
@@ -30,31 +32,16 @@ describe('Gemini CLI Quota Fetcher', () => {
beforeEach(async () => {
moduleVersion += 1;
mock.restore();
tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-gemini-refresh-'));
originalCcsHome = process.env.CCS_HOME;
originalCcsDir = process.env.CCS_DIR;
originalGeminiClientId = process.env.CCS_GEMINI_OAUTH_CLIENT_ID;
originalGeminiClientSecret = process.env.CCS_GEMINI_OAUTH_CLIENT_SECRET;
process.env.CCS_HOME = tempHome;
delete process.env.CCS_GEMINI_OAUTH_CLIENT_ID;
delete process.env.CCS_GEMINI_OAUTH_CLIENT_SECRET;
const authDir = path.join(tempHome, '.ccs', 'cliproxy', 'auth');
const actualConfigGenerator = await import(
`../../../src/cliproxy/config-generator?gemini-config-generator-actual=${moduleVersion}`
);
const actualAccountManager = await import(
`../../../src/cliproxy/account-manager?gemini-account-manager-actual=${moduleVersion}`
);
mock.module('../../../src/cliproxy/config-generator', () => ({
...actualConfigGenerator,
getProviderAuthDir: () => authDir,
}));
mock.module('../../../src/cliproxy/account-manager', () => ({
...actualAccountManager,
getDefaultAccount: () => null,
getProviderAccounts: () => [],
}));
delete process.env.CCS_DIR;
const configGenerator = await import(
`../../../src/cliproxy/config-generator?gemini-config-generator=${moduleVersion}`
@@ -69,10 +56,21 @@ describe('Gemini CLI Quota Fetcher', () => {
});
afterEach(() => {
mock.restore();
restoreFetch();
fs.rmSync(tempHome, { recursive: true, force: true });
if (originalCcsHome === undefined) {
delete process.env.CCS_HOME;
} else {
process.env.CCS_HOME = originalCcsHome;
}
if (originalCcsDir === undefined) {
delete process.env.CCS_DIR;
} else {
process.env.CCS_DIR = originalCcsDir;
}
if (originalGeminiClientId === undefined) {
delete process.env.CCS_GEMINI_OAUTH_CLIENT_ID;
} else {