Files
lzt404 0e7437493d fix(api): align management requests and configuration with v8
- Use native v8 routes for credentials, models, OAuth, and API calls
- Remove implicit legacy endpoint and response transformations
- Serialize OAuth exclusion updates while preserving other providers
- Send YAML as raw text and preserve explicit null request bodies
- Update mocks and regression tests, including migrated Claude aliases
2026-10-04 17:31:52 +08:00

63 lines
2.7 KiB
TypeScript

import { describe, expect, it } from 'bun:test';
import {
modelMatchesRule,
normalizeOAuthExcludedRules,
oauthExcludedRulesFromPayload,
oauthModelCandidates,
oauthModelsFromPayload,
openOAuthModelNames,
setOAuthModelsExcluded,
} from '../src/services/oauthModels';
const models = [
{ id: 'gpt-5.4', displayName: 'GPT 5.4' },
{ id: 'gpt-image-1.5', displayName: 'GPT Image 1.5' },
{ id: 'gpt-image-2', displayName: 'GPT Image 2' },
];
describe('OAuth model exclusion rules', () => {
it('parses model definitions and normalizes provider rules without duplicates', () => {
expect(oauthModelsFromPayload({ models: [
{ id: 'gpt-5.4', display_name: 'GPT 5.4' },
{ id: 'GPT-5.4' },
] })).toEqual([{ id: 'gpt-5.4', displayName: 'GPT 5.4' }]);
expect(oauthExcludedRulesFromPayload({
codex: [' GPT-IMAGE-* ', 'gpt-image-*'],
}, 'codex')).toEqual(['gpt-image-*']);
expect(normalizeOAuthExcludedRules([' GPT-* ', '', 'gpt-*', 'future-model']))
.toEqual(['gpt-*', 'future-model']);
});
it('treats checked models as exclusions and unchecking removes only their exact rule', () => {
const rules = setOAuthModelsExcluded(['future-*'], [models[0]], true);
expect(rules).toEqual(['future-*', 'gpt-5.4']);
expect([...openOAuthModelNames(models, rules)]).toEqual(['gpt-image-1.5', 'gpt-image-2']);
expect(setOAuthModelsExcluded(rules, [models[0]], false)).toEqual(['future-*']);
});
it('excludes all candidates without converting the list to a wildcard', () => {
const rules = setOAuthModelsExcluded(['future-*'], models, true);
expect(rules).toEqual(['future-*', 'gpt-5.4', 'gpt-image-1.5', 'gpt-image-2']);
expect(rules).not.toContain('*');
expect(openOAuthModelNames([{ id: 'new-model' }], rules).has('new-model')).toBe(true);
});
it('clearing selections preserves wildcard and off-list rules', () => {
expect(setOAuthModelsExcluded(['gpt-image-*', 'gpt-5.4', 'future-model', '*'], models, false))
.toEqual(['gpt-image-*', 'future-model', '*']);
expect(setOAuthModelsExcluded(['gpt-image-*'], [models[1]], false)).toEqual(['gpt-image-*']);
});
it('restores excluded exact models to candidates without expanding wildcard rules', () => {
expect(oauthModelCandidates([models[0]], ['gpt-image-2', 'GPT-5.4', 'future-*']))
.toEqual([models[0], { id: 'gpt-image-2' }]);
});
it('matches CPA wildcard rules case-insensitively and treats regex punctuation literally', () => {
expect(modelMatchesRule('GPT-IMAGE-2', 'gpt-image-*')).toBe(true);
expect(modelMatchesRule('gpt-5x4', 'gpt-5.4')).toBe(false);
expect(openOAuthModelNames([...models, { id: 'future-model' }], ['*']).size).toBe(0);
expect(openOAuthModelNames(models, []).size).toBe(models.length);
});
});