forked from boos/BoosAPI
feat(tests): add comprehensive tests for TokenRouter and related components
- Implement tests for TokenRouter downstream policy, covering allowedRouteIds and site weight multipliers. - Add tests for TokenRouter patterns and model mapping, including regex pattern matching and model mapping precedence. - Create tests for BrandIcon component to verify brand detection logic. - Develop tests for Accounts page, focusing on rebind panel behavior and refresh action on error. - Introduce fuzzy search utility tests to validate substring and subsequence matching. - Implement navigation focus helpers tests for building and parsing focus paths. - Add progressive render tests to ensure correct initial and next visible counts. - Create route model candidates index tests to validate route candidate indexing and matching logic.
This commit is contained in:
@@ -113,8 +113,79 @@ function ensureSiteStatusSchema() {
|
||||
`);
|
||||
}
|
||||
|
||||
function ensureSiteProxySchema() {
|
||||
if (!tableExists('sites')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tableColumnExists('sites', 'proxy_url')) {
|
||||
sqlite.exec(`ALTER TABLE sites ADD COLUMN proxy_url text;`);
|
||||
}
|
||||
}
|
||||
|
||||
function ensureRouteGroupingSchema() {
|
||||
if (!tableExists('token_routes') || !tableExists('route_channels')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tableColumnExists('token_routes', 'display_name')) {
|
||||
sqlite.exec(`ALTER TABLE token_routes ADD COLUMN display_name text;`);
|
||||
}
|
||||
|
||||
if (!tableColumnExists('token_routes', 'display_icon')) {
|
||||
sqlite.exec(`ALTER TABLE token_routes ADD COLUMN display_icon text;`);
|
||||
}
|
||||
|
||||
if (!tableColumnExists('route_channels', 'source_model')) {
|
||||
sqlite.exec(`ALTER TABLE route_channels ADD COLUMN source_model text;`);
|
||||
}
|
||||
}
|
||||
|
||||
function ensureDownstreamApiKeySchema() {
|
||||
sqlite.exec(`
|
||||
CREATE TABLE IF NOT EXISTS downstream_api_keys (
|
||||
id integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
name text NOT NULL,
|
||||
key text NOT NULL,
|
||||
description text,
|
||||
enabled integer DEFAULT true,
|
||||
expires_at text,
|
||||
max_cost real,
|
||||
used_cost real DEFAULT 0,
|
||||
max_requests integer,
|
||||
used_requests integer DEFAULT 0,
|
||||
supported_models text,
|
||||
allowed_route_ids text,
|
||||
site_weight_multipliers text,
|
||||
last_used_at text,
|
||||
created_at text DEFAULT (datetime('now')),
|
||||
updated_at text DEFAULT (datetime('now'))
|
||||
);
|
||||
`);
|
||||
|
||||
sqlite.exec(`
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS downstream_api_keys_key_unique
|
||||
ON downstream_api_keys(key);
|
||||
`);
|
||||
sqlite.exec(`
|
||||
CREATE INDEX IF NOT EXISTS downstream_api_keys_name_idx
|
||||
ON downstream_api_keys(name);
|
||||
`);
|
||||
sqlite.exec(`
|
||||
CREATE INDEX IF NOT EXISTS downstream_api_keys_enabled_idx
|
||||
ON downstream_api_keys(enabled);
|
||||
`);
|
||||
sqlite.exec(`
|
||||
CREATE INDEX IF NOT EXISTS downstream_api_keys_expires_at_idx
|
||||
ON downstream_api_keys(expires_at);
|
||||
`);
|
||||
}
|
||||
|
||||
ensureTokenManagementSchema();
|
||||
ensureSiteStatusSchema();
|
||||
ensureSiteProxySchema();
|
||||
ensureRouteGroupingSchema();
|
||||
ensureDownstreamApiKeySchema();
|
||||
|
||||
export const db = drizzle(sqlite, { schema });
|
||||
export { schema };
|
||||
|
||||
+29
-1
@@ -5,7 +5,8 @@ export const sites = sqliteTable('sites', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
name: text('name').notNull(),
|
||||
url: text('url').notNull(),
|
||||
platform: text('platform').notNull(), // 'new-api' | 'one-api' | 'veloera' | 'one-hub' | 'done-hub' | 'sub2api'
|
||||
platform: text('platform').notNull(), // 'new-api' | 'one-api' | 'veloera' | 'one-hub' | 'done-hub' | 'sub2api' | 'openai' | 'claude' | 'gemini'
|
||||
proxyUrl: text('proxy_url'),
|
||||
status: text('status').notNull().default('active'), // 'active' | 'disabled'
|
||||
isPinned: integer('is_pinned', { mode: 'boolean' }).default(false),
|
||||
sortOrder: integer('sort_order').default(0),
|
||||
@@ -101,6 +102,8 @@ export const tokenModelAvailability = sqliteTable('token_model_availability', {
|
||||
export const tokenRoutes = sqliteTable('token_routes', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
modelPattern: text('model_pattern').notNull(),
|
||||
displayName: text('display_name'),
|
||||
displayIcon: text('display_icon'),
|
||||
modelMapping: text('model_mapping'), // JSON
|
||||
enabled: integer('enabled', { mode: 'boolean' }).default(true),
|
||||
createdAt: text('created_at').default(sql`(datetime('now'))`),
|
||||
@@ -115,6 +118,7 @@ export const routeChannels = sqliteTable('route_channels', {
|
||||
routeId: integer('route_id').notNull().references(() => tokenRoutes.id, { onDelete: 'cascade' }),
|
||||
accountId: integer('account_id').notNull().references(() => accounts.id, { onDelete: 'cascade' }),
|
||||
tokenId: integer('token_id').references(() => accountTokens.id, { onDelete: 'set null' }),
|
||||
sourceModel: text('source_model'),
|
||||
priority: integer('priority').default(0),
|
||||
weight: integer('weight').default(10),
|
||||
enabled: integer('enabled', { mode: 'boolean' }).default(true),
|
||||
@@ -163,6 +167,30 @@ export const settings = sqliteTable('settings', {
|
||||
value: text('value'), // JSON
|
||||
});
|
||||
|
||||
export const downstreamApiKeys = sqliteTable('downstream_api_keys', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
name: text('name').notNull(),
|
||||
key: text('key').notNull(),
|
||||
description: text('description'),
|
||||
enabled: integer('enabled', { mode: 'boolean' }).default(true),
|
||||
expiresAt: text('expires_at'),
|
||||
maxCost: real('max_cost'),
|
||||
usedCost: real('used_cost').default(0),
|
||||
maxRequests: integer('max_requests'),
|
||||
usedRequests: integer('used_requests').default(0),
|
||||
supportedModels: text('supported_models'), // JSON array<string>
|
||||
allowedRouteIds: text('allowed_route_ids'), // JSON array<number>
|
||||
siteWeightMultipliers: text('site_weight_multipliers'), // JSON object { [siteId]: multiplier }
|
||||
lastUsedAt: text('last_used_at'),
|
||||
createdAt: text('created_at').default(sql`(datetime('now'))`),
|
||||
updatedAt: text('updated_at').default(sql`(datetime('now'))`),
|
||||
}, (table) => ({
|
||||
keyUnique: uniqueIndex('downstream_api_keys_key_unique').on(table.key),
|
||||
nameIdx: index('downstream_api_keys_name_idx').on(table.name),
|
||||
enabledIdx: index('downstream_api_keys_enabled_idx').on(table.enabled),
|
||||
expiresAtIdx: index('downstream_api_keys_expires_at_idx').on(table.expiresAt),
|
||||
}));
|
||||
|
||||
export const events = sqliteTable('events', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
type: text('type').notNull(), // 'checkin' | 'balance' | 'token' | 'proxy' | 'status'
|
||||
|
||||
Reference in New Issue
Block a user