PSA/shared/workflow/runtime/ai/__tests__/aiSchema.test.ts
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

189 lines
4.5 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
buildWorkflowAiSimpleSchema,
hydrateWorkflowAiSimpleFields,
parseWorkflowAiSchemaText,
resolveWorkflowAiSchemaFromConfig,
validateWorkflowAiSchema,
type WorkflowAiSimpleField,
type WorkflowJsonSchema,
} from '../aiSchema';
const simpleFields: WorkflowAiSimpleField[] = [
{
id: 'summary',
name: 'summary',
type: 'string',
required: true,
description: 'Short summary',
},
{
id: 'sentiment',
name: 'sentiment',
type: 'string',
required: false,
description: 'Overall tone',
},
{
id: 'details',
name: 'details',
type: 'object',
required: false,
children: [
{
id: 'confidence',
name: 'confidence',
type: 'number',
required: true,
},
],
},
{
id: 'tags',
name: 'tags',
type: 'array',
required: false,
arrayItemType: 'string',
},
{
id: 'actions',
name: 'actions',
type: 'array',
required: false,
arrayItemType: 'object',
children: [
{
id: 'label',
name: 'label',
type: 'string',
required: true,
},
{
id: 'priority',
name: 'priority',
type: 'integer',
required: false,
},
],
},
];
describe('workflow ai schema utilities', () => {
it('T011/T012/T013/T014/T015/T016/T017: simple builder serializes the supported object-root subset canonically', () => {
expect(buildWorkflowAiSimpleSchema(simpleFields)).toEqual({
type: 'object',
properties: {
summary: {
type: 'string',
description: 'Short summary',
},
sentiment: {
type: 'string',
description: 'Overall tone',
},
details: {
type: 'object',
properties: {
confidence: {
type: 'number',
},
},
additionalProperties: false,
required: ['confidence'],
},
tags: {
type: 'array',
items: {
type: 'string',
},
},
actions: {
type: 'array',
items: {
type: 'object',
properties: {
label: {
type: 'string',
},
priority: {
type: 'integer',
},
},
additionalProperties: false,
required: ['label'],
},
},
},
additionalProperties: false,
required: ['summary'],
});
});
it('T019: advanced schema validation rejects invalid JSON and unsupported v1 constructs', () => {
expect(parseWorkflowAiSchemaText('{')).toMatchObject({
schema: null,
});
expect(
validateWorkflowAiSchema(
{
type: 'object',
properties: {
result: {
anyOf: [{ type: 'string' }, { type: 'number' }],
},
},
},
'advanced'
)
).toContain('AI output schema.properties.result cannot use anyOf in v1.');
});
it('T020: simple-compatible saved schemas rehydrate into the simple field tree', () => {
const schema = buildWorkflowAiSimpleSchema(simpleFields);
const hydrated = hydrateWorkflowAiSimpleFields(schema);
expect(hydrated.ok).toBe(true);
if (!hydrated.ok) return;
expect(hydrated.fields).toHaveLength(5);
expect(hydrated.fields.find((field) => field.name === 'details')?.children?.[0]).toMatchObject({
name: 'confidence',
type: 'number',
required: true,
});
expect(hydrated.fields.find((field) => field.name === 'actions')).toMatchObject({
type: 'array',
arrayItemType: 'object',
});
});
it('T021: advanced-only schemas stay out of simple mode without lossy conversion', () => {
const advancedOnlySchema: WorkflowJsonSchema = {
type: 'object',
properties: {
result: {
type: 'object',
additionalProperties: {
type: 'string',
},
},
},
};
const hydrated = hydrateWorkflowAiSimpleFields(advancedOnlySchema);
expect(hydrated).toEqual({
ok: false,
reason: 'Simple mode does not support map-style object fields on result.',
});
const resolved = resolveWorkflowAiSchemaFromConfig({
actionId: 'ai.infer',
aiOutputSchemaMode: 'advanced',
aiOutputSchemaText: JSON.stringify(advancedOnlySchema),
});
expect(resolved.schema).toEqual(advancedOnlySchema);
expect(resolved.errors).toEqual([]);
});
});