UNPKG

hey-api-builders

Version:

A custom plugin for @hey-api/openapi-ts that generates mock data builders with a lightweight custom runtime, Zod integration, or static mock generation.

723 lines 31.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const vitest_1 = require("vitest"); const schema_transformer_1 = require("./schema-transformer"); (0, vitest_1.describe)('Schema Transformer', () => { (0, vitest_1.describe)('irToSchema', () => { (0, vitest_1.it)('converts simple string schema', () => { const ir = { type: 'string' }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toEqual({ type: 'string' }); }); (0, vitest_1.it)('converts object schema with properties', () => { const ir = { type: 'object', properties: { name: { type: 'string' }, }, }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('type', 'object'); (0, vitest_1.expect)(result).toHaveProperty('properties'); }); (0, vitest_1.it)('handles required fields', () => { const ir = { type: 'object', properties: { name: { type: 'string' }, }, required: ['name'], }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('required'); (0, vitest_1.expect)(result.required).toEqual(['name']); }); (0, vitest_1.it)('handles $ref references', () => { const all = { User: { type: 'object', properties: { name: { type: 'string' } } }, }; const ir = { $ref: '#/components/schemas/User' }; const result = (0, schema_transformer_1.irToSchema)(ir, all); (0, vitest_1.expect)(result).toHaveProperty('type', 'object'); (0, vitest_1.expect)(result).toHaveProperty('properties'); }); (0, vitest_1.it)('handles nullable fields', () => { const ir = { type: 'string', nullable: true, }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result.type).toEqual(['string', 'null']); }); (0, vitest_1.it)('infers type from properties', () => { const ir = { properties: { name: { type: 'string' }, }, }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('type', 'object'); }); (0, vitest_1.it)('infers type from items', () => { const ir = { items: [{ type: 'string' }], }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('type', 'array'); }); (0, vitest_1.it)('handles array items', () => { const ir = { type: 'array', items: [{ type: 'string' }], }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('items'); }); (0, vitest_1.it)('handles additionalProperties boolean', () => { const ir = { type: 'object', additionalProperties: false, }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('additionalProperties', false); }); (0, vitest_1.it)('handles additionalProperties schema', () => { const ir = { type: 'object', additionalProperties: { type: 'string' }, }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('additionalProperties'); (0, vitest_1.expect)(result.additionalProperties).toHaveProperty('type', 'string'); }); (0, vitest_1.it)('copies format property', () => { const ir = { type: 'string', format: 'uuid', }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('format', 'uuid'); }); (0, vitest_1.it)('copies pattern property', () => { const ir = { type: 'string', pattern: '^[A-Z]+$', }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('pattern', '^[A-Z]+$'); }); (0, vitest_1.it)('copies min/max properties for strings', () => { const ir = { type: 'string', minLength: 5, maxLength: 100, }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('minLength', 5); (0, vitest_1.expect)(result).toHaveProperty('maxLength', 100); }); (0, vitest_1.it)('copies min/max properties for numbers', () => { const ir = { type: 'number', minimum: 0, maximum: 100, }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('minimum', 0); (0, vitest_1.expect)(result).toHaveProperty('maximum', 100); }); (0, vitest_1.it)('converts example to examples array', () => { const ir = { type: 'string', example: 'test-value', }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('examples'); (0, vitest_1.expect)(result.examples).toEqual(['test-value']); }); (0, vitest_1.it)('handles circular references without infinite loop', () => { const ir = { type: 'object' }; const seen = new Set(); seen.add(ir); const result = (0, schema_transformer_1.irToSchema)(ir, {}, seen); (0, vitest_1.expect)(result).toEqual({}); }); (0, vitest_1.it)('returns empty object for null input', () => { const result = (0, schema_transformer_1.irToSchema)(null, {}); (0, vitest_1.expect)(result).toEqual({}); }); (0, vitest_1.it)('returns empty object for non-object input', () => { const result = (0, schema_transformer_1.irToSchema)('string', {}); (0, vitest_1.expect)(result).toEqual({}); }); (0, vitest_1.it)('handles enum with type enum and items', () => { const ir = { type: 'enum', items: [{ const: 'value1' }, { const: 'value2' }, { const: 'value3' }], }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('anyOf'); (0, vitest_1.expect)(result.anyOf).toHaveLength(3); }); (0, vitest_1.it)('handles enum with items but no enum property', () => { const ir = { items: [{ const: 'red' }, { const: 'blue' }, { const: 'green' }], }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('anyOf'); (0, vitest_1.expect)(result.anyOf).toHaveLength(3); }); (0, vitest_1.it)('handles nullable arrays with union types', () => { const ir = { type: ['string', 'null'], nullable: true, }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result.type).toEqual(['string', 'null']); }); (0, vitest_1.it)('handles allOf composition', () => { const ir = { allOf: [ { type: 'object', properties: { a: { type: 'string' } } }, { type: 'object', properties: { b: { type: 'number' } } }, ], }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('allOf'); (0, vitest_1.expect)(result.allOf).toHaveLength(2); }); (0, vitest_1.it)('handles anyOf composition', () => { const ir = { anyOf: [{ type: 'string' }, { type: 'number' }], }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('anyOf'); (0, vitest_1.expect)(result.anyOf).toHaveLength(2); }); (0, vitest_1.it)('handles oneOf composition', () => { const ir = { oneOf: [{ type: 'boolean' }, { type: 'string' }], }; const result = (0, schema_transformer_1.irToSchema)(ir, {}); (0, vitest_1.expect)(result).toHaveProperty('oneOf'); (0, vitest_1.expect)(result.oneOf).toHaveLength(2); }); }); (0, vitest_1.describe)('normalizeSchema', () => { (0, vitest_1.it)('normalizes basic schema', () => { const schema = { type: 'string' }; const result = (0, schema_transformer_1.normalizeSchema)(schema); (0, vitest_1.expect)(result).toHaveProperty('type', 'string'); }); (0, vitest_1.it)('normalizes nested properties', () => { const schema = { type: 'object', properties: { nested: { type: 'string' }, }, }; const result = (0, schema_transformer_1.normalizeSchema)(schema); (0, vitest_1.expect)(result.properties).toBeDefined(); (0, vitest_1.expect)(result.properties?.nested).toHaveProperty('type', 'string'); }); (0, vitest_1.it)('normalizes enum type with items', () => { const schema = { type: 'enum', items: [{ const: 'value1' }, { const: 'value2' }], }; const result = (0, schema_transformer_1.normalizeSchema)(schema); (0, vitest_1.expect)(result.enum).toEqual(['value1', 'value2']); (0, vitest_1.expect)(result.type).toBe('string'); }); (0, vitest_1.it)('normalizes enum type with number items', () => { const schema = { type: 'enum', items: [{ const: 1 }, { const: 2 }, { const: 3 }], }; const result = (0, schema_transformer_1.normalizeSchema)(schema); (0, vitest_1.expect)(result.enum).toEqual([1, 2, 3]); (0, vitest_1.expect)(result.type).toBe('integer'); }); (0, vitest_1.it)('normalizes enum type with mixed types to string', () => { const schema = { type: 'enum', items: [{ const: 'text' }, { const: 123 }], }; const result = (0, schema_transformer_1.normalizeSchema)(schema); (0, vitest_1.expect)(result.enum).toEqual(['text', 123]); (0, vitest_1.expect)(result.type).toBe('string'); }); (0, vitest_1.it)('normalizes enum type with no valid items', () => { const schema = { type: 'enum', items: [], }; const result = (0, schema_transformer_1.normalizeSchema)(schema); (0, vitest_1.expect)(result.type).toBe('string'); }); (0, vitest_1.it)('normalizes array with enum items', () => { const schema = { type: 'object', properties: { values: { type: 'array', items: [ { type: 'enum', items: [{ const: 'a' }] }, { type: 'enum', items: [{ const: 'b' }] }, ], }, }, }; const result = (0, schema_transformer_1.normalizeSchema)(schema); (0, vitest_1.expect)(result.properties?.values).toBeDefined(); }); (0, vitest_1.it)('normalizes array with mixed type items to anyOf', () => { const schema = { type: 'array', items: [{ type: 'string' }, { type: 'number' }], }; const result = (0, schema_transformer_1.normalizeSchema)(schema); (0, vitest_1.expect)(result.anyOf).toBeDefined(); (0, vitest_1.expect)(result).not.toHaveProperty('type'); }); (0, vitest_1.it)('normalizes nested items in array', () => { const schema = { type: 'object', properties: { data: { type: 'array', items: { type: 'string' }, }, }, }; const result = (0, schema_transformer_1.normalizeSchema)(schema); (0, vitest_1.expect)(result.properties?.data).toBeDefined(); }); (0, vitest_1.it)('normalizes allOf schemas', () => { const schema = { type: 'object', allOf: [{ type: 'string' }], }; const result = (0, schema_transformer_1.normalizeSchema)(schema); (0, vitest_1.expect)(result.allOf).toBeDefined(); }); (0, vitest_1.it)('normalizes anyOf schemas', () => { const schema = { type: 'object', anyOf: [{ type: 'string' }], }; const result = (0, schema_transformer_1.normalizeSchema)(schema); (0, vitest_1.expect)(result.anyOf).toBeDefined(); }); (0, vitest_1.it)('normalizes oneOf schemas', () => { const schema = { type: 'object', oneOf: [{ type: 'string' }], }; const result = (0, schema_transformer_1.normalizeSchema)(schema); (0, vitest_1.expect)(result.oneOf).toBeDefined(); }); }); (0, vitest_1.describe)('sanitizeSchema', () => { (0, vitest_1.it)('removes enum type and sets appropriate type', () => { const schema = { type: 'enum', enum: ['value1', 'value2'], }; const result = (0, schema_transformer_1.sanitizeSchema)(schema); (0, vitest_1.expect)(result.type).toBe('string'); (0, vitest_1.expect)(result.enum).toEqual(['value1', 'value2']); }); (0, vitest_1.it)('removes IR.SchemaObject type', () => { const schema = { type: 'unknown' }; const result = (0, schema_transformer_1.sanitizeSchema)(schema); (0, vitest_1.expect)(result).not.toHaveProperty('type'); }); (0, vitest_1.it)('removes logicalOperator property', () => { const schema = { type: 'string', logicalOperator: 'AND', }; const result = (0, schema_transformer_1.sanitizeSchema)(schema); (0, vitest_1.expect)(result).not.toHaveProperty('logicalOperator'); }); (0, vitest_1.it)('sanitizes nested properties', () => { const schema = { type: 'object', properties: { field: { type: 'enum', enum: ['val'] }, }, }; const result = (0, schema_transformer_1.sanitizeSchema)(schema); (0, vitest_1.expect)(result.properties?.field).not.toHaveProperty('type', 'enum'); }); (0, vitest_1.it)('sanitizes arrays', () => { const schema = { type: 'array', items: { type: 'unknown' }, }; const result = (0, schema_transformer_1.sanitizeSchema)(schema); (0, vitest_1.expect)(result.items).toBeDefined(); }); (0, vitest_1.it)('sets type to string when enum exists without type', () => { const schema = { enum: ['a', 'b', 'c'], }; const result = (0, schema_transformer_1.sanitizeSchema)(schema); (0, vitest_1.expect)(result.type).toBe('string'); }); (0, vitest_1.it)('sanitizes additionalProperties', () => { const schema = { type: 'object', additionalProperties: { type: 'object', }, }; const result = (0, schema_transformer_1.sanitizeSchema)(schema); (0, vitest_1.expect)(result.additionalProperties).toBeDefined(); }); (0, vitest_1.it)('sanitizes array items when items is array', () => { const schema = { type: 'array', items: [{ type: 'enum', enum: ['val1'] }, { type: 'string' }], }; const result = (0, schema_transformer_1.sanitizeSchema)(schema); (0, vitest_1.expect)(result.items).toBeDefined(); if (Array.isArray(result.items)) { (0, vitest_1.expect)(result.items[0]).not.toHaveProperty('type', 'enum'); } }); (0, vitest_1.it)('sanitizes allOf schemas', () => { const schema = { type: 'object', allOf: [{ type: 'enum', enum: ['val'] }], }; const result = (0, schema_transformer_1.sanitizeSchema)(schema); (0, vitest_1.expect)(result.allOf).toBeDefined(); }); (0, vitest_1.it)('sanitizes anyOf schemas', () => { const schema = { type: 'object', anyOf: [{ type: 'string' }], }; const result = (0, schema_transformer_1.sanitizeSchema)(schema); (0, vitest_1.expect)(result.anyOf).toBeDefined(); }); (0, vitest_1.it)('sanitizes oneOf schemas', () => { const schema = { type: 'object', oneOf: [{ type: 'enum', enum: ['x'] }], }; const result = (0, schema_transformer_1.sanitizeSchema)(schema); (0, vitest_1.expect)(result.oneOf).toBeDefined(); }); }); (0, vitest_1.describe)('collectSchemas', () => { (0, vitest_1.it)('collects and processes multiple schemas', () => { const schemas = { UserSchema: { type: 'object', properties: { name: { type: 'string' } }, }, ProductSchema: { type: 'object', properties: { price: { type: 'number' } }, }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result).toHaveLength(2); (0, vitest_1.expect)(result[0].typeName).toBe('User'); (0, vitest_1.expect)(result[1].typeName).toBe('Product'); }); (0, vitest_1.it)('removes Schema suffix from type names', () => { const schemas = { UserSchema: { type: 'object' }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].typeName).toBe('User'); }); (0, vitest_1.it)('normalizes type names', () => { const schemas = { UITheme: { type: 'object' }, APIKey: { type: 'object' }, turnaround_data_source: { type: 'string' }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].typeName).toBe('UiTheme'); (0, vitest_1.expect)(result[1].typeName).toBe('ApiKey'); (0, vitest_1.expect)(result[2].typeName).toBe('TurnaroundDataSource'); }); (0, vitest_1.it)('identifies object types correctly', () => { const schemas = { ObjectType: { type: 'object' }, StringType: { type: 'string' }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].isObject).toBe(true); (0, vitest_1.expect)(result[1].isObject).toBe(false); }); (0, vitest_1.it)('generates safe constant names', () => { const schemas = { 'My-Schema': { type: 'object' }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].constName).toMatch(/Schema$/); (0, vitest_1.expect)(result[0].constName).not.toContain('-'); }); (0, vitest_1.it)('handles schemas with $ref', () => { const schemas = { User: { type: 'object', properties: { address: { $ref: '#/components/schemas/Address' }, }, }, Address: { type: 'object' }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result.length).toBe(2); }); (0, vitest_1.it)('handles schemas with additionalProperties', () => { const schemas = { DynamicObject: { type: 'object', additionalProperties: { type: 'string' }, }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result.length).toBe(1); (0, vitest_1.expect)(result[0].schema.additionalProperties).toBeDefined(); }); (0, vitest_1.it)('handles schemas with required array', () => { const schemas = { User: { type: 'object', properties: { id: { type: 'number' }, name: { type: 'string' }, }, required: ['id'], }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].schema.required).toEqual(['id']); }); (0, vitest_1.it)('handles oneOf schemas', () => { const schemas = { Mixed: { oneOf: [{ type: 'string' }, { type: 'number' }], }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].schema.oneOf).toBeDefined(); }); (0, vitest_1.it)('handles anyOf schemas', () => { const schemas = { Flexible: { anyOf: [{ type: 'string' }, { type: 'boolean' }], }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].schema.anyOf).toBeDefined(); }); (0, vitest_1.it)('handles allOf schemas', () => { const schemas = { Combined: { allOf: [ { type: 'object', properties: { a: { type: 'string' } } }, { type: 'object', properties: { b: { type: 'number' } } }, ], }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].schema.allOf).toBeDefined(); }); (0, vitest_1.it)('handles array schemas with min/max items', () => { const schemas = { LimitedArray: { type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 10, }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].schema.minItems).toBe(1); (0, vitest_1.expect)(result[0].schema.maxItems).toBe(10); }); (0, vitest_1.it)('handles number schemas with constraints', () => { const schemas = { ConstrainedNumber: { type: 'number', minimum: 0, maximum: 100, multipleOf: 5, }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0]).toBeDefined(); (0, vitest_1.expect)(result[0].typeName).toBe('ConstrainedNumber'); }); (0, vitest_1.it)('handles string schemas with format and pattern', () => { const schemas = { FormattedString: { type: 'string', format: 'email', pattern: '^[a-z]+@[a-z]+\\.[a-z]+$', minLength: 5, maxLength: 50, }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0]).toBeDefined(); (0, vitest_1.expect)(result[0].typeName).toBe('FormattedString'); }); (0, vitest_1.it)('handles nested object schemas', () => { const schemas = { Nested: { type: 'object', properties: { inner: { type: 'object', properties: { value: { type: 'string' }, }, }, }, }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].schema.properties?.inner).toBeDefined(); }); (0, vitest_1.it)('handles schemas with description and title', () => { const schemas = { Documented: { type: 'object', title: 'Documented Schema', description: 'A well-documented schema', }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].schema.title).toBe('Documented Schema'); (0, vitest_1.expect)(result[0].schema.description).toBe('A well-documented schema'); }); (0, vitest_1.it)('handles schemas with nullable', () => { const schemas = { Nullable: { type: 'string', nullable: true, }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0]).toBeDefined(); (0, vitest_1.expect)(result[0].typeName).toBe('Nullable'); }); (0, vitest_1.it)('handles schemas with deprecated flag', () => { const schemas = { Old: { type: 'string', deprecated: true, }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].schema.deprecated).toBe(true); }); (0, vitest_1.it)('handles schemas with readOnly flag', () => { const schemas = { ReadOnly: { type: 'object', properties: { id: { type: 'number', readOnly: true }, }, }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].schema.properties?.id.readOnly).toBe(true); }); (0, vitest_1.it)('handles schemas with writeOnly flag', () => { const schemas = { WriteOnly: { type: 'object', properties: { password: { type: 'string', writeOnly: true }, }, }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].schema.properties?.password.writeOnly).toBe(true); }); (0, vitest_1.it)('handles schemas with examples', () => { const schemas = { WithExamples: { type: 'string', examples: ['example1', 'example2'], }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0]).toBeDefined(); (0, vitest_1.expect)(result[0].typeName).toBe('WithExamples'); }); (0, vitest_1.it)('handles schemas with default values', () => { const schemas = { WithDefault: { type: 'string', default: 'default-value', }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0]).toBeDefined(); (0, vitest_1.expect)(result[0].typeName).toBe('WithDefault'); }); (0, vitest_1.it)('handles schemas with const values', () => { const schemas = { Constant: { type: 'string', const: 'fixed', }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0]).toBeDefined(); (0, vitest_1.expect)(result[0].typeName).toBe('Constant'); }); (0, vitest_1.it)('handles complex nested structures', () => { const schemas = { Complex: { type: 'object', properties: { users: { type: 'array', items: { type: 'object', properties: { name: { type: 'string' }, roles: { type: 'array', items: { type: 'string', enum: ['admin', 'user', 'guest'], }, }, }, }, }, }, }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].schema.properties?.users).toBeDefined(); }); (0, vitest_1.it)('handles schemas with exclusive minimum and maximum', () => { const schemas = { Exclusive: { type: 'number', exclusiveMinimum: 0, exclusiveMaximum: 100, }, }; const result = (0, schema_transformer_1.collectSchemas)(schemas); (0, vitest_1.expect)(result[0].schema.exclusiveMinimum).toBe(0); (0, vitest_1.expect)(result[0].schema.exclusiveMaximum).toBe(100); }); }); }); //# sourceMappingURL=schema-transformer.test.js.map