- Implemented `normalizeMigrationInput` to standardize database migration input for sqlite, mysql, and postgres. - Added `maskConnectionString` to obscure sensitive credentials in connection strings. - Created comprehensive tests for migration input normalization and connection string masking. - Established database schema creation logic for multiple dialects (sqlite, mysql, postgres). - Developed functions for managing database transactions, including begin, commit, and rollback. - Implemented data insertion logic from backup snapshots into the target database.
124 lines
4.1 KiB
TypeScript
124 lines
4.1 KiB
TypeScript
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
|
import { mkdtempSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { and, eq } from 'drizzle-orm';
|
|
|
|
type DbModule = typeof import('../db/index.js');
|
|
type ModelServiceModule = typeof import('./modelService.js');
|
|
|
|
describe('rebuildTokenRoutesFromAvailability', () => {
|
|
let db: DbModule['db'];
|
|
let schema: DbModule['schema'];
|
|
let rebuildTokenRoutesFromAvailability: ModelServiceModule['rebuildTokenRoutesFromAvailability'];
|
|
let dataDir = '';
|
|
|
|
beforeAll(async () => {
|
|
dataDir = mkdtempSync(join(tmpdir(), 'metapi-model-service-'));
|
|
process.env.DATA_DIR = dataDir;
|
|
|
|
await import('../db/migrate.js');
|
|
const dbModule = await import('../db/index.js');
|
|
const modelService = await import('./modelService.js');
|
|
|
|
db = dbModule.db;
|
|
schema = dbModule.schema;
|
|
rebuildTokenRoutesFromAvailability = modelService.rebuildTokenRoutesFromAvailability;
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
await db.delete(schema.routeChannels).run();
|
|
await db.delete(schema.tokenRoutes).run();
|
|
await db.delete(schema.tokenModelAvailability).run();
|
|
await db.delete(schema.modelAvailability).run();
|
|
await db.delete(schema.accountTokens).run();
|
|
await db.delete(schema.accounts).run();
|
|
await db.delete(schema.sites).run();
|
|
});
|
|
|
|
afterAll(() => {
|
|
delete process.env.DATA_DIR;
|
|
});
|
|
|
|
it('removes stale exact routes and keeps wildcard routes on rebuild', async () => {
|
|
const site = await db.insert(schema.sites).values({
|
|
name: 'site-1',
|
|
url: 'https://site-1.example.com',
|
|
platform: 'new-api',
|
|
}).returning().get();
|
|
|
|
const account = await db.insert(schema.accounts).values({
|
|
siteId: site.id,
|
|
username: 'user-1',
|
|
accessToken: 'access-1',
|
|
status: 'active',
|
|
}).returning().get();
|
|
|
|
const token = await db.insert(schema.accountTokens).values({
|
|
accountId: account.id,
|
|
name: 'default',
|
|
token: 'sk-test',
|
|
source: 'manual',
|
|
enabled: true,
|
|
isDefault: true,
|
|
}).returning().get();
|
|
|
|
await db.insert(schema.tokenModelAvailability).values({
|
|
tokenId: token.id,
|
|
modelName: 'latest-model',
|
|
available: true,
|
|
}).run();
|
|
|
|
const staleRoute = await db.insert(schema.tokenRoutes).values({
|
|
modelPattern: 'old-model',
|
|
enabled: true,
|
|
}).returning().get();
|
|
|
|
await db.insert(schema.routeChannels).values({
|
|
routeId: staleRoute.id,
|
|
accountId: account.id,
|
|
tokenId: token.id,
|
|
priority: 0,
|
|
weight: 10,
|
|
enabled: true,
|
|
manualOverride: false,
|
|
}).run();
|
|
|
|
const wildcardRoute = await db.insert(schema.tokenRoutes).values({
|
|
modelPattern: 'gpt-*',
|
|
enabled: true,
|
|
}).returning().get();
|
|
|
|
await db.insert(schema.routeChannels).values({
|
|
routeId: wildcardRoute.id,
|
|
accountId: account.id,
|
|
tokenId: token.id,
|
|
priority: 0,
|
|
weight: 10,
|
|
enabled: true,
|
|
manualOverride: false,
|
|
}).run();
|
|
|
|
const rebuild = await rebuildTokenRoutesFromAvailability();
|
|
|
|
expect(rebuild.models).toBe(1);
|
|
expect(rebuild.removedRoutes).toBe(1);
|
|
|
|
const oldRoute = await db.select().from(schema.tokenRoutes).where(eq(schema.tokenRoutes.id, staleRoute.id)).get();
|
|
expect(oldRoute).toBeUndefined();
|
|
|
|
const oldChannels = await db.select().from(schema.routeChannels).where(eq(schema.routeChannels.routeId, staleRoute.id)).all();
|
|
expect(oldChannels).toHaveLength(0);
|
|
|
|
const latestRoute = await db.select().from(schema.tokenRoutes).where(eq(schema.tokenRoutes.modelPattern, 'latest-model')).get();
|
|
expect(latestRoute).toBeDefined();
|
|
const latestChannels = await db.select().from(schema.routeChannels)
|
|
.where(and(eq(schema.routeChannels.routeId, latestRoute!.id), eq(schema.routeChannels.tokenId, token.id)))
|
|
.all();
|
|
expect(latestChannels.length).toBeGreaterThan(0);
|
|
|
|
const wildcardRouteAfter = await db.select().from(schema.tokenRoutes).where(eq(schema.tokenRoutes.id, wildcardRoute.id)).get();
|
|
expect(wildcardRouteAfter).toBeDefined();
|
|
});
|
|
});
|