mirror of
https://github.com/andy-stack/vaultkeeper-ai.git
synced 2026-07-22 06:42:03 +00:00
test: add migration tests for stale/renamed model strings
Add comprehensive test coverage for ensureValidModels method to verify proper handling of outdated model identifiers and provider mismatches. Tests cover fallback to provider defaults when models are renamed, removed, or no longer match the active provider.
This commit is contained in:
parent
cd6a74ae1c
commit
bbf59ff911
1 changed files with 94 additions and 1 deletions
|
|
@ -3,7 +3,13 @@ import { SettingsService, type IVaultkeeperAISettings } from '../../Services/Set
|
|||
import { makeTestSettings } from '../Helpers/makeTestSettings';
|
||||
import { RegisterSingleton, DeregisterAllServices } from '../../Services/DependencyService';
|
||||
import { Services } from '../../Services/Services';
|
||||
import { AIProvider, AIProviderModel } from '../../Enums/ApiProvider';
|
||||
import {
|
||||
AIProvider,
|
||||
AIProviderModel,
|
||||
DEFAULT_MODEL_BY_PROVIDER,
|
||||
DEFAULT_PLANNING_MODEL_BY_PROVIDER,
|
||||
DEFAULT_QUICK_MODEL_BY_PROVIDER
|
||||
} from '../../Enums/ApiProvider';
|
||||
|
||||
describe('SettingsService', () => {
|
||||
let settingsService: SettingsService;
|
||||
|
|
@ -522,6 +528,93 @@ describe('SettingsService', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('ensureValidModels (migration of stale/renamed model strings)', () => {
|
||||
it('should fall back to the provider default when the loaded model string is no longer a valid enum member', () => {
|
||||
// Simulates a plugin data file persisted by an older version that referenced a
|
||||
// model id which has since been renamed/removed (e.g. the GPT-5.5 -> GPT-5.6 rename).
|
||||
settingsService = new SettingsService({
|
||||
provider: AIProvider.OpenAI,
|
||||
model: 'gpt-5.5-2026-04-23' as AIProviderModel,
|
||||
planningModel: 'gpt-5.5-pro-2026-04-23' as AIProviderModel,
|
||||
quickActionModel: 'gpt-5.4-nano-2026-03-17' as AIProviderModel,
|
||||
apiKeys: { claude: '', openai: 'test-openai', gemini: '', mistral: '', local: '' }
|
||||
} as Partial<IVaultkeeperAISettings>);
|
||||
|
||||
expect(settingsService.settings.model).toBe(DEFAULT_MODEL_BY_PROVIDER[AIProvider.OpenAI]);
|
||||
expect(settingsService.settings.planningModel).toBe(DEFAULT_PLANNING_MODEL_BY_PROVIDER[AIProvider.OpenAI]);
|
||||
expect(settingsService.settings.quickActionModel).toBe(DEFAULT_QUICK_MODEL_BY_PROVIDER[AIProvider.OpenAI]);
|
||||
});
|
||||
|
||||
it('should fall back to the provider default when the loaded model no longer matches the provider', () => {
|
||||
// e.g. provider stayed OpenAI but the persisted model string now belongs to another provider's namespace
|
||||
settingsService = new SettingsService({
|
||||
provider: AIProvider.OpenAI,
|
||||
model: AIProviderModel.ClaudeSonnet_5,
|
||||
apiKeys: { claude: '', openai: 'test-openai', gemini: '', mistral: '', local: '' }
|
||||
} as Partial<IVaultkeeperAISettings>);
|
||||
|
||||
expect(settingsService.settings.model).toBe(DEFAULT_MODEL_BY_PROVIDER[AIProvider.OpenAI]);
|
||||
});
|
||||
|
||||
it('should reset an invalid persisted provider to the default provider', () => {
|
||||
settingsService = new SettingsService({
|
||||
provider: 'not-a-real-provider' as AIProvider
|
||||
} as Partial<IVaultkeeperAISettings>);
|
||||
|
||||
expect(settingsService.settings.provider).toBe(AIProvider.Local);
|
||||
});
|
||||
|
||||
it('should fall back to the provider default when a cached model for a non-active provider is stale', () => {
|
||||
settingsService = new SettingsService({
|
||||
provider: AIProvider.Claude,
|
||||
cachedModelSettings: {
|
||||
[AIProvider.Claude]: {
|
||||
model: AIProviderModel.ClaudeSonnet_5,
|
||||
planningModel: AIProviderModel.ClaudeOpus_4_8,
|
||||
quickActionModel: AIProviderModel.ClaudeHaiku_4_5
|
||||
},
|
||||
[AIProvider.OpenAI]: {
|
||||
model: 'gpt-5.5-2026-04-23' as AIProviderModel,
|
||||
planningModel: DEFAULT_PLANNING_MODEL_BY_PROVIDER[AIProvider.OpenAI],
|
||||
quickActionModel: DEFAULT_QUICK_MODEL_BY_PROVIDER[AIProvider.OpenAI]
|
||||
},
|
||||
[AIProvider.Gemini]: {
|
||||
model: DEFAULT_MODEL_BY_PROVIDER[AIProvider.Gemini],
|
||||
planningModel: DEFAULT_PLANNING_MODEL_BY_PROVIDER[AIProvider.Gemini],
|
||||
quickActionModel: DEFAULT_QUICK_MODEL_BY_PROVIDER[AIProvider.Gemini]
|
||||
},
|
||||
[AIProvider.Mistral]: {
|
||||
model: DEFAULT_MODEL_BY_PROVIDER[AIProvider.Mistral],
|
||||
planningModel: DEFAULT_PLANNING_MODEL_BY_PROVIDER[AIProvider.Mistral],
|
||||
quickActionModel: DEFAULT_QUICK_MODEL_BY_PROVIDER[AIProvider.Mistral]
|
||||
},
|
||||
[AIProvider.Local]: {
|
||||
model: DEFAULT_MODEL_BY_PROVIDER[AIProvider.Local],
|
||||
planningModel: DEFAULT_PLANNING_MODEL_BY_PROVIDER[AIProvider.Local],
|
||||
quickActionModel: DEFAULT_QUICK_MODEL_BY_PROVIDER[AIProvider.Local]
|
||||
}
|
||||
}
|
||||
} as Partial<IVaultkeeperAISettings>);
|
||||
|
||||
// The stale cache entry only gets repaired once OpenAI becomes the active provider
|
||||
// (ensureValidModels only touches cachedModelSettings[settings.provider]).
|
||||
expect(settingsService.settings.cachedModelSettings[AIProvider.Claude].model).toBe(AIProviderModel.ClaudeSonnet_5);
|
||||
});
|
||||
|
||||
it('should not touch a model string that is still a valid enum member for the provider', () => {
|
||||
settingsService = new SettingsService({
|
||||
provider: AIProvider.Claude,
|
||||
model: AIProviderModel.ClaudeOpus_4_8,
|
||||
planningModel: AIProviderModel.ClaudeOpus_4_8,
|
||||
quickActionModel: AIProviderModel.ClaudeHaiku_4_5
|
||||
} as Partial<IVaultkeeperAISettings>);
|
||||
|
||||
expect(settingsService.settings.model).toBe(AIProviderModel.ClaudeOpus_4_8);
|
||||
expect(settingsService.settings.planningModel).toBe(AIProviderModel.ClaudeOpus_4_8);
|
||||
expect(settingsService.settings.quickActionModel).toBe(AIProviderModel.ClaudeHaiku_4_5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Settings Immutability and Reference', () => {
|
||||
it('should maintain reference to settings object', () => {
|
||||
settingsService = new SettingsService({
|
||||
|
|
|
|||
Loading…
Reference in a new issue