Files
twenty/packages/twenty-server/test/integration/metadata/suites/agent/failing-agent-creation.integration-spec.ts
T
Félix Malfait 908aefe7c1 feat: replace hardcoded AI model constants with JSON seed catalog (#18818)
## Summary

- Replaces per-provider TypeScript constant files
(`openai-models.const.ts`, `anthropic-models.const.ts`, etc.) with a
single `ai-providers.json` catalog as the source of truth
- Adds runtime model discovery via AI SDK for self-hosted providers,
with `models.dev` enrichment for pricing/capabilities
- Introduces composite model IDs (`provider/modelId`) for canonical,
conflict-free identification
- Simplifies provider configuration: API keys are injected from
environment variables (e.g., `OPENAI_API_KEY`)
- Adds admin panel UI for provider management (add/remove/test), model
discovery, recommended model configuration, and default fast/smart model
selection per workspace
- Removes deprecated config variables (`AI_DISABLED_MODEL_IDS`,
`AUTO_ENABLE_NEW_AI_MODELS`, etc.)
- Adds database migration for composite model ID format

## Test plan

- [ ] Server typecheck passes
- [ ] Frontend typecheck passes
- [ ] Server unit tests pass
- [ ] Frontend unit tests pass
- [ ] CI pipeline green
- [ ] Admin panel AI tab loads correctly
- [ ] Provider discovery works for configured providers
- [ ] Model recommendation toggles persist
- [ ] Default fast/smart model selection works


Made with [Cursor](https://cursor.com)
2026-03-21 16:03:58 +01:00

178 lines
4.4 KiB
TypeScript

import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util';
import { createOneAgent } from 'test/integration/metadata/suites/agent/utils/create-one-agent.util';
import { deleteOneAgent } from 'test/integration/metadata/suites/agent/utils/delete-one-agent.util';
import {
eachTestingContextFilter,
type EachTestingContext,
} from 'twenty-shared/testing';
import { isDefined } from 'twenty-shared/utils';
import { type CreateAgentInput } from 'src/engine/metadata-modules/ai/ai-agent/dtos/create-agent.input';
type TestContext = {
input: CreateAgentInput;
};
type GlobalTestContext = {
existingAgentLabel: string;
};
const globalTestContext: GlobalTestContext = {
existingAgentLabel: 'Existing Test Agent',
};
type CreateOneAgentTestingContext = EachTestingContext<TestContext>[];
describe('Agent creation should fail', () => {
let existingAgentId: string | undefined;
beforeAll(async () => {
const { data } = await createOneAgent({
expectToFail: false,
input: {
label: globalTestContext.existingAgentLabel,
prompt: 'Existing agent for testing',
modelId: 'openai/gpt-4.1',
},
});
existingAgentId = data.createOneAgent.id;
});
afterAll(async () => {
if (isDefined(existingAgentId)) {
await deleteOneAgent({
expectToFail: false,
input: { id: existingAgentId },
});
}
});
const failingAgentCreationTestCases: CreateOneAgentTestingContext = [
// Missing required properties tests
{
title: 'when label is missing',
context: {
input: {
prompt: 'Test prompt',
modelId: 'openai/gpt-4.1',
} as CreateAgentInput,
},
},
{
title: 'when prompt is missing',
context: {
input: {
label: 'Test Agent Missing Prompt',
modelId: 'openai/gpt-4.1',
} as CreateAgentInput,
},
},
{
title: 'when modelId is missing',
context: {
input: {
label: 'Test Agent Missing ModelId',
prompt: 'Test prompt',
} as CreateAgentInput,
},
},
{
title: 'when label is empty string',
context: {
input: {
label: '',
prompt: 'Test prompt',
modelId: 'openai/gpt-4.1',
},
},
},
{
title: 'when prompt is empty string',
context: {
input: {
label: 'Empty Prompt Agent',
prompt: '',
modelId: 'openai/gpt-4.1',
},
},
},
{
title: 'when modelId is empty string',
context: {
input: {
label: 'Empty ModelId Agent',
prompt: 'Test prompt',
modelId: '' as any,
},
},
},
// Name uniqueness test
{
title: 'when computed name already exists',
context: {
input: {
label: globalTestContext.existingAgentLabel,
prompt: 'Duplicate agent',
modelId: 'openai/gpt-4.1',
},
},
},
// Invalid response format tests
{
title: 'when responseFormat has invalid type',
context: {
input: {
label: 'Invalid Response Format Agent',
prompt: 'Test prompt',
modelId: 'openai/gpt-4.1',
responseFormat: {
type: 'invalid',
} as any,
},
},
},
{
title: 'when responseFormat type is json but schema is missing',
context: {
input: {
label: 'JSON Without Schema Agent',
prompt: 'Test prompt',
modelId: 'openai/gpt-4.1',
responseFormat: {
type: 'json',
} as any,
},
},
},
{
title: 'when responseFormat type is text but schema is present',
context: {
input: {
label: 'Text With Schema Agent',
prompt: 'Test prompt',
modelId: 'openai/gpt-4.1',
responseFormat: {
type: 'text',
schema: { type: 'object' },
} as any,
},
},
},
];
it.each(eachTestingContextFilter(failingAgentCreationTestCases))(
'$title',
async ({ context }) => {
const { errors } = await createOneAgent({
expectToFail: true,
input: context.input,
});
expectOneNotInternalServerErrorSnapshot({
errors,
});
},
);
});