fix: clean up deleted provider state correctly (#696)

This commit is contained in:
Lingxuan Zuo
2026-03-27 23:32:56 +08:00
committed by GitHub
Unverified
parent 07f3c310b5
commit 9b56d80d22
8 changed files with 510 additions and 32 deletions

View File

@@ -13,6 +13,7 @@ const mocks = vi.hoisted(() => ({
getProviderConfig: vi.fn(),
getProviderDefaultModel: vi.fn(),
removeProviderFromOpenClaw: vi.fn(),
removeProviderKeyFromOpenClaw: vi.fn(),
saveOAuthTokenToOpenClaw: vi.fn(),
saveProviderKeyToOpenClaw: vi.fn(),
setOpenClawDefaultModel: vi.fn(),
@@ -46,6 +47,7 @@ vi.mock('@electron/utils/provider-registry', () => ({
vi.mock('@electron/utils/openclaw-auth', () => ({
removeProviderFromOpenClaw: mocks.removeProviderFromOpenClaw,
removeProviderKeyFromOpenClaw: mocks.removeProviderKeyFromOpenClaw,
saveOAuthTokenToOpenClaw: mocks.saveOAuthTokenToOpenClaw,
saveProviderKeyToOpenClaw: mocks.saveProviderKeyToOpenClaw,
setOpenClawDefaultModel: mocks.setOpenClawDefaultModel,
@@ -71,6 +73,7 @@ vi.mock('@electron/utils/logger', () => ({
import {
syncAgentModelOverrideToRuntime,
syncDefaultProviderToRuntime,
syncDeletedProviderApiKeyToRuntime,
syncDeletedProviderToRuntime,
syncSavedProviderToRuntime,
} from '@electron/services/providers/provider-runtime-sync';
@@ -116,6 +119,7 @@ describe('provider-runtime-sync refresh strategy', () => {
mocks.setOpenClawDefaultModelWithOverride.mockResolvedValue(undefined);
mocks.saveProviderKeyToOpenClaw.mockResolvedValue(undefined);
mocks.removeProviderFromOpenClaw.mockResolvedValue(undefined);
mocks.removeProviderKeyFromOpenClaw.mockResolvedValue(undefined);
mocks.updateAgentModelProvider.mockResolvedValue(undefined);
mocks.updateSingleAgentModelProvider.mockResolvedValue(undefined);
mocks.listAgentsSnapshot.mockResolvedValue({ agents: [] });
@@ -137,6 +141,34 @@ describe('provider-runtime-sync refresh strategy', () => {
expect(gateway.debouncedReload).not.toHaveBeenCalled();
});
it('removes both runtime and stored account keys when deleting a custom provider', async () => {
const gateway = createGateway('running');
const customProvider = createProvider({
id: 'moonshot-cn',
type: 'custom',
baseUrl: 'https://api.moonshot.cn/v1',
});
await syncDeletedProviderToRuntime(customProvider, 'moonshot-cn', gateway as GatewayManager);
expect(mocks.removeProviderFromOpenClaw).toHaveBeenCalledWith('custom-moonshot');
expect(mocks.removeProviderFromOpenClaw).toHaveBeenCalledWith('moonshot-cn');
expect(mocks.removeProviderFromOpenClaw).toHaveBeenCalledTimes(2);
expect(gateway.debouncedRestart).toHaveBeenCalledTimes(1);
});
it('only clears the api-key profile when deleting a provider api key', async () => {
const openaiProvider = createProvider({
id: 'openai-personal',
type: 'openai',
});
await syncDeletedProviderApiKeyToRuntime(openaiProvider, 'openai-personal');
expect(mocks.removeProviderKeyFromOpenClaw).toHaveBeenCalledWith('openai');
expect(mocks.removeProviderFromOpenClaw).not.toHaveBeenCalled();
});
it('uses debouncedReload after switching default provider when gateway is running', async () => {
const gateway = createGateway('running');
await syncDefaultProviderToRuntime('moonshot', gateway as GatewayManager);
@@ -153,6 +185,34 @@ describe('provider-runtime-sync refresh strategy', () => {
expect(gateway.debouncedRestart).not.toHaveBeenCalled();
});
it('uses gpt-5.4 as the browser OAuth default model for OpenAI', async () => {
mocks.getProvider.mockResolvedValue(
createProvider({
id: 'openai-personal',
type: 'openai',
model: undefined,
}),
);
mocks.getProviderAccount.mockResolvedValue({ authMode: 'oauth_browser' });
mocks.getProviderSecret.mockResolvedValue({
type: 'oauth',
accessToken: 'access',
refreshToken: 'refresh',
expiresAt: 123,
email: 'user@example.com',
subject: 'project-1',
});
const gateway = createGateway('running');
await syncDefaultProviderToRuntime('openai-personal', gateway as GatewayManager);
expect(mocks.setOpenClawDefaultModel).toHaveBeenCalledWith(
'openai-codex',
'openai-codex/gpt-5.4',
expect.any(Array),
);
});
it('syncs a targeted agent model override to runtime provider registry', async () => {
mocks.getAllProviders.mockResolvedValue([
createProvider({