Files
insights/services/mcp/tests/unit/schema-transforms.test.ts
zeekay 40d120dbf7 refactor(api): say /v1/ in the Python callers
Every first-party Python caller and test now names the mount the API actually
answers on. Mechanical: URL literals only, and only where the /api/ was ours --
a preceding word character means it was a module path like insights/api/ or a
third-party host like slack.com/api/, and those are untouched.

Left saying /api/, each for a reason rather than for safety:
- products/warehouse_sources/.../sources/** is a catalogue of vendor endpoints
- services/llm-gateway deploys separately and is under a code freeze, so moving
  its two calls would break it during the skew
- the mcp_store and Slack/Teams redirect_uris are registered with third parties
  and must byte-match what was registered
- the Wizard and Raycast client-metadata paths are OAuth client_ids
- /api/surveys and /api/feature_flag/local_evaluation are SDK aliases the
  ingress routes to the Rust fleets, held by released SDK versions

Test URLconfs moved with the requests that call them, and the Datastore
user-selection fixtures now carry the leading slash that request.path has.
2026-08-08 18:38:44 -07:00

204 lines
6.2 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { stripEnumMinLength, stripUuidFormat } from '../../scripts/lib/schema-transforms.mjs'
describe('stripEnumMinLength', () => {
it('removes minLength from a schema with enum', () => {
const schema = {
type: 'string',
enum: ['Cohort', 'FeatureFlag', 'Insight'],
minLength: 1,
}
stripEnumMinLength(schema)
expect(schema).not.toHaveProperty('minLength')
expect(schema).toHaveProperty('enum')
expect(schema).toHaveProperty('type')
})
it('preserves minLength on string schemas without enum', () => {
const schema = {
type: 'string',
minLength: 1,
}
stripEnumMinLength(schema)
expect(schema.minLength).toBe(1)
})
it('handles nested enum schemas in parameters', () => {
const spec = {
paths: {
'/v1/projects/{project_id}/activity_log/': {
get: {
parameters: [
{
in: 'query',
name: 'scope',
schema: {
type: 'string',
enum: ['Cohort', 'FeatureFlag'],
minLength: 1,
},
},
{
in: 'query',
name: 'item_id',
schema: {
type: 'string',
minLength: 1,
},
},
],
},
},
},
}
stripEnumMinLength(spec)
const params = spec.paths['/v1/projects/{project_id}/activity_log/'].get.parameters
expect(params[0]!.schema).not.toHaveProperty('minLength')
expect(params[0]!.schema.enum).toEqual(['Cohort', 'FeatureFlag'])
expect(params[1]!.schema.minLength).toBe(1)
})
it('handles array items with enum and minLength', () => {
const spec = {
paths: {
'/v1/things/': {
get: {
parameters: [
{
in: 'query',
name: 'scopes',
schema: {
type: 'array',
items: {
type: 'string',
enum: ['Cohort', 'FeatureFlag'],
minLength: 1,
},
},
},
],
},
},
},
}
stripEnumMinLength(spec)
const items = spec.paths['/v1/things/'].get.parameters[0]!.schema.items
expect(items).not.toHaveProperty('minLength')
expect(items.enum).toEqual(['Cohort', 'FeatureFlag'])
})
it('handles enum in component schemas', () => {
const spec = {
components: {
schemas: {
ScopeEnum: {
type: 'string',
enum: ['A', 'B', 'C'],
minLength: 1,
},
RegularString: {
type: 'string',
minLength: 3,
},
},
},
}
stripEnumMinLength(spec)
expect(spec.components.schemas.ScopeEnum).not.toHaveProperty('minLength')
expect(spec.components.schemas.RegularString.minLength).toBe(3)
})
it('is a no-op for null and undefined', () => {
expect(() => stripEnumMinLength(null)).not.toThrow()
expect(() => stripEnumMinLength(undefined)).not.toThrow()
})
it('is a no-op for empty objects', () => {
const obj = {}
stripEnumMinLength(obj)
expect(obj).toEqual({})
})
it('handles deeply nested structures', () => {
const spec = {
a: {
b: {
c: {
type: 'string',
enum: ['X', 'Y'],
minLength: 1,
},
},
},
}
stripEnumMinLength(spec)
expect(spec.a.b.c).not.toHaveProperty('minLength')
expect(spec.a.b.c.enum).toEqual(['X', 'Y'])
})
})
describe('stripUuidFormat', () => {
it('strips format from a scalar string uuid field', () => {
const schema = { type: 'string', format: 'uuid' }
stripUuidFormat(schema)
expect(schema).not.toHaveProperty('format')
expect(schema.type).toBe('string')
})
it('strips format from a nullable uuid field with array-form type', () => {
const schema = { type: ['string', 'null'], format: 'uuid' }
stripUuidFormat(schema)
expect(schema).not.toHaveProperty('format')
expect(schema.type).toEqual(['string', 'null'])
})
it('leaves non-uuid formats untouched', () => {
const schema = { type: 'string', format: 'date-time' }
stripUuidFormat(schema)
expect(schema.format).toBe('date-time')
})
it('strips uuid format from a nested nullable provider key field', () => {
const spec = {
components: {
schemas: {
ModelConfiguration: {
type: 'object',
properties: {
provider_key_id: { type: ['string', 'null'], format: 'uuid' },
},
},
},
},
}
stripUuidFormat(spec)
expect(spec.components.schemas.ModelConfiguration.properties.provider_key_id).not.toHaveProperty('format')
})
it('is a no-op for null and undefined', () => {
expect(() => stripUuidFormat(null)).not.toThrow()
expect(() => stripUuidFormat(undefined)).not.toThrow()
})
})