mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-12 20:15:40 +00:00
fix: enforce tier lock during quota preflight (#1550)
Tier lock now fails closed in quota preflight when no healthy locked-tier account is available, instead of falling through cross-tier.
This commit is contained in:
1 parent
06e83091be
commit
361328c788
2 files changed
+51
-3
No files matched your search
@@ -658,8 +658,8 @@ export async function preflightCheck(provider: CLIProxyProvider): Promise<Prefli
|
|||||||
// match the locked tier. If it doesn't, route to a healthy account in the
|
// match the locked tier. If it doesn't, route to a healthy account in the
|
||||||
// locked tier instead. Locks are per-provider: locking "agy" to "ultra"
|
// locked tier instead. Locks are per-provider: locking "agy" to "ultra"
|
||||||
// does NOT constrain "claude", "codex", "gemini", or "ghcp".
|
// does NOT constrain "claude", "codex", "gemini", or "ghcp".
|
||||||
// Graceful degradation: if no locked-tier account is available, fall through
|
// This is intentionally strict: if no locked-tier account is available, do
|
||||||
// to the default (don't block the request entirely).
|
// not fail open to a cross-tier default.
|
||||||
const tierLock = getTierLockForProvider(quotaConfig.manual, provider);
|
const tierLock = getTierLockForProvider(quotaConfig.manual, provider);
|
||||||
if (tierLock !== null && (defaultAccount.tier || 'unknown') !== tierLock) {
|
if (tierLock !== null && (defaultAccount.tier || 'unknown') !== tierLock) {
|
||||||
const lockedTierAccount = await findHealthyAccount(provider, []);
|
const lockedTierAccount = await findHealthyAccount(provider, []);
|
||||||
@@ -673,7 +673,12 @@ export async function preflightCheck(provider: CLIProxyProvider): Promise<Prefli
|
|||||||
reason: `Tier lock: selected ${tierLock} account`,
|
reason: `Tier lock: selected ${tierLock} account`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// No locked-tier account available — fall through and use default
|
|
||||||
|
return {
|
||||||
|
proceed: false,
|
||||||
|
accountId: '',
|
||||||
|
reason: `Tier lock: no healthy ${tierLock} account available`,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if default is paused
|
// Check if default is paused
|
||||||
|
|||||||
@@ -352,6 +352,49 @@ describe('quota-manager findHealthyAccount — tier_lock', () => {
|
|||||||
expect(result).toBeNull();
|
expect(result).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// preflightCheck must enforce the same strict tier lock as findHealthyAccount
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
it('preflightCheck blocks a cross-tier default when no healthy locked-tier account exists', async () => {
|
||||||
|
writeMinimalConfig(tempHome, { agy: 'pro' });
|
||||||
|
_mockAccounts = [ULTRA_ACCOUNT, PRO_ACCOUNT];
|
||||||
|
|
||||||
|
const uid = `preflight-lock-pro-exhausted-${Date.now()}-${Math.random()}`;
|
||||||
|
const { preflightCheck, setCachedQuota } = await import(
|
||||||
|
`../../../src/cliproxy/quota/quota-manager?${uid}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Default is ultra, but agy is locked to pro. The only pro account is
|
||||||
|
// exhausted, so preflight must not fail open to the healthy ultra default.
|
||||||
|
setCachedQuota('agy', ULTRA_ACCOUNT.id, HEALTHY_QUOTA as never);
|
||||||
|
setCachedQuota('agy', PRO_ACCOUNT.id, EXHAUSTED_QUOTA as never);
|
||||||
|
|
||||||
|
const result = await preflightCheck('agy');
|
||||||
|
expect(result.proceed).toBe(false);
|
||||||
|
expect(result.accountId).not.toBe(ULTRA_ACCOUNT.id);
|
||||||
|
expect(result.reason).toBe('Tier lock: no healthy pro account available');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preflightCheck switches a cross-tier default to a healthy locked-tier account', async () => {
|
||||||
|
writeMinimalConfig(tempHome, { agy: 'pro' });
|
||||||
|
_mockAccounts = [ULTRA_ACCOUNT, PRO_ACCOUNT];
|
||||||
|
|
||||||
|
const uid = `preflight-lock-pro-healthy-${Date.now()}-${Math.random()}`;
|
||||||
|
const { preflightCheck, setCachedQuota } = await import(
|
||||||
|
`../../../src/cliproxy/quota/quota-manager?${uid}`
|
||||||
|
);
|
||||||
|
|
||||||
|
setCachedQuota('agy', ULTRA_ACCOUNT.id, HEALTHY_QUOTA as never);
|
||||||
|
setCachedQuota('agy', PRO_ACCOUNT.id, HEALTHY_QUOTA as never);
|
||||||
|
|
||||||
|
const result = await preflightCheck('agy');
|
||||||
|
expect(result.proceed).toBe(true);
|
||||||
|
expect(result.accountId).toBe(PRO_ACCOUNT.id);
|
||||||
|
expect(result.switchedFrom).toBe(ULTRA_ACCOUNT.id);
|
||||||
|
expect(result.reason).toBe('Tier lock: selected pro account');
|
||||||
|
});
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Baseline: no tier_lock — any available healthy account is returned.
|
// Baseline: no tier_lock — any available healthy account is returned.
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in new issue
Block a user