使用 PostgreSQL 统一同步与异步非文本任务的并发准入、持久化等待和 Worker 容量分配,并将生产 API 与独立 Worker 角色拆分。 补充策略管理、共享契约、OpenAPI、Kubernetes 双节点 Worker 清单及跨节点验收脚本;未默认启用任何生产 queue_size 策略。 已在原基线完成 Go、前端、迁移、Shell、Kustomize 与长任务容量验收;合入最新主干后将重新执行发布门禁。
131 lines
4.0 KiB
TypeScript
131 lines
4.0 KiB
TypeScript
import type { BaseModelCatalogItem } from '@easyai-ai-gateway/contracts';
|
||
import { describe, expect, it } from 'vitest';
|
||
import { createEmptyPlatformForm, platformModelPayloads, platformPayload, validatePlatformForm } from './platform-form';
|
||
|
||
const baseModel = {
|
||
id: 'model-1',
|
||
providerKey: 'simulation',
|
||
canonicalModelKey: 'simulation:model-1',
|
||
invocationName: 'model-1',
|
||
providerModelName: 'model-1',
|
||
modelType: ['video_generate'],
|
||
modelAlias: 'model-1',
|
||
displayName: 'Model 1',
|
||
referenceCount: 0,
|
||
pricingVersion: 1,
|
||
status: 'active',
|
||
createdAt: '2026-07-24T00:00:00Z',
|
||
updatedAt: '2026-07-24T00:00:00Z',
|
||
} satisfies BaseModelCatalogItem;
|
||
|
||
function selectedForm() {
|
||
return {
|
||
...createEmptyPlatformForm('simulation'),
|
||
selectedModelIds: [baseModel.id],
|
||
};
|
||
}
|
||
|
||
describe('platform model rate limit payload', () => {
|
||
it('preserves an existing model policy when the edit form was not touched', () => {
|
||
const [payload] = platformModelPayloads([baseModel], selectedForm());
|
||
|
||
expect(payload.rateLimitPolicyMode).toBeUndefined();
|
||
expect(payload.rateLimitPolicy).toBeUndefined();
|
||
});
|
||
|
||
it('writes inherit only after the override is explicitly disabled', () => {
|
||
const [payload] = platformModelPayloads([baseModel], {
|
||
...selectedForm(),
|
||
modelRateLimitOverrideTouched: true,
|
||
});
|
||
|
||
expect(payload.rateLimitPolicyMode).toBe('inherit');
|
||
expect(payload.rateLimitPolicy).toBeUndefined();
|
||
});
|
||
|
||
it('writes an explicit unlimited override when the enabled policy has no rules', () => {
|
||
const [payload] = platformModelPayloads([baseModel], {
|
||
...selectedForm(),
|
||
modelOverrideRateLimit: true,
|
||
modelRateLimitOverrideTouched: true,
|
||
});
|
||
|
||
expect(payload.rateLimitPolicyMode).toBe('override');
|
||
expect(payload.rateLimitPolicy).toEqual({ rules: [] });
|
||
});
|
||
|
||
it('serializes queue length and defaults maximum wait to 600 seconds', () => {
|
||
const form = {
|
||
...selectedForm(),
|
||
name: 'Queue platform',
|
||
baseUrl: 'https://example.test',
|
||
testMode: true,
|
||
queueSize: '12',
|
||
queueMaxWaitSeconds: '',
|
||
modelOverrideRateLimit: true,
|
||
modelRateLimitOverrideTouched: true,
|
||
modelQueueSize: '4',
|
||
modelQueueMaxWaitSeconds: '90',
|
||
};
|
||
|
||
expect(platformPayload(form).rateLimitPolicy).toEqual({
|
||
rules: [{ metric: 'queue_size', limit: 12, maxWaitSeconds: 600 }],
|
||
});
|
||
expect(platformModelPayloads([baseModel], form)[0].rateLimitPolicy).toEqual({
|
||
rules: [{ metric: 'queue_size', limit: 4, maxWaitSeconds: 90 }],
|
||
});
|
||
});
|
||
|
||
it('rejects queue values outside the shared policy bounds', () => {
|
||
const form = {
|
||
...selectedForm(),
|
||
name: 'Queue platform',
|
||
baseUrl: 'https://example.test',
|
||
testMode: true,
|
||
queueSize: '10001',
|
||
};
|
||
expect(validatePlatformForm(form, 1)).toContain('1–10000');
|
||
expect(validatePlatformForm({ ...form, queueSize: '2', queueMaxWaitSeconds: '3601' }, 1)).toContain('1–3600');
|
||
});
|
||
|
||
it('preserves unknown policy fields and non-editable rules', () => {
|
||
const form = {
|
||
...selectedForm(),
|
||
name: 'Compatible platform',
|
||
baseUrl: 'https://example.test',
|
||
testMode: true,
|
||
concurrencyLimit: '3',
|
||
queueSize: '6',
|
||
rateLimitPolicyExtra: { extensionMode: 'legacy' },
|
||
rateLimitRuleExtras: {
|
||
concurrent: { consume: 'lease', vendorHint: 'keep' },
|
||
queue_size: { vendorQueueMode: 'shared' },
|
||
},
|
||
rateLimitPreservedRules: [
|
||
{ metric: 'tpm_input', limit: 1234, vendorWindow: true },
|
||
],
|
||
};
|
||
|
||
expect(platformPayload(form).rateLimitPolicy).toEqual({
|
||
extensionMode: 'legacy',
|
||
rules: [
|
||
{ metric: 'tpm_input', limit: 1234, vendorWindow: true },
|
||
{
|
||
consume: 'lease',
|
||
vendorHint: 'keep',
|
||
metric: 'concurrent',
|
||
limit: 3,
|
||
windowSeconds: 60,
|
||
leaseTtlSeconds: 120,
|
||
},
|
||
{
|
||
vendorQueueMode: 'shared',
|
||
metric: 'queue_size',
|
||
limit: 6,
|
||
maxWaitSeconds: 600,
|
||
},
|
||
],
|
||
});
|
||
});
|
||
});
|