114 行
4.0 KiB
TypeScript
114 行
4.0 KiB
TypeScript
import { AdminDeepSeekController } from '../../../src/modules/ai/controller/admin/deepseek';
|
|
import { DeepSeekService } from '../../../src/modules/ai/service/deepseek';
|
|
import { CoolCommException } from '@cool-midway/core';
|
|
|
|
describe('AdminDeepSeekController 多租户 BYOK 与 Token 用量控制台 (TDD)', () => {
|
|
let controller: AdminDeepSeekController;
|
|
let service: DeepSeekService;
|
|
|
|
beforeEach(() => {
|
|
service = new DeepSeekService();
|
|
controller = new AdminDeepSeekController();
|
|
controller.deepSeekService = service;
|
|
(controller as any).ok = (data: any) => ({ code: 1000, message: 'success', data });
|
|
});
|
|
|
|
describe('1. 租户管理员私有 BYOK 配置隔离', () => {
|
|
it('租户管理员保存私有 API Key,仅生效于该租户,不污染全局配置', async () => {
|
|
// 模拟租户 888 的登录上下文
|
|
(controller as any).ctx = {
|
|
admin: { username: 'tenant_admin_888', tenantId: 888 },
|
|
};
|
|
|
|
// 租户保存私有 Key
|
|
const saveRes: any = await controller.config({
|
|
apiKey: 'sk-tenant-888-secret-key-123456',
|
|
model: 'deepseek-reasoner',
|
|
});
|
|
|
|
expect(saveRes.code).toBe(1000);
|
|
expect(saveRes.data.isConfigured).toBe(true);
|
|
expect(saveRes.data.model).toBe('deepseek-reasoner');
|
|
// 验证脱敏显示
|
|
expect(saveRes.data.apiKey).toBe('sk-t****3456');
|
|
|
|
// 验证底层 service 中该租户已配置独立 Key
|
|
expect(service.isConfigured(888)).toBe(true);
|
|
expect(service.getTenantConfig(888).apiKey).toBe('sk-tenant-888-secret-key-123456');
|
|
|
|
// 验证全局公共池并未被污染
|
|
expect(service.isConfigured()).toBe(false);
|
|
expect(service.getConfig().apiKey).toBeFalsy();
|
|
});
|
|
|
|
it('超管访问 config 操作平台公共资金池配置', async () => {
|
|
// 模拟平台超管上下文
|
|
(controller as any).ctx = {
|
|
admin: { username: 'admin', isSuper: true },
|
|
};
|
|
|
|
await controller.config({
|
|
apiKey: 'sk-platform-master-key-999999',
|
|
model: 'deepseek-chat',
|
|
});
|
|
|
|
// 平台全局已配置
|
|
expect(service.isConfigured()).toBe(true);
|
|
expect(service.getConfig().apiKey).toBe('sk-platform-master-key-999999');
|
|
});
|
|
});
|
|
|
|
describe('2. 租户 Token 消耗统计与用量查询 (POST /usage)', () => {
|
|
it('租户可准确查询自身的 Token 消耗与配额进度', async () => {
|
|
// 模拟记录租户 666 的 Token 消耗
|
|
service.recordTokenUsage(666, {
|
|
promptTokens: 120,
|
|
completionTokens: 80,
|
|
totalTokens: 200,
|
|
});
|
|
service.setTenantTokenQuota(666, 10000);
|
|
|
|
(controller as any).ctx = {
|
|
admin: { username: 'tenant_user_666', tenantId: 666 },
|
|
};
|
|
|
|
const usageRes = await (controller as any).usage();
|
|
expect(usageRes.code).toBe(1000);
|
|
expect(usageRes.data.totalTokens).toBe(200);
|
|
expect(usageRes.data.promptTokens).toBe(120);
|
|
expect(usageRes.data.completionTokens).toBe(80);
|
|
expect(usageRes.data.callCount).toBe(1);
|
|
expect(usageRes.data.quota).toBe(10000);
|
|
expect(usageRes.data.percent).toBe(2); // 200 / 10000 = 2%
|
|
});
|
|
});
|
|
|
|
describe('3. 超管设置租户配额 (POST /quota)', () => {
|
|
it('超级管理员可成功为指定租户设置额度上限', async () => {
|
|
(controller as any).ctx = {
|
|
admin: { username: 'admin', isSuper: true },
|
|
};
|
|
|
|
const quotaRes = await (controller as any).quota({
|
|
tenantId: 777,
|
|
maxTokens: 50000,
|
|
});
|
|
|
|
expect(quotaRes.code).toBe(1000);
|
|
// 验证生效
|
|
expect(() => service.assertTenantTokenQuota(777, 40000)).not.toThrow();
|
|
expect(() => service.assertTenantTokenQuota(777, 60000)).toThrow(CoolCommException);
|
|
});
|
|
|
|
it('非超级管理员调用 quota 必须抛出 403 阻断越权', async () => {
|
|
(controller as any).ctx = {
|
|
admin: { username: 'tenant_rep', tenantId: 10 },
|
|
};
|
|
|
|
await expect(
|
|
(controller as any).quota({ tenantId: 10, maxTokens: 999999 })
|
|
).rejects.toThrow(CoolCommException);
|
|
});
|
|
});
|
|
});
|