Remove fallbackProviderRef usage from settings and config context to streamline provider resolution. Adjust normalizeLegacyProviderRef to retain 'default-openai' unless a non-default fallback is explicitly provided. Update provider listing to enforce deterministic ordering by updatedAt, createdAt, and slug. Expand unit tests to verify provider ordering, legacy ref normalization, and selection logic.
138 lines
4 KiB
TypeScript
138 lines
4 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
import { inArray } from 'drizzle-orm';
|
|
import { db } from '../../src/db';
|
|
import { adminProviders } from '../../src/db/schema';
|
|
|
|
import {
|
|
AdminProviderError,
|
|
listAdminProviders,
|
|
toMasked,
|
|
validateProviderType,
|
|
validateSlug,
|
|
type AdminProviderRecord,
|
|
} from '../../src/lib/server/admin/providers';
|
|
|
|
test.describe('admin provider validation', () => {
|
|
test('normalizes valid slugs to lowercase', () => {
|
|
expect(validateSlug('My-Provider-1')).toBe('my-provider-1');
|
|
});
|
|
|
|
test('rejects built-in provider ids as slugs', () => {
|
|
expect(() => validateSlug('openai')).toThrow(AdminProviderError);
|
|
expect(() => validateSlug('custom-openai')).toThrow('reserved');
|
|
});
|
|
|
|
test('rejects malformed slug values', () => {
|
|
expect(() => validateSlug('-bad-')).toThrow(AdminProviderError);
|
|
expect(() => validateSlug('bad_slug')).toThrow(AdminProviderError);
|
|
expect(() => validateSlug('')).toThrow(AdminProviderError);
|
|
});
|
|
|
|
test('accepts only known provider types', () => {
|
|
expect(validateProviderType('openai')).toBe('openai');
|
|
expect(() => validateProviderType('unknown')).toThrow(AdminProviderError);
|
|
});
|
|
|
|
test('masks api key from last4 in list responses', () => {
|
|
const record: AdminProviderRecord = {
|
|
id: 'id-1',
|
|
slug: 'shared-one',
|
|
displayName: 'Shared One',
|
|
providerType: 'openai',
|
|
baseUrl: null,
|
|
apiKeyCiphertext: 'ciphertext',
|
|
apiKeyIv: 'iv',
|
|
apiKeyLast4: 'abcd',
|
|
defaultModel: 'gpt-4o-mini-tts',
|
|
defaultInstructions: 'warm tone',
|
|
enabled: true,
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
};
|
|
|
|
expect(toMasked(record).apiKeyMask).toBe('••••abcd');
|
|
});
|
|
|
|
test('lists providers in deterministic updated/created/slug order', async () => {
|
|
const suffix = `${Date.now()}${Math.random().toString(36).slice(2, 8)}`;
|
|
const inserted = [
|
|
{
|
|
id: `prov-aaa-${suffix}`,
|
|
slug: `ord-aaa-${suffix}`,
|
|
displayName: 'Order AAA',
|
|
providerType: 'openai' as const,
|
|
baseUrl: null,
|
|
apiKeyCiphertext: 'cipher',
|
|
apiKeyIv: 'iv',
|
|
apiKeyLast4: '1111',
|
|
defaultModel: null,
|
|
defaultInstructions: null,
|
|
enabled: 1,
|
|
createdAt: 200,
|
|
updatedAt: 300,
|
|
},
|
|
{
|
|
id: `prov-zzz-${suffix}`,
|
|
slug: `ord-zzz-${suffix}`,
|
|
displayName: 'Order ZZZ',
|
|
providerType: 'openai' as const,
|
|
baseUrl: null,
|
|
apiKeyCiphertext: 'cipher',
|
|
apiKeyIv: 'iv',
|
|
apiKeyLast4: '2222',
|
|
defaultModel: null,
|
|
defaultInstructions: null,
|
|
enabled: 1,
|
|
createdAt: 200,
|
|
updatedAt: 300,
|
|
},
|
|
{
|
|
id: `prov-mid-${suffix}`,
|
|
slug: `ord-mid-${suffix}`,
|
|
displayName: 'Order MID',
|
|
providerType: 'openai' as const,
|
|
baseUrl: null,
|
|
apiKeyCiphertext: 'cipher',
|
|
apiKeyIv: 'iv',
|
|
apiKeyLast4: '3333',
|
|
defaultModel: null,
|
|
defaultInstructions: null,
|
|
enabled: 1,
|
|
createdAt: 150,
|
|
updatedAt: 300,
|
|
},
|
|
{
|
|
id: `prov-old-${suffix}`,
|
|
slug: `ord-old-${suffix}`,
|
|
displayName: 'Order OLD',
|
|
providerType: 'openai' as const,
|
|
baseUrl: null,
|
|
apiKeyCiphertext: 'cipher',
|
|
apiKeyIv: 'iv',
|
|
apiKeyLast4: '4444',
|
|
defaultModel: null,
|
|
defaultInstructions: null,
|
|
enabled: 1,
|
|
createdAt: 999,
|
|
updatedAt: 200,
|
|
},
|
|
];
|
|
|
|
try {
|
|
await db.insert(adminProviders).values(inserted);
|
|
const listed = await listAdminProviders();
|
|
const ordered = listed
|
|
.filter((row) => row.slug.endsWith(suffix))
|
|
.map((row) => row.slug);
|
|
|
|
expect(ordered).toEqual([
|
|
`ord-aaa-${suffix}`,
|
|
`ord-zzz-${suffix}`,
|
|
`ord-mid-${suffix}`,
|
|
`ord-old-${suffix}`,
|
|
]);
|
|
} finally {
|
|
await db.delete(adminProviders).where(inArray(adminProviders.id, inserted.map((row) => row.id)));
|
|
}
|
|
});
|
|
});
|