From 97ff27b14ee474600f3fa429ed0f2a2b03747a27 Mon Sep 17 00:00:00 2001 From: Artur Ciocanu Date: Sat, 16 Dec 2023 16:21:46 +0200 Subject: [PATCH] Adding tests --- src/generators/scala/ScalaRenderer.ts | 51 +- .../scala/presets/DescriptionPreset.ts | 58 +- test/TestUtils/TestRenderers.ts | 2 + test/generators/scala/Constants.spec.ts | 12 + .../generators/scala/ScalaConstrainer.spec.ts | 512 ++++++++++++++++++ test/generators/scala/ScalaGenerator.spec.ts | 211 ++++++++ test/generators/scala/ScalaRenderer.spec.ts | 52 ++ .../scala/presets/DescriptionPreset.spec.ts | 44 ++ 8 files changed, 927 insertions(+), 15 deletions(-) create mode 100644 test/generators/scala/Constants.spec.ts create mode 100644 test/generators/scala/ScalaConstrainer.spec.ts create mode 100644 test/generators/scala/ScalaGenerator.spec.ts create mode 100644 test/generators/scala/ScalaRenderer.spec.ts create mode 100644 test/generators/scala/presets/DescriptionPreset.spec.ts diff --git a/src/generators/scala/ScalaRenderer.ts b/src/generators/scala/ScalaRenderer.ts index 2ce8f1ebd2..c5120264bd 100644 --- a/src/generators/scala/ScalaRenderer.ts +++ b/src/generators/scala/ScalaRenderer.ts @@ -1,6 +1,7 @@ import { AbstractRenderer } from '../AbstractRenderer'; import { ScalaGenerator, ScalaOptions } from './ScalaGenerator'; import { ConstrainedMetaModel, InputMetaModel, Preset } from '../../models'; +import { FormatHelpers } from '../../helpers'; import { ScalaDependencyManager } from './ScalaDependencyManager'; /** @@ -10,11 +11,7 @@ import { ScalaDependencyManager } from './ScalaDependencyManager'; */ export abstract class ScalaRenderer< RendererModelType extends ConstrainedMetaModel -> extends AbstractRenderer< - ScalaOptions, - ScalaGenerator, - RendererModelType -> { +> extends AbstractRenderer { constructor( options: ScalaOptions, generator: ScalaGenerator, @@ -25,4 +22,48 @@ export abstract class ScalaRenderer< ) { super(options, generator, presets, model, inputModel); } + + renderComments(lines: string | string[]): string { + lines = FormatHelpers.breakLines(lines); + const newLiteral = lines.map((line) => ` * ${line}`).join('\n'); + return `/** +${newLiteral} + */`; + } + + renderAnnotation( + annotationName: string, + value?: any | Record + ): string { + const name = `@${annotationName}`; + + if (value === undefined || value === null) { + return name; + } + + if (typeof value !== 'object') { + return `${name}(${value})`; + } + + const entries = Object.entries(value || {}); + + if (entries.length === 0) { + return name; + } + + const values = concatenateEntries(entries); + return `${name}(${values})`; + } +} + +function concatenateEntries(entries: [string, unknown][] = []): string { + return entries + .map(([paramName, newValue]) => { + if (paramName && newValue !== undefined) { + return `${paramName}=${newValue}`; + } + return newValue; + }) + .filter((v) => v !== undefined) + .join(', '); } diff --git a/src/generators/scala/presets/DescriptionPreset.ts b/src/generators/scala/presets/DescriptionPreset.ts index 6169ab68c4..a7fb1c7ac2 100644 --- a/src/generators/scala/presets/DescriptionPreset.ts +++ b/src/generators/scala/presets/DescriptionPreset.ts @@ -1,4 +1,48 @@ +import { ScalaRenderer } from '../ScalaRenderer'; import { ScalaPreset } from '../ScalaPreset'; +import { FormatHelpers } from '../../../helpers'; +import { ConstrainedEnumModel, ConstrainedObjectModel } from '../../../models'; +function renderDescription({ + renderer, + content, + item +}: { + renderer: ScalaRenderer; + content: string; + item: ConstrainedObjectModel | ConstrainedEnumModel; +}): string { + if (!item.originalInput['description']) { + return content; + } + + let comment = `${item.originalInput['description']}`; + + if (item instanceof ConstrainedObjectModel) { + const properties = Object.keys(item.properties) + .map((key) => item.properties[`${key}`]) + .map((model) => { + const property = `@property ${model.propertyName}`; + const desc = model.property.originalInput['description']; + + return desc !== undefined ? `${property} ${desc}` : property; + }) + .join('\n'); + + comment += `\n\n${properties}`; + } + + const examples = Array.isArray(item.originalInput['examples']) + ? `Examples: \n${FormatHelpers.renderJSONExamples( + item.originalInput['examples'] + )}` + : null; + + if (examples !== null) { + comment += `\n\n${examples}`; + } + + return `${renderer.renderComments(comment)}\n${content}`; +} /** * Preset which adds description to rendered model. @@ -7,19 +51,13 @@ import { ScalaPreset } from '../ScalaPreset'; */ export const SCALA_DESCRIPTION_PRESET: ScalaPreset = { class: { - self({ content }) { - const renderedDesc = 'my description'; - return `${renderedDesc}\n${content}`; - }, - getter({ content }) { - const renderedDesc = 'my description'; - return `${renderedDesc}\n${content}`; + self({ renderer, model, content }) { + return renderDescription({ renderer, content, item: model }); } }, enum: { - self({ content }) { - const renderedDesc = 'my description'; - return `${renderedDesc}\n${content}`; + self({ renderer, model, content }) { + return renderDescription({ renderer, content, item: model }); } } }; diff --git a/test/TestUtils/TestRenderers.ts b/test/TestUtils/TestRenderers.ts index 56ca121ff8..063d71bf99 100644 --- a/test/TestUtils/TestRenderers.ts +++ b/test/TestUtils/TestRenderers.ts @@ -15,6 +15,7 @@ import { RustRenderer } from '../../src/generators/rust/RustRenderer'; import { PythonRenderer } from '../../src/generators/python/PythonRenderer'; import { KotlinRenderer } from '../../src/generators/kotlin/KotlinRenderer'; import { PhpRenderer } from '../../src/generators/php/PhpRenderer'; +import { ScalaRenderer } from '../../src/generators/scala/ScalaRenderer'; export class TestRenderer extends AbstractRenderer { constructor(presets = []) { @@ -43,3 +44,4 @@ export class MockRustRenderer extends RustRenderer {} export class MockPythonRenderer extends PythonRenderer {} export class MockKotlinRenderer extends KotlinRenderer {} export class MockPhpRenderer extends PhpRenderer {} +export class MockScalaRenderer extends ScalaRenderer {} diff --git a/test/generators/scala/Constants.spec.ts b/test/generators/scala/Constants.spec.ts new file mode 100644 index 0000000000..59da18ea3e --- /dev/null +++ b/test/generators/scala/Constants.spec.ts @@ -0,0 +1,12 @@ +import { isReservedScalaKeyword } from '../../../src/generators/scala/Constants'; + +describe('Reserved keywords', () => { + it('should return true if the word is a reserved keyword', () => { + expect(isReservedScalaKeyword('abstract')).toBe(true); + expect(isReservedScalaKeyword('type')).toBe(true); + }); + + it('should return false if the word is not a reserved keyword', () => { + expect(isReservedScalaKeyword('dinosaur')).toBe(false); + }); +}); diff --git a/test/generators/scala/ScalaConstrainer.spec.ts b/test/generators/scala/ScalaConstrainer.spec.ts new file mode 100644 index 0000000000..b4c1e411b9 --- /dev/null +++ b/test/generators/scala/ScalaConstrainer.spec.ts @@ -0,0 +1,512 @@ +import { ScalaDefaultTypeMapping } from '../../../src/generators/scala/ScalaConstrainer'; +import { ScalaGenerator, ScalaOptions } from '../../../src/generators/scala'; +import { + ConstrainedAnyModel, + ConstrainedArrayModel, + ConstrainedBooleanModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedEnumValueModel, + ConstrainedFloatModel, + ConstrainedIntegerModel, + ConstrainedObjectModel, + ConstrainedReferenceModel, + ConstrainedStringModel, + ConstrainedTupleModel, + ConstrainedTupleValueModel, + ConstrainedUnionModel +} from '../../../src'; +describe('ScalaConstrainer', () => { + describe('ObjectModel', () => { + test('should render the constrained name as type', () => { + const model = new ConstrainedObjectModel('test', undefined, {}, '', {}); + const type = ScalaDefaultTypeMapping.Object({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual(model.name); + }); + }); + describe('Reference', () => { + test('should render the constrained name as type', () => { + const refModel = new ConstrainedAnyModel('test', undefined, {}, ''); + const model = new ConstrainedReferenceModel( + 'test', + undefined, + {}, + '', + refModel + ); + const type = ScalaDefaultTypeMapping.Reference({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual(model.name); + }); + }); + describe('Any', () => { + test('should render type', () => { + const model = new ConstrainedAnyModel('test', undefined, {}, ''); + const type = ScalaDefaultTypeMapping.Any({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual(''); + }); + }); + describe('Float', () => { + test('should render type', () => { + const model = new ConstrainedFloatModel('test', undefined, {}, ''); + const type = ScalaDefaultTypeMapping.Float({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Double'); + }); + test('should render Float when format has number format', () => { + const model = new ConstrainedFloatModel( + 'test', + {}, + { format: 'float' }, + '' + ); + const type = ScalaDefaultTypeMapping.Float({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Float'); + }); + }); + describe('Integer', () => { + test('should render type', () => { + const model = new ConstrainedIntegerModel('test', undefined, {}, ''); + const type = ScalaDefaultTypeMapping.Integer({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Int'); + }); + test('should render Int when format has integer format', () => { + const model = new ConstrainedIntegerModel( + 'test', + {}, + { format: 'int32' }, + '' + ); + const type = ScalaDefaultTypeMapping.Integer({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Int'); + }); + test('should render Long when format has long format', () => { + const model = new ConstrainedIntegerModel( + 'test', + {}, + { format: 'long' }, + '' + ); + const type = ScalaDefaultTypeMapping.Integer({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Long'); + }); + test('should render Long when format has int64 format', () => { + const model = new ConstrainedIntegerModel( + 'test', + {}, + { format: 'int64' }, + '' + ); + const type = ScalaDefaultTypeMapping.Integer({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Long'); + }); + }); + describe('String', () => { + test('should render type', () => { + const model = new ConstrainedStringModel('test', undefined, {}, ''); + const type = ScalaDefaultTypeMapping.String({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('String'); + }); + test('should render LocalDate when format has date format', () => { + const model = new ConstrainedStringModel( + 'test', + {}, + { format: 'date' }, + '' + ); + const type = ScalaDefaultTypeMapping.String({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('java.time.LocalDate'); + }); + test('should render OffsetTime when format has time format', () => { + const model = new ConstrainedStringModel( + 'test', + {}, + { format: 'time' }, + '' + ); + const type = ScalaDefaultTypeMapping.String({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('java.time.OffsetTime'); + }); + test('should render OffsetDateTime when format has dateTime format', () => { + const model = new ConstrainedStringModel( + 'test', + {}, + { format: 'dateTime' }, + '' + ); + const type = ScalaDefaultTypeMapping.String({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('java.time.OffsetDateTime'); + }); + test('should render OffsetDateTime when format has date-time format', () => { + const model = new ConstrainedStringModel( + 'test', + {}, + { format: 'date-time' }, + '' + ); + const type = ScalaDefaultTypeMapping.String({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('java.time.OffsetDateTime'); + }); + test('should render byte when format has binary format', () => { + const model = new ConstrainedStringModel( + 'test', + {}, + { format: 'binary' }, + '' + ); + const type = ScalaDefaultTypeMapping.String({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('ByteArray'); + }); + }); + + describe('Boolean', () => { + test('should render type', () => { + const model = new ConstrainedBooleanModel('test', undefined, {}, ''); + const type = ScalaDefaultTypeMapping.Boolean({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Boolean'); + }); + }); + + describe('Tuple', () => { + test('should render type', () => { + const stringModel = new ConstrainedStringModel( + 'test', + undefined, + {}, + 'String' + ); + const tupleValueModel = new ConstrainedTupleValueModel(0, stringModel); + const model = new ConstrainedTupleModel('test', undefined, {}, '', [ + tupleValueModel + ]); + const type = ScalaDefaultTypeMapping.Tuple({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('List[Any]'); + }); + test('should render multiple tuple types', () => { + const stringModel = new ConstrainedStringModel( + 'test', + undefined, + {}, + 'String' + ); + const tupleValueModel0 = new ConstrainedTupleValueModel(0, stringModel); + const tupleValueModel1 = new ConstrainedTupleValueModel(1, stringModel); + const model = new ConstrainedTupleModel('test', undefined, {}, '', [ + tupleValueModel0, + tupleValueModel1 + ]); + const type = ScalaDefaultTypeMapping.Tuple({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('List[Any]'); + }); + }); + + describe('Array', () => { + test('should render type', () => { + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + {}, + 'String' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + {}, + '', + arrayModel + ); + const options: ScalaOptions = { + ...ScalaGenerator.defaultOptions, + collectionType: 'Array' + }; + const type = ScalaDefaultTypeMapping.Array({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Array[String]'); + }); + test('should render array as a list', () => { + const arrayModel = new ConstrainedStringModel( + 'test', + undefined, + {}, + 'String' + ); + const model = new ConstrainedArrayModel( + 'test', + undefined, + {}, + '', + arrayModel + ); + const options: ScalaOptions = { + ...ScalaGenerator.defaultOptions, + collectionType: 'List' + }; + const type = ScalaDefaultTypeMapping.Array({ + constrainedModel: model, + options, + dependencyManager: undefined as never + }); + expect(type).toEqual('List[String]'); + }); + }); + + describe('Enum', () => { + test('should render string enum values as String type', () => { + const enumValue = new ConstrainedEnumValueModel( + 'test', + 'string type', + {} + ); + const model = new ConstrainedEnumModel('test', undefined, {}, '', [ + enumValue + ]); + const type = ScalaDefaultTypeMapping.Enum({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('String'); + }); + test('should render boolean enum values as boolean type', () => { + const enumValue = new ConstrainedEnumValueModel('test', true, {}); + const model = new ConstrainedEnumModel('test', undefined, {}, '', [ + enumValue + ]); + const type = ScalaDefaultTypeMapping.Enum({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Boolean'); + }); + test('should render generic number enum value with format', () => { + const enumValue = new ConstrainedEnumValueModel('test', 123, {}); + const model = new ConstrainedEnumModel('test', undefined, {}, '', [ + enumValue + ]); + const type = ScalaDefaultTypeMapping.Enum({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Int'); + }); + test('should render generic number enum value with float format as float type', () => { + const enumValue = new ConstrainedEnumValueModel('test', 12.0, {}); + const model = new ConstrainedEnumModel( + 'test', + {}, + { format: 'float' }, + '', + [enumValue] + ); + const type = ScalaDefaultTypeMapping.Enum({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Float'); + }); + test('should render generic number enum value with double format as double type', () => { + const enumValue = new ConstrainedEnumValueModel('test', 12.0, {}); + const model = new ConstrainedEnumModel( + 'test', + {}, + { format: 'double' }, + '', + [enumValue] + ); + const type = ScalaDefaultTypeMapping.Enum({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Double'); + }); + test('should render object enum value as generic Object', () => { + const enumValue = new ConstrainedEnumValueModel('test', {}, {}); + const model = new ConstrainedEnumModel('test', undefined, {}, '', [ + enumValue + ]); + const type = ScalaDefaultTypeMapping.Enum({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Any'); + }); + test('should render multiple value types as generic Object', () => { + const enumValue2 = new ConstrainedEnumValueModel('test', true, {}); + const enumValue1 = new ConstrainedEnumValueModel( + 'test', + 'string type', + {} + ); + const model = new ConstrainedEnumModel('test', undefined, {}, '', [ + enumValue1, + enumValue2 + ]); + const type = ScalaDefaultTypeMapping.Enum({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Any'); + }); + test('should render double and integer as double type', () => { + const enumValue2 = new ConstrainedEnumValueModel('test', 123, {}); + const enumValue1 = new ConstrainedEnumValueModel('test', 123.12, {}); + const model = new ConstrainedEnumModel('test', undefined, {}, '', [ + enumValue1, + enumValue2 + ]); + const type = ScalaDefaultTypeMapping.Enum({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Double'); + }); + test('should render int and long as long type', () => { + const enumValue2 = new ConstrainedEnumValueModel('test', 123, {}); + const enumValue1 = new ConstrainedEnumValueModel('test', 123, {}); + const model = new ConstrainedEnumModel( + 'test', + {}, + { format: 'long' }, + '', + [enumValue1, enumValue2] + ); + const type = ScalaDefaultTypeMapping.Enum({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Long'); + }); + }); + + describe('Union', () => { + test('should render type', () => { + const unionModel = new ConstrainedStringModel( + 'test', + undefined, + {}, + 'str' + ); + const model = new ConstrainedUnionModel('test', undefined, {}, '', [ + unionModel + ]); + const type = ScalaDefaultTypeMapping.Union({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Any'); + }); + }); + + describe('Dictionary', () => { + test('should render type', () => { + const keyModel = new ConstrainedStringModel( + 'test', + undefined, + {}, + 'String' + ); + const valueModel = new ConstrainedStringModel( + 'test', + undefined, + {}, + 'String' + ); + const model = new ConstrainedDictionaryModel( + 'test', + undefined, + {}, + '', + keyModel, + valueModel + ); + const type = ScalaDefaultTypeMapping.Dictionary({ + constrainedModel: model, + options: ScalaGenerator.defaultOptions, + dependencyManager: undefined as never + }); + expect(type).toEqual('Map[String, String]'); + }); + }); +}); diff --git a/test/generators/scala/ScalaGenerator.spec.ts b/test/generators/scala/ScalaGenerator.spec.ts new file mode 100644 index 0000000000..4b3731d042 --- /dev/null +++ b/test/generators/scala/ScalaGenerator.spec.ts @@ -0,0 +1,211 @@ +import { ScalaGenerator } from '../../../src/generators/scala'; + +describe('ScalaGenerator', () => { + let generator: ScalaGenerator; + beforeEach(() => { + generator = new ScalaGenerator(); + }); + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('should not render reserved keyword', async () => { + const doc = { + $id: 'Address', + type: 'object', + properties: { + class: { type: 'string' } + }, + additionalProperties: false + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render `data class` type', async () => { + const doc = { + $id: 'Address', + type: 'object', + properties: { + street_name: { type: 'string' }, + city: { type: 'string', description: 'City description' }, + state: { type: 'string' }, + house_number: { type: 'number' }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + }, + date: { type: 'string', format: 'date' }, + time: { type: 'string', format: 'time' }, + dateTime: { type: 'string', format: 'date-time' }, + binary: { type: 'string', format: 'binary' } + }, + patternProperties: { + '^S(.?*)test&': { + type: 'string' + } + }, + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] + }; + + const expectedDependencies = []; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); + }); + + test('should render `enum class` type (string type)', async () => { + const doc = { + $id: 'States', + type: 'string', + enum: ['Texas', 'Alabama', 'California', 'New York'] + }; + const expectedDependencies = []; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + expect(models[0].dependencies).toEqual(expectedDependencies); + }); + + test('should render `enum` type (integer type)', async () => { + const doc = { + $id: 'Numbers', + type: 'integer', + enum: [0, 1, 2, 3] + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render `enum` type (union type)', async () => { + const doc = { + $id: 'Union', + type: ['string', 'integer', 'boolean'], + enum: ['Texas', 'Alabama', 0, 1, '1', true, { test: 'test' }] + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render enums with translated special characters', async () => { + const doc = { + $id: 'States', + enum: ['test+', 'test', 'test-', 'test?!', '*test'] + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render List type for collections', async () => { + const doc = { + $id: 'CustomClass', + type: 'object', + additionalProperties: false, + properties: { + arrayType: { + type: 'array', + items: { type: 'integer' }, + additionalItems: false + } + } + }; + + generator = new ScalaGenerator({ collectionType: 'List' }); + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render models and their dependencies', async () => { + const doc = { + $id: 'Address', + type: 'object', + properties: { + street_name: { type: 'string' }, + city: { type: 'string', description: 'City description' }, + state: { type: 'string' }, + house_number: { type: 'number' }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + }, + other_model: { + type: 'object', + $id: 'OtherModel', + properties: { street_name: { type: 'string' } } + } + }, + patternProperties: { + '^S(.?*)test&': { + type: 'string' + } + }, + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] + }; + const config = { packageName: 'test.package' }; + const models = await generator.generateCompleteModels(doc, config); + expect(models).toHaveLength(2); + expect(models[0].result).toMatchSnapshot(); + expect(models[1].result).toMatchSnapshot(); + }); + test('should escape reserved keywords in package name', async () => { + const doc = { + $id: 'Address', + type: 'object', + properties: { + street_name: { type: 'string' }, + city: { type: 'string', description: 'City description' }, + state: { type: 'string' }, + house_number: { type: 'number' }, + marriage: { + type: 'boolean', + description: 'Status if marriage live in given house' + }, + members: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }] + }, + array_type: { + type: 'array', + items: [{ type: 'string' }, { type: 'number' }] + } + }, + patternProperties: { + '^S(.?*)test&': { + type: 'string' + } + }, + required: ['street_name', 'city', 'state', 'house_number', 'array_type'] + }; + const config = { packageName: 'test.class.package' }; + const models = await generator.generateCompleteModels(doc, config); + + const expectedPackageDeclaration = 'package test.`class`.`package'; + expect(models[0].result).toContain(expectedPackageDeclaration); + }); +}); diff --git a/test/generators/scala/ScalaRenderer.spec.ts b/test/generators/scala/ScalaRenderer.spec.ts new file mode 100644 index 0000000000..75b85b94d2 --- /dev/null +++ b/test/generators/scala/ScalaRenderer.spec.ts @@ -0,0 +1,52 @@ +import { ScalaGenerator } from '../../../src/generators/scala'; +import { ScalaRenderer } from '../../../src/generators/scala/ScalaRenderer'; +import { ConstrainedObjectModel, InputMetaModel } from '../../../src/models'; +import { MockScalaRenderer } from '../../TestUtils/TestRenderers'; + +describe('ScalaRenderer', () => { + let renderer: ScalaRenderer; + beforeEach(() => { + renderer = new MockScalaRenderer( + ScalaGenerator.defaultOptions, + new ScalaGenerator(), + [], + new ConstrainedObjectModel('', undefined, '', {}), + new InputMetaModel() + ); + }); + + describe('renderComments()', () => { + test('Should be able to render comments', () => { + expect(renderer.renderComments('someComment')).toEqual(`/** + * someComment + */`); + }); + }); + + describe('renderAnnotation()', () => { + test('Should render', () => { + expect(renderer.renderAnnotation('someComment')).toEqual('@SomeComment'); + }); + test('Should be able to render multiple values', () => { + expect( + renderer.renderAnnotation('someComment', { test: 1, cool: '"story"' }) + ).toEqual('@SomeComment(test=1, cool="story")'); + }); + test('Should be able to render one value', () => { + expect( + renderer.renderAnnotation('someComment', { test: '"test2"' }) + ).toEqual('@SomeComment(test="test2")'); + }); + test('Should be able to use different prefixes', () => { + expect(renderer.renderAnnotation('someComment', null, 'get:')).toEqual( + '@get:SomeComment' + ); + expect(renderer.renderAnnotation('someComment', null, 'field:')).toEqual( + '@field:SomeComment' + ); + expect(renderer.renderAnnotation('someComment', null, 'param:')).toEqual( + '@param:SomeComment' + ); + }); + }); +}); diff --git a/test/generators/scala/presets/DescriptionPreset.spec.ts b/test/generators/scala/presets/DescriptionPreset.spec.ts new file mode 100644 index 0000000000..6d78deabf0 --- /dev/null +++ b/test/generators/scala/presets/DescriptionPreset.spec.ts @@ -0,0 +1,44 @@ +import { + ScalaGenerator, + SCALA_DESCRIPTION_PRESET +} from '../../../../src/generators/scala'; + +describe('SCALA_DESCRIPTION_PRESET', () => { + let generator: ScalaGenerator; + beforeEach(() => { + generator = new ScalaGenerator({ presets: [SCALA_DESCRIPTION_PRESET] }); + }); + + test('should render description and examples for class', async () => { + const doc = { + $id: 'Clazz', + type: 'object', + description: 'Description for class', + examples: [{ prop: 'value' }], + properties: { + prop: { + type: 'string', + description: 'Description for prop', + examples: ['exampleValue'] + } + } + }; + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); + + test('should render description and examples for enum', async () => { + const doc = { + $id: 'Enum', + type: 'string', + description: 'Description for enum', + examples: ['value'], + enum: ['on', 'off'] + }; + + const models = await generator.generate(doc); + expect(models).toHaveLength(1); + expect(models[0].result).toMatchSnapshot(); + }); +});