Repository: asyncapi/modelina
Pull request #797 (2022-07-05 13:37:55)

chore: fix enum model conversion
**Description**
This PR fixes enum models are not created properly from CommonModel by moving all the primitive meta models to be converted last.

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=797)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=797&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=797&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=797&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=797&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=797&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=797&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=797&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=797&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=797&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=797&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=797&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=797&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=797) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=797&metric=duplicated_lines_density&view=list) No Duplication information



Patch:
diff --git a/src/helpers/CommonModelToMetaModel.ts b/src/helpers/CommonModelToMetaModel.ts
index 9c08be96..f7ddf81c 100644
--- a/src/helpers/CommonModelToMetaModel.ts
+++ b/src/helpers/CommonModelToMetaModel.ts
@@ -12,26 +12,10 @@ export function convertToMetaModel(jsonSchemaModel: CommonModel): MetaModel {
   if (unionModel !== undefined) {
     return unionModel;
   }
-  const stringModel = convertToStringModel(jsonSchemaModel, modelName);
-  if (stringModel !== undefined) {
-    return stringModel;
-  }
-  const floatModel = convertToFloatModel(jsonSchemaModel, modelName);
-  if (floatModel !== undefined) {
-    return floatModel;
-  }
-  const integerModel = convertToIntegerModel(jsonSchemaModel, modelName);
-  if (integerModel !== undefined) {
-    return integerModel;
-  }
   const enumModel = convertToEnumModel(jsonSchemaModel, modelName);
   if (enumModel !== undefined) {
     return enumModel;
   }
-  const booleanModel = convertToBooleanModel(jsonSchemaModel, modelName);
-  if (booleanModel !== undefined) {
-    return booleanModel;
-  }
   const objectModel = convertToObjectModel(jsonSchemaModel, modelName);
   if (objectModel !== undefined) {
     return objectModel;
@@ -44,6 +28,22 @@ export function convertToMetaModel(jsonSchemaModel: CommonModel): MetaModel {
   if (arrayModel !== undefined) {
     return arrayModel;
   }
+  const stringModel = convertToStringModel(jsonSchemaModel, modelName);
+  if (stringModel !== undefined) {
+    return stringModel;
+  }
+  const floatModel = convertToFloatModel(jsonSchemaModel, modelName);
+  if (floatModel !== undefined) {
+    return floatModel;
+  }
+  const integerModel = convertToIntegerModel(jsonSchemaModel, modelName);
+  if (integerModel !== undefined) {
+    return integerModel;
+  }
+  const booleanModel = convertToBooleanModel(jsonSchemaModel, modelName);
+  if (booleanModel !== undefined) {
+    return booleanModel;
+  }
   throw new Error('Failed to convert to MetaModel');
 }
 export function convertToUnionModel(jsonSchemaModel: CommonModel, name: string): UnionModel | undefined {
@@ -52,26 +52,10 @@ export function convertToUnionModel(jsonSchemaModel: CommonModel, name: string):
   }
   // Has multiple types, so convert to union
   const unionModel = new UnionModel(name, jsonSchemaModel.originalInput, []);
-  const stringModel = convertToStringModel(jsonSchemaModel, name);
-  if (stringModel !== undefined) {
-    unionModel.union.push(stringModel);
-  }
-  const floatModel = convertToFloatModel(jsonSchemaModel, name);
-  if (floatModel !== undefined) {
-    unionModel.union.push(floatModel);
-  }
-  const integerModel = convertToIntegerModel(jsonSchemaModel, name);
-  if (integerModel !== undefined) {
-    unionModel.union.push(integerModel);
-  }
   const enumModel = convertToEnumModel(jsonSchemaModel, name);
   if (enumModel !== undefined) {
     unionModel.union.push(enumModel);
   }
-  const booleanModel = convertToBooleanModel(jsonSchemaModel, name);
-  if (booleanModel !== undefined) {
-    unionModel.union.push(booleanModel);
-  }
   const objectModel = convertToObjectModel(jsonSchemaModel, name);
   if (objectModel !== undefined) {
     unionModel.union.push(objectModel);
@@ -84,6 +68,22 @@ export function convertToUnionModel(jsonSchemaModel: CommonModel, name: string):
   if (arrayModel !== undefined) {
     unionModel.union.push(arrayModel);
   }
+  const stringModel = convertToStringModel(jsonSchemaModel, name);
+  if (stringModel !== undefined) {
+    unionModel.union.push(stringModel);
+  }
+  const floatModel = convertToFloatModel(jsonSchemaModel, name);
+  if (floatModel !== undefined) {
+    unionModel.union.push(floatModel);
+  }
+  const integerModel = convertToIntegerModel(jsonSchemaModel, name);
+  if (integerModel !== undefined) {
+    unionModel.union.push(integerModel);
+  }
+  const booleanModel = convertToBooleanModel(jsonSchemaModel, name);
+  if (booleanModel !== undefined) {
+    unionModel.union.push(booleanModel);
+  }
   return unionModel;
 }
 export function convertToStringModel(jsonSchemaModel: CommonModel, name: string): StringModel | undefined {
diff --git a/src/helpers/ConstrainHelpers.ts b/src/helpers/ConstrainHelpers.ts
index d6729b17..02aa9d26 100644
--- a/src/helpers/ConstrainHelpers.ts
+++ b/src/helpers/ConstrainHelpers.ts
@@ -149,7 +149,7 @@ function constrainDictionaryModel<Options>(typeMapping: TypeMapping<Options>, co
 function constrainObjectModel<Options>(typeMapping: TypeMapping<Options>, constrainRules: Constraints, context: ConstrainContext<Options, ObjectModel>): ConstrainedObjectModel {
   const constrainedModel = new ConstrainedObjectModel(context.constrainedName, context.metaModel.originalInput, '', {});
   for (const propertyMetaModel of Object.values(context.metaModel.properties)) {
-    const constrainedPropertyModel = new ConstrainedObjectPropertyModel('', propertyMetaModel.required, constrainedModel);
+    const constrainedPropertyModel = new ConstrainedObjectPropertyModel('', propertyMetaModel.propertyName, propertyMetaModel.required, constrainedModel, propertyMetaModel);
     const constrainedPropertyName = constrainRules.propertyKey({objectPropertyModel: propertyMetaModel, constrainedObjectPropertyModel: constrainedPropertyModel, constrainedObjectModel: constrainedModel, objectModel: context.metaModel});
     constrainedPropertyModel.propertyName = constrainedPropertyName;
     const constrainedProperty = constrainMetaModel(typeMapping, constrainRules, {...context, metaModel: propertyMetaModel.property, propertyKey: constrainedPropertyName});
diff --git a/src/helpers/NameHelpers.ts b/src/helpers/NameHelpers.ts
deleted file mode 100644
index 6d49ca6c..00000000
--- a/src/helpers/NameHelpers.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { ObjectModel } from '../models';
-
-/**
- * Recursively find the proper property name.
- * 
- * This function ensures that the property name is unique for the model
- * 
- * @param rootModel 
- * @param propertyName 
- */
-export function getUniquePropertyName(rootModel: ObjectModel, proposedPropertyName: string): string {
-  if (Object.keys(rootModel.properties || {}).includes(proposedPropertyName)) {
-    return getUniquePropertyName(rootModel, `reserved_${proposedPropertyName}`);
-  }
-  return proposedPropertyName;
-}
-
diff --git a/src/helpers/index.ts b/src/helpers/index.ts
index 6770022a..c3ae019f 100644
--- a/src/helpers/index.ts
+++ b/src/helpers/index.ts
@@ -1,6 +1,5 @@
 export * from './FormatHelpers';
 export * from './TypeHelpers';
-export * from './NameHelpers';
 export * from './FileHelpers';
 export * from './CommonModelToMetaModel';
 export * from './Splitter';
diff --git a/src/models/ConstrainedMetaModel.ts b/src/models/ConstrainedMetaModel.ts
index 5cb99d1a..38ff6e25 100644
--- a/src/models/ConstrainedMetaModel.ts
+++ b/src/models/ConstrainedMetaModel.ts
@@ -1,4 +1,4 @@
-import { MetaModel } from './MetaModel';
+import { MetaModel, ObjectPropertyModel } from './MetaModel';
 
 export abstract class ConstrainedMetaModel extends MetaModel {
   constructor(
@@ -13,7 +13,9 @@ export abstract class ConstrainedMetaModel extends MetaModel {
    * 
    * This is often used when you want to know which other models you are referencing.
    */
-  abstract getNearestDependencies(): ConstrainedMetaModel[]
+  getNearestDependencies(): ConstrainedMetaModel[] {
+    return [];
+  }
 }
 
 export class ConstrainedReferenceModel extends ConstrainedMetaModel {
@@ -24,35 +26,12 @@ export class ConstrainedReferenceModel extends ConstrainedMetaModel {
     public ref: ConstrainedMetaModel) {
     super(name, originalInput, type);
   }
-  getNearestDependencies(): ConstrainedMetaModel[] {
-    return [];
-  }
-}
-export class ConstrainedAnyModel extends ConstrainedMetaModel {
-  getNearestDependencies(): ConstrainedMetaModel[] {
-    return [];
-  }
-}
-export class ConstrainedFloatModel extends ConstrainedMetaModel {
-  getNearestDependencies(): ConstrainedMetaModel[] {
-    return [];
-  }
-}
-export class ConstrainedIntegerModel extends ConstrainedMetaModel {
-  getNearestDependencies(): ConstrainedMetaModel[] {
-    return [];
-  }
-}
-export class ConstrainedStringModel extends ConstrainedMetaModel {
-  getNearestDependencies(): ConstrainedMetaModel[] {
-    return [];
-  }
-}
-export class ConstrainedBooleanModel extends ConstrainedMetaModel {
-  getNearestDependencies(): ConstrainedMetaModel[] {
-    return [];
-  }
 }
+export class ConstrainedAnyModel extends ConstrainedMetaModel { }
+export class ConstrainedFloatModel extends ConstrainedMetaModel { }
+export class ConstrainedIntegerModel extends ConstrainedMetaModel { }
+export class ConstrainedStringModel extends ConstrainedMetaModel { }
+export class ConstrainedBooleanModel extends ConstrainedMetaModel { }
 export class ConstrainedTupleValueModel {
   constructor(
     public index: number, 
@@ -64,21 +43,43 @@ export class ConstrainedTupleModel extends ConstrainedMetaModel {
     name: string,
     originalInput: any, 
     type: string, 
-  public tuple: ConstrainedTupleValueModel[]) {
+    public tuple: ConstrainedTupleValueModel[]) {
     super(name, originalInput, type);
   }
+
   getNearestDependencies(): ConstrainedMetaModel[] {
-    return [];
+    let dependencyModels = Object.values(this.tuple).filter(
+      (tupleModel) => {
+        return tupleModel.value instanceof ConstrainedReferenceModel;
+      }
+    ).map((tupleModel) => {
+      return tupleModel.value = tupleModel.value as ConstrainedReferenceModel;
+    });
+    //Ensure no self references
+    dependencyModels = dependencyModels.filter((referenceModel) => {
+      return referenceModel.name !== this.name;
+    });
+    return dependencyModels;
   }
 }
 export class ConstrainedObjectPropertyModel {
   constructor(
     public propertyName: string,
+    public unconstrainedPropertyName: string,
     public required: boolean,
-    public property: ConstrainedMetaModel) {
+    public property: ConstrainedMetaModel,
+    public rawMetaModel: ObjectPropertyModel) {
   }
 }
 export class ConstrainedArrayModel extends ConstrainedMetaModel {
+  constructor(
+    name: string,
+    originalInput: any, 
+    type: string, 
+    public valueModel: ConstrainedMetaModel) {
+    super(name, originalInput, type);
+  }
+
   getNearestDependencies(): ConstrainedMetaModel[] {
     if (this.valueModel instanceof ConstrainedReferenceModel &&
       this.valueModel.name !== this.name) {
@@ -86,15 +87,16 @@ export class ConstrainedArrayModel extends ConstrainedMetaModel {
     }
     return [];
   }
+}
+export class ConstrainedUnionModel extends ConstrainedMetaModel {
   constructor(
     name: string,
     originalInput: any, 
     type: string, 
-    public valueModel: ConstrainedMetaModel) {
+    public union: ConstrainedMetaModel[]) {
     super(name, originalInput, type);
   }
-}
-export class ConstrainedUnionModel extends ConstrainedMetaModel {
+
   getNearestDependencies(): ConstrainedReferenceModel[] {
     let dependencyModels = Object.values(this.union).filter(
       (unionModel) => {
@@ -109,13 +111,6 @@ export class ConstrainedUnionModel extends ConstrainedMetaModel {
     });
     return dependencyModels;
   }
-  constructor(
-    name: string,
-    originalInput: any, 
-    type: string, 
-    public union: ConstrainedMetaModel[]) {
-    super(name, originalInput, type);
-  }
 }
 export class ConstrainedEnumValueModel {
   constructor(
@@ -124,6 +119,14 @@ export class ConstrainedEnumValueModel {
   }
 }
 export class ConstrainedEnumModel extends ConstrainedMetaModel {
+  constructor(
+    name: string,
+    originalInput: any, 
+    type: string, 
+    public values: ConstrainedEnumValueModel[]) {
+    super(name, originalInput, type);
+  }
+
   getNearestDependencies(): ConstrainedReferenceModel[] {
     let dependencyModels = Object.values(this.values).filter(
       (enumModel) => {
@@ -138,18 +141,8 @@ export class ConstrainedEnumModel extends ConstrainedMetaModel {
     });
     return dependencyModels;
   }
-  constructor(
-    name: string,
-    originalInput: any, 
-    type: string, 
-    public values: ConstrainedEnumValueModel[]) {
-    super(name, originalInput, type);
-  }
 }
 export class ConstrainedDictionaryModel extends ConstrainedMetaModel {
-  getNearestDependencies(): ConstrainedMetaModel[] {
-    return [];
-  }
   constructor(
     name: string,
     originalInput: any, 
@@ -159,6 +152,22 @@ export class ConstrainedDictionaryModel extends ConstrainedMetaModel {
     public serializationType: 'unwrap' | 'normal' = 'normal') {
     super(name, originalInput, type);
   }
+
+  getNearestDependencies(): ConstrainedMetaModel[] {
+    const dependencies = [this.key, this.value];
+    let dependencyModels = dependencies.filter(
+      (model) => {
+        return model instanceof ConstrainedReferenceModel;
+      }
+    ).map((model) => {
+      return model as ConstrainedReferenceModel;
+    });
+    //Ensure no self references
+    dependencyModels = dependencyModels.filter((referenceModel) => {
+      return referenceModel.name !== this.name;
+    });
+    return dependencyModels;
+  }
 }
 
 export class ConstrainedObjectModel extends ConstrainedMetaModel {
@@ -166,9 +175,10 @@ export class ConstrainedObjectModel extends ConstrainedMetaModel {
     name: string,
     originalInput: any, 
     type: string,
-  public properties: { [key: string]: ConstrainedObjectPropertyModel; }) {
+    public properties: { [key: string]: ConstrainedObjectPropertyModel; }) {
     super(name, originalInput, type);
   }
+
   getNearestDependencies(): ConstrainedReferenceModel[] {
     let dependencyModels = Object.values(this.properties).filter(
       (modelProperty) => {


Test patch:
diff --git a/test/TestUtils/TestConstrainer.ts b/test/TestUtils/TestConstrainer.ts
new file mode 100644
index 00000000..7f5c2346
--- /dev/null
+++ b/test/TestUtils/TestConstrainer.ts
@@ -0,0 +1,24 @@
+/* eslint-disable no-undef */
+import { Constraints, TypeMapping } from '../../src/helpers';
+
+export const mockedTypeMapping: TypeMapping<any> = {
+  Object: jest.fn().mockReturnValue('test'),
+  Reference: jest.fn().mockReturnValue('test'),
+  Any: jest.fn().mockReturnValue('test'),
+  Float: jest.fn().mockReturnValue('test'),
+  Integer: jest.fn().mockReturnValue('test'),
+  String: jest.fn().mockReturnValue('test'),
+  Boolean: jest.fn().mockReturnValue('test'),
+  Tuple: jest.fn().mockReturnValue('test'),
+  Array: jest.fn().mockReturnValue('test'),
+  Enum: jest.fn().mockReturnValue('test'),
+  Union: jest.fn().mockReturnValue('test'),
+  Dictionary: jest.fn().mockReturnValue('test'),
+};
+
+export const mockedConstraints: Constraints = {
+  enumKey: jest.fn().mockImplementation(({enumKey}) => enumKey),
+  enumValue: jest.fn().mockImplementation(({enumValue}) => enumValue),
+  modelName: jest.fn().mockImplementation(({modelName}) => modelName),
+  propertyKey: jest.fn().mockImplementation(({objectPropertyModel}) => objectPropertyModel.propertyName)
+};
diff --git a/test/TestUtils/TestGenerator.ts b/test/TestUtils/TestGenerator.ts
index 8feb1131..50958370 100644
--- a/test/TestUtils/TestGenerator.ts
+++ b/test/TestUtils/TestGenerator.ts
@@ -1,5 +1,5 @@
 /* eslint-disable @typescript-eslint/no-unused-vars */
-import { AbstractGenerator, CommonInputModel, CommonModel, IndentationTypes, RenderOutput } from '../../src';
+import { AbstractGenerator, InputMetaModel, IndentationTypes, RenderOutput, ConstrainedMetaModel, MetaModel, ConstrainedAnyModel, Preset } from '../../src';
 
 export const testOptions = {
   indentation: {
@@ -7,16 +7,24 @@ export const testOptions = {
     size: 2,
   }
 };
-export class TestGenerator extends AbstractGenerator {
-  constructor() {
-    super('TestGenerator', testOptions);
+export class TestGenerator extends AbstractGenerator<any, any> {
+  constructor(options: any = testOptions) {
+    super('TestGenerator', options);
   }
 
-  render(model: CommonModel, _inputModel: CommonInputModel): Promise<RenderOutput> {
-    return Promise.resolve(RenderOutput.toRenderOutput({result: model.$id || 'rendered content', renderedName: 'TestName'}));
+  public constrainToMetaModel(model: MetaModel): ConstrainedMetaModel {
+    return new ConstrainedAnyModel(model.name, undefined, '');
   }
-
-  renderCompleteModel(_model: CommonModel, _inputModel: CommonInputModel, _options: any): Promise<RenderOutput> {
-    return Promise.resolve(RenderOutput.toRenderOutput({result: 'rendered complete content', renderedName: 'TestName'}));
+  public splitMetaModel(model: MetaModel): MetaModel[] {
+    return [model];
+  }
+  public render(model: MetaModel, inputModel: InputMetaModel): Promise<RenderOutput> {
+    return Promise.resolve(RenderOutput.toRenderOutput({result: model.name || 'rendered content', renderedName: 'TestName'}));
+  }
+  public renderCompleteModel(model: MetaModel, inputModel: InputMetaModel, options: any): Promise<RenderOutput> {
+    return Promise.resolve(RenderOutput.toRenderOutput({result: model.name || 'rendered complete content', renderedName: 'TestName'}));
+  }
+  public testGetPresets(string: string): Array<[Preset, unknown]> {
+    return this.getPresets(string);
   }
 }
diff --git a/test/TestUtils/TestRenderers.ts b/test/TestUtils/TestRenderers.ts
index 1a3e4d32..9c5e73c0 100644
--- a/test/TestUtils/TestRenderers.ts
+++ b/test/TestUtils/TestRenderers.ts
@@ -1,22 +1,24 @@
-import { AbstractRenderer, CommonInputModel, CommonModel, RenderOutput } from '../../src';
+import { AbstractRenderer, InputMetaModel, RenderOutput, ConstrainedAnyModel } from '../../src';
 import { GoRenderer } from '../../src/generators/go/GoRenderer';
 import { CSharpRenderer } from '../../src/generators/csharp/CSharpRenderer';
 import { JavaRenderer } from '../../src/generators/java/JavaRenderer';
 import { JavaScriptRenderer } from '../../src/generators/javascript/JavaScriptRenderer';
 import { TypeScriptRenderer } from '../../src/generators/typescript/TypeScriptRenderer';
 import {testOptions, TestGenerator} from './TestGenerator';
+import { DartRenderer } from '../../src/generators/dart/DartRenderer';
 
 export class TestRenderer extends AbstractRenderer {
   constructor(presets = []) {
-    super(testOptions, new TestGenerator(), presets, new CommonModel(), new CommonInputModel());
+    super(testOptions, new TestGenerator(), presets, new ConstrainedAnyModel('', undefined, ''), new InputMetaModel());
   }
   render(): Promise<RenderOutput> {
     return Promise.resolve(RenderOutput.toRenderOutput({result: '', renderedName: ''}));
   }
 }
 
-export class MockJavaRenderer extends JavaRenderer {}
-export class MockTypeScriptRenderer extends TypeScriptRenderer {}
-export class MockGoRenderer extends GoRenderer {}
-export class MockCSharpRenderer extends CSharpRenderer {}
-export class MockJavaScriptRenderer extends JavaScriptRenderer {}
+export class MockJavaRenderer extends JavaRenderer<any> {}
+export class MockTypeScriptRenderer extends TypeScriptRenderer<any> {}
+export class MockGoRenderer extends GoRenderer<any> {}
+export class MockCSharpRenderer extends CSharpRenderer<any> {}
+export class MockJavaScriptRenderer extends JavaScriptRenderer<any> {}
+export class MockDartRenderer extends DartRenderer<any> {}
diff --git a/test/generators/AbstractGenerator.spec.ts b/test/generators/AbstractGenerator.spec.ts
index df601c65..9535513d 100644
--- a/test/generators/AbstractGenerator.spec.ts
+++ b/test/generators/AbstractGenerator.spec.ts
@@ -1,5 +1,4 @@
-import { AbstractGenerator } from '../../src/generators'; 
-import { CommonInputModel, CommonModel, RenderOutput } from '../../src/models';
+import { InputMetaModel, CommonModel, AnyModel } from '../../src/models';
 import {TestGenerator} from '../TestUtils/TestGenerator';
 describe('AbstractGenerator', () => {
   let generator: TestGenerator;
@@ -8,58 +7,61 @@ describe('AbstractGenerator', () => {
   });
 
   test('generate() should return OutputModels', async () => {
-    const doc: any = { $id: 'test' };
-    const outputModels = await generator.generate(doc);
+    const cim = new InputMetaModel();
+    const model = new AnyModel('test', undefined);
+    cim.models[model.name] = model;
+    const outputModels = await generator.generate(cim);
 
     expect(outputModels[0].result).toEqual('test');
     expect(outputModels[0].modelName).toEqual('TestName');
   });
 
   test('generateCompleteModels() should return OutputModels', async () => {
-    const doc: any = { $id: 'test' };
-    const outputModels = await generator.generateCompleteModels(doc, {});
+    const cim = new InputMetaModel();
+    const model = new AnyModel('test', undefined);
+    cim.models[model.name] = model;
+    const outputModels = await generator.generateCompleteModels(cim, {});
 
-    expect(outputModels[0].result).toEqual('rendered complete content');
+    expect(outputModels[0].result).toEqual('test');
     expect(outputModels[0].modelName).toEqual('TestName');
   });
 
-  test('generate() should process CommonInputModel instance', async () => {
-    const cim = new CommonInputModel();
-    const model = new CommonModel();
-    model.$id = 'test';
-    cim.models[model.$id] = model;
+  test('generate() should process InputMetaModel instance', async () => {
+    const cim = new InputMetaModel();
+    const model = new AnyModel('test', undefined);
+    cim.models[model.name] = model;
     const outputModels = await generator.generate(cim);
     expect(outputModels[0].result).toEqual('test');
     expect(outputModels[0].modelName).toEqual('TestName');
   });
 
-  test('generateCompleteModels() should process CommonInputModel instance', async () => {
-    const cim = new CommonInputModel();
-    const model = new CommonModel();
-    model.$id = 'test';
-    cim.models[model.$id] = model;
+  test('generateCompleteModels() should process InputMetaModel instance', async () => {
+    const cim = new InputMetaModel();
+    const model = new AnyModel('test', undefined);
+    cim.models[model.name] = model;
     const outputModels = await generator.generateCompleteModels(cim, {});
 
-    expect(outputModels[0].result).toEqual('rendered complete content');
+    expect(outputModels[0].result).toEqual('test');
     expect(outputModels[0].modelName).toEqual('TestName');
   });
 
-  test('should `process` function return CommonInputModel', async () => {
-    const doc: any = { $id: 'test' };
+  test('should `process` function return InputMetaModel', async () => {
+    const doc: any = { type: 'string', $id: 'test' };
     const commonInputModel = await generator.process(doc);
     const keys = Object.keys(commonInputModel.models);
 
-    expect(commonInputModel).toBeInstanceOf(CommonInputModel);
+    expect(commonInputModel).toBeInstanceOf(InputMetaModel);
     expect(commonInputModel.models).toBeDefined();
     expect(keys).toHaveLength(1);
     expect(commonInputModel.models[keys[0]].originalInput).toEqual({
       $id: 'test',
+      type: 'string',
       'x-modelgen-inferred-name': 'root',
     });
   });
 
   test('should `render` function return renderer model', async () => {
-    const doc: any = { $id: 'SomeModel' };
+    const doc: any = { type: 'string', $id: 'SomeModel' };
     const commonInputModel = await generator.process(doc);
     const keys = Object.keys(commonInputModel.models);
     const renderedContent = await generator.render(commonInputModel.models[keys[0]], commonInputModel);
@@ -71,22 +73,7 @@ describe('AbstractGenerator', () => {
 
   describe('getPresets()', () => {
     test('getPresets()', () => {
-      class GeneratorWithPresets extends AbstractGenerator<any, any> {
-        constructor() {
-          super('TestGenerator', {presets: [{preset: {test: 'test2'}, options: {}}]});
-        }
-        
-        render(model: CommonModel, inputModel: CommonInputModel): any { return; }
-
-        testGetPresets(string: string) {
-          return this.getPresets(string);
-        }
-
-        renderCompleteModel(model: CommonModel, inputModel: CommonInputModel, options: any): Promise<RenderOutput> {
-          throw new Error('Method not implemented.');
-        }
-      }
-      const newGenerator = new GeneratorWithPresets();
+      const newGenerator = new TestGenerator({presets: [{preset: {test: 'test2'}, options: {}}]});
       expect(newGenerator.testGetPresets('test')).toEqual([['test2', {}]]);
     });
   });
diff --git a/test/generators/AbstractRenderer.spec.ts b/test/generators/AbstractRenderer.spec.ts
index 8e8d0d57..719d3972 100644
--- a/test/generators/AbstractRenderer.spec.ts
+++ b/test/generators/AbstractRenderer.spec.ts
@@ -16,14 +16,6 @@ describe('AbstractRenderer', () => {
     const content = renderer.renderBlock(['Test1', 'Test2']);
     expect(content).toEqual('Test1\nTest2');
   });
-
-  test('can use generator inside renderer', async () => {
-    const generator = renderer.generator;
-    const doc: any = { $id: 'test' };
-    const outputModels = await generator.generate(doc);
-
-    expect(outputModels[0].result).toEqual('test');
-  });
   
   describe('addDependency()', () => {
     test('should add dependency', () => {
@@ -94,6 +86,7 @@ describe('AbstractRenderer', () => {
       expect(presetCallback).not.toHaveBeenCalled();
     });
   });
+  
   describe('runPreset()', () => {
     test('should use string', async () => {
       const preset1Callback = jest.fn();
@@ -133,6 +126,7 @@ describe('AbstractRenderer', () => {
       expect(preset1Callback).toHaveBeenCalled();
       expect(preset2Callback).toHaveBeenCalled();
     });
+    
     test('should not use previous preset if undefined returned', async () => {
       const preset1Callback = jest.fn();
       const preset2Callback = jest.fn();
diff --git a/test/helpers/CommonModelToMetaModel.spec.ts b/test/helpers/CommonModelToMetaModel.spec.ts
index e090c7e7..f3e22fbb 100644
--- a/test/helpers/CommonModelToMetaModel.spec.ts
+++ b/test/helpers/CommonModelToMetaModel.spec.ts
@@ -1,4 +1,4 @@
-import { AnyModel, ArrayModel, BooleanModel, CommonModel, FloatModel, IntegerModel, ObjectModel, StringModel, TupleModel, UnionModel } from '../../src';
+import { AnyModel, ArrayModel, BooleanModel, CommonModel, EnumModel, FloatModel, IntegerModel, ObjectModel, StringModel, TupleModel, UnionModel } from '../../src';
 import { convertToMetaModel } from '../../src/helpers';
 describe('CommonModelToMetaModel', () => {
   afterEach(() => {
@@ -14,6 +14,19 @@ describe('CommonModelToMetaModel', () => {
     expect(model).not.toBeUndefined();
     expect(model instanceof AnyModel).toEqual(true);
   });
+  test('should convert to enum model', () => { 
+    const cm = new CommonModel();
+    cm.type = 'string';
+    cm.$id = 'test';
+    cm.enum = [
+      'test'
+    ];
+
+    const model = convertToMetaModel(cm);
+
+    expect(model).not.toBeUndefined();
+    expect(model instanceof EnumModel).toEqual(true);
+  });
   test('should convert to string model', () => { 
     const cm = new CommonModel();
     cm.type = 'string';
diff --git a/test/helpers/ConstrainHelpers.spec.ts b/test/helpers/ConstrainHelpers.spec.ts
index 8a6e2a92..5013aec3 100644
--- a/test/helpers/ConstrainHelpers.spec.ts
+++ b/test/helpers/ConstrainHelpers.spec.ts
@@ -1,25 +1,7 @@
 import { NO_NUMBER_START_CHAR, NO_EMPTY_VALUE, NO_DUPLICATE_PROPERTIES, FormatHelpers, NO_DUPLICATE_ENUM_KEYS, constrainMetaModel, TypeMapping, Constraints } from '../../src/helpers';
 import { AnyModel, ArrayModel, BooleanModel, ConstrainedAnyModel, ConstrainedArrayModel, ConstrainedBooleanModel, ConstrainedDictionaryModel, ConstrainedEnumModel, ConstrainedEnumValueModel, ConstrainedFloatModel, ConstrainedIntegerModel, ConstrainedObjectModel, ConstrainedObjectPropertyModel, ConstrainedReferenceModel, ConstrainedStringModel, ConstrainedTupleModel, ConstrainedTupleValueModel, ConstrainedUnionModel, DictionaryModel, EnumModel, EnumValueModel, FloatModel, IntegerModel, ObjectModel, ObjectPropertyModel, ReferenceModel, StringModel, TupleModel, TupleValueModel, UnionModel } from '../../src/models';
-const mockedTypeMapping: TypeMapping<any> = {
-  Any: jest.fn(),
-  Array: jest.fn(),
-  Boolean: jest.fn(),
-  Dictionary: jest.fn(),
-  Enum: jest.fn(),
-  Float: jest.fn(),
-  Integer: jest.fn(),
-  Object: jest.fn(),
-  Reference: jest.fn(),
-  String: jest.fn(),
-  Tuple: jest.fn(),
-  Union: jest.fn()
-};
-const mockedConstraints: Constraints = {
-  enumKey: jest.fn().mockImplementation(({enumKey}) => enumKey),
-  enumValue: jest.fn().mockImplementation(({enumValue}) => enumValue),
-  modelName: jest.fn().mockImplementation(({modelName}) => modelName),
-  propertyKey: jest.fn().mockImplementation(({objectPropertyModel}) => objectPropertyModel.propertyName)
-};
+import { mockedConstraints, mockedTypeMapping } from '../TestUtils/TestConstrainer';
+
 describe('ConstrainHelpers', () => {
   afterEach(() => {
     jest.clearAllMocks();
diff --git a/test/helpers/NameHelpers.spec.ts b/test/helpers/NameHelpers.spec.ts
deleted file mode 100644
index 4c224060..00000000
--- a/test/helpers/NameHelpers.spec.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import { CommonNamingConventionImplementation, DefaultPropertyNames, getUniquePropertyName } from '../../src/helpers'; 
-import { CommonInputModel, CommonModel } from '../../src/models';
-
-describe('NameHelpers', () => {
-  describe('getUniquePropertyName', () => {
-    test('should return correct name for additionalProperties', () => {  
-      const model = CommonModel.toCommonModel({});
-
-      const additionalPropertiesName = getUniquePropertyName(model, DefaultPropertyNames.additionalProperties);
-      
-      expect(additionalPropertiesName).toEqual('additionalProperties');
-    });
-    test('should handle duplicate names', () => {  
-      const model = CommonModel.toCommonModel({properties: {additionalProperties: {}}});
-
-      const additionalPropertiesName = getUniquePropertyName(model, DefaultPropertyNames.additionalProperties);
-      
-      expect(additionalPropertiesName).toEqual('reserved_additionalProperties');
-    });
-  });
-
-  describe('CommonNamingConventionImplementation', () => {
-    const isReservedKeyword = jest.fn().mockReturnValue(false);
-    const defaultCtx = {model: CommonModel.toCommonModel({}), inputModel: new CommonInputModel(), reservedKeywordCallback: isReservedKeyword};
-    describe('type', () => {
-      test('should handle undefined', () => {
-        const name = undefined;
-        const formattedName = CommonNamingConventionImplementation.type!(name, defaultCtx);
-        expect(formattedName).toEqual('');
-      });
-      test('Should default name to pascal case', () => {
-        const name = 'some_not Pascal string';
-        const formattedName = CommonNamingConventionImplementation!.type!(name, defaultCtx);
-        expect(formattedName).toEqual('SomeNotPascalString');
-      });
-    });
-    describe('property', () => {
-      test('should handle undefined', () => {
-        const name = undefined;
-        const formattedName = CommonNamingConventionImplementation!.property!(name, defaultCtx);
-        expect(formattedName).toEqual('');
-      });
-      test('Should default name to camel case', () => {
-        const name = 'some_not Pascal string';
-        const formattedName = CommonNamingConventionImplementation!.property!(name, defaultCtx);
-        expect(formattedName).toEqual('someNotPascalString');
-      });
-      test('Should return accurate reserved property name', () => {
-        const name = '$ref';
-        isReservedKeyword.mockReturnValueOnce(true);
-        const formattedName = CommonNamingConventionImplementation!.property!(name, defaultCtx);
-        expect(formattedName).toEqual('reservedRef');
-      });
-    });
-  });
-});
diff --git a/test/helpers/TypeHelpers.spec.ts b/test/helpers/TypeHelpers.spec.ts
index bf398053..21b1f2d7 100644
--- a/test/helpers/TypeHelpers.spec.ts
+++ b/test/helpers/TypeHelpers.spec.ts
@@ -1,6 +1,5 @@
 import { TypeHelpers, ModelKind, getTypeFromMapping, TypeMapping } from '../../src/helpers'; 
 import { CommonModel, ConstrainedAnyModel, ConstrainedArrayModel, ConstrainedBooleanModel, ConstrainedDictionaryModel, ConstrainedEnumModel, ConstrainedFloatModel, ConstrainedIntegerModel, ConstrainedMetaModel, ConstrainedObjectModel, ConstrainedReferenceModel, ConstrainedStringModel, ConstrainedTupleModel, ConstrainedUnionModel } from '../../src/models';
-import { TestRenderer } from '../TestUtils/TestRenderers';
 
 describe('TypeHelpers', () => {
   describe('extractKind', () => {
@@ -57,8 +56,7 @@ describe('TypeHelpers', () => {
     });
   });
   describe('getTypeFromMapping', () => {
-    const testRenderer = new TestRenderer();
-    const typeMapping: TypeMapping<TestRenderer> = {
+    const typeMapping: TypeMapping<any> = {
       Object: jest.fn().mockReturnValue('test'),
       Reference: jest.fn().mockReturnValue('test'),
       Any: jest.fn().mockReturnValue('test'),
@@ -72,30 +70,35 @@ describe('TypeHelpers', () => {
       Union: jest.fn().mockReturnValue('test'),
       Dictionary: jest.fn().mockReturnValue('test')
     };
+    class CustomConstrainedMetaModel extends ConstrainedMetaModel {
+      getNearestDependencies(): ConstrainedMetaModel[] {
+        throw new Error('Method not implemented.');
+      }
+    }
     test('should return undefined with generic constrained model', () => {
-      const constrainedModel = new ConstrainedMetaModel('', undefined, '');
+      const constrainedModel = new CustomConstrainedMetaModel('', undefined, '');
 
       const t = () => {
-        getTypeFromMapping(typeMapping, {constrainedModel, renderer: testRenderer});
+        getTypeFromMapping(typeMapping, {constrainedModel, options: {}});
       };
       expect(t).toThrow('Could not find type for model');
     });
     const modelsToCheck = [
       new ConstrainedObjectModel('', undefined, '', {}),
-      new ConstrainedReferenceModel('', undefined, '', new ConstrainedMetaModel('', undefined, '')),
+      new ConstrainedReferenceModel('', undefined, '', new CustomConstrainedMetaModel('', undefined, '')),
       new ConstrainedAnyModel('', undefined, ''),
       new ConstrainedFloatModel('', undefined, ''),
       new ConstrainedIntegerModel('', undefined, ''),
       new ConstrainedStringModel('', undefined, ''),
       new ConstrainedBooleanModel('', undefined, ''),
       new ConstrainedTupleModel('', undefined, '', []),
-      new ConstrainedArrayModel('', undefined, '', new ConstrainedMetaModel('', undefined, '')),
+      new ConstrainedArrayModel('', undefined, '', new CustomConstrainedMetaModel('', undefined, '')),
       new ConstrainedEnumModel('', undefined, '', []),
       new ConstrainedUnionModel('', undefined, '', []),
-      new ConstrainedDictionaryModel('', undefined, '', new ConstrainedMetaModel('', undefined, ''), new ConstrainedMetaModel('', undefined, ''))
+      new ConstrainedDictionaryModel('', undefined, '', new CustomConstrainedMetaModel('', undefined, ''), new CustomConstrainedMetaModel('', undefined, ''))
     ];
     test.each(modelsToCheck)('should return type from mapping', (constrainedModel: ConstrainedMetaModel) => {
-      const foundType = getTypeFromMapping(typeMapping, {constrainedModel, renderer: testRenderer});
+      const foundType = getTypeFromMapping(typeMapping, {constrainedModel, options: {}});
       expect(foundType).toEqual('test');
     });
   });
diff --git a/test/models/CommonModel.spec.ts b/test/models/CommonModel.spec.ts
index d69522a2..2e6d036b 100644
--- a/test/models/CommonModel.spec.ts
+++ b/test/models/CommonModel.spec.ts
@@ -532,42 +532,6 @@ describe('CommonModel', () => {
         expect(doc1.additionalItems).toBeUndefined();
       });
     });
-
-    describe('patternProperties', () => {
-      test('should be merged when only right side is defined', () => {
-        const doc = { };
-        let doc1 = CommonModel.toCommonModel(doc);
-        const doc2 = CommonModel.toCommonModel(doc);
-        doc2.patternProperties = {pattern1: CommonModel.toCommonModel({type: 'string'})};
-        doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc);
-        expect(doc1.patternProperties).toEqual(doc2.patternProperties);
-      });
-      test('should be merged when both sides are defined', () => {
-        const doc = { };
-        let doc1 = CommonModel.toCommonModel(doc);
-        const doc2 = CommonModel.toCommonModel(doc);
-        doc2.patternProperties = {pattern1: CommonModel.toCommonModel({type: 'string'})};
-        doc1.patternProperties = {pattern2: CommonModel.toCommonModel({type: 'number'})};
-        doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc);
-        expect(doc1.patternProperties).toEqual({pattern1: {type: 'string'}, pattern2: {type: 'number'}});
-      });
-      test('should be merged together when both sides are defined', () => {
-        const doc = { };
-        let doc1 = CommonModel.toCommonModel(doc);
-        const doc2 = CommonModel.toCommonModel(doc);
-        doc2.patternProperties = {pattern1: CommonModel.toCommonModel({type: 'string'})};
-        doc1.patternProperties = {pattern1: CommonModel.toCommonModel({type: 'number'})};
-        doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc);
-        expect(doc1.patternProperties).toEqual({pattern1: {type: ['number', 'string'], originalInput: {}}});
-      });
-      test('should not change if nothing is defined', () => {
-        const doc = { };
-        let doc1 = CommonModel.toCommonModel(doc);
-        const doc2 = CommonModel.toCommonModel(doc);
-        doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc);
-        expect(doc1.patternProperties).toBeUndefined();
-      });
-    });
   });
   
   describe('addItem', () => {
@@ -758,33 +722,6 @@ describe('CommonModel', () => {
       expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith(1, additionalItemsModel, additionalItemsModel, {});
     });
   });
-  describe('addPatternProperty', () => {
-    beforeAll(() => {
-      jest.spyOn(CommonModel, 'mergeCommonModels');
-    });
-    afterEach(() => {
-      jest.clearAllMocks();
-    });
-    test('should add patternProperty to model', () => {
-      const patternPropertyModel = new CommonModel();
-      const pattern = 'TestPattern';
-      patternPropertyModel.$id = 'test'; 
-      const model = new CommonModel(); 
-      model.addPatternProperty(pattern, patternPropertyModel, {});
-      expect(model.patternProperties).toEqual({TestPattern: patternPropertyModel});
-      expect(CommonModel.mergeCommonModels).not.toHaveBeenCalled();
-    });
-    test('should merge additionalProperties together', () => {
-      const patternPropertyModel = new CommonModel();
-      const pattern = 'TestPattern';
-      patternPropertyModel.$id = 'test'; 
-      const model = new CommonModel(); 
-      model.addPatternProperty(pattern, patternPropertyModel, {});
-      model.addPatternProperty(pattern, patternPropertyModel, {});
-      expect(model.patternProperties).toEqual({TestPattern: patternPropertyModel});
-      expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith(1, patternPropertyModel, patternPropertyModel, {});
-    });
-  });
   describe('addEnum', () => {
     test('should add enum', () => {
       const model = new CommonModel(); 
diff --git a/test/models/ConstrainedMetaModel.spec.ts b/test/models/ConstrainedMetaModel.spec.ts
new file mode 100644
index 00000000..48edbe4e
--- /dev/null
+++ b/test/models/ConstrainedMetaModel.spec.ts
@@ -0,0 +1,199 @@
+import { constrainMetaModel } from '../../src/helpers';
+import { AnyModel, ArrayModel, BooleanModel, ConstrainedArrayModel, ConstrainedDictionaryModel, ConstrainedEnumModel, ConstrainedObjectModel, ConstrainedReferenceModel, ConstrainedTupleModel, ConstrainedUnionModel, DictionaryModel, EnumModel, EnumValueModel, FloatModel, IntegerModel, ObjectModel, ObjectPropertyModel, ReferenceModel, StringModel, TupleModel, TupleValueModel, UnionModel } from '../../src/models';
+import { mockedConstraints, mockedTypeMapping } from '../TestUtils/TestConstrainer';
+
+describe('ConstrainedMetaModel', () => {
+  describe('ReferenceModel', () => {
+    test('should return no dependencies', () => {
+      const stringModel = new StringModel('', undefined);
+      const rawModel = new ReferenceModel('', undefined, stringModel);
+      
+      const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, {
+        metaModel: rawModel,
+        constrainedName: '',
+        options: undefined
+      });
+      const dependencies = model.getNearestDependencies();
+      expect(dependencies).toHaveLength(0);
+    });
+  });
+  describe('StringModel', () => {
+    test('should return no dependencies', () => {
+      const rawModel = new StringModel('', undefined);
+      
+      const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, {
+        metaModel: rawModel,
+        constrainedName: '',
+        options: undefined
+      });
+      const dependencies = model.getNearestDependencies();
+      expect(dependencies).toHaveLength(0);
+    });
+  });
+  describe('AnyModel', () => {
+    test('should return no dependencies', () => {
+      const rawModel = new AnyModel('', undefined);
+      
+      const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, {
+        metaModel: rawModel,
+        constrainedName: '',
+        options: undefined
+      });
+      const dependencies = model.getNearestDependencies();
+      expect(dependencies).toHaveLength(0);
+    });
+  });
+  describe('FloatModel', () => {
+    test('should return no dependencies', () => {
+      const rawModel = new FloatModel('', undefined);
+      
+      const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, {
+        metaModel: rawModel,
+        constrainedName: '',
+        options: undefined
+      });
+      const dependencies = model.getNearestDependencies();
+      expect(dependencies).toHaveLength(0);
+    });
+  });
+  describe('IntegerModel', () => {
+    test('should return no dependencies', () => {
+      const rawModel = new IntegerModel('', undefined);
+      
+      const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, {
+        metaModel: rawModel,
+        constrainedName: '',
+        options: undefined
+      });
+      const dependencies = model.getNearestDependencies();
+      expect(dependencies).toHaveLength(0);
+    });
+  });
+  describe('BooleanModel', () => {
+    test('should return no dependencies', () => {
+      const rawModel = new BooleanModel('', undefined);
+      
+      const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, {
+        metaModel: rawModel,
+        constrainedName: '',
+        options: undefined
+      });
+      const dependencies = model.getNearestDependencies();
+      expect(dependencies).toHaveLength(0);
+    });
+  });
+  describe('TupleModel', () => {
+    test('should return all reference dependencies', () => {
+      const stringModel = new StringModel('', undefined);
+      const referenceModel = new ReferenceModel('', undefined, stringModel);
+      const referenceTupleModel = new TupleValueModel(0, referenceModel);
+      const stringTupleModel = new TupleValueModel(1, stringModel);
+      const rawModel = new TupleModel('test', undefined, [referenceTupleModel, stringTupleModel]);
+      
+      const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, {
+        metaModel: rawModel,
+        constrainedName: '',
+        options: undefined
+      }) as ConstrainedTupleModel;
+      const dependencies = model.getNearestDependencies();
+      expect(dependencies).toHaveLength(1);
+      expect(dependencies[0]).toEqual(model.tuple[0].value);
+    });
+  });
+  describe('ObjectModel', () => {
+    test('should return all reference dependencies', () => {
+      const stringModel = new StringModel('', undefined);
+      const referenceModel = new ReferenceModel('', undefined, stringModel);
+      const referenceObjectPropertyModel = new ObjectPropertyModel('reference', false, referenceModel);
+      const stringObjectPropertyModel = new ObjectPropertyModel('string', false, stringModel);
+      const rawModel = new ObjectModel('test', undefined, {
+        reference: referenceObjectPropertyModel,
+        string: stringObjectPropertyModel
+      });
+      
+      const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, {
+        metaModel: rawModel,
+        constrainedName: '',
+        options: undefined
+      }) as ConstrainedObjectModel;
+      const dependencies = model.getNearestDependencies();
+      expect(dependencies).toHaveLength(1);
+      expect(dependencies[0]).toEqual(model.properties['reference'].property);
+    });
+  });
+  describe('EnumModel', () => {
+    test('should return no dependencies', () => {
+      const referenceEnumValueModel = new EnumValueModel('reference', 'referenceModel');
+      const stringEnumValueModel = new EnumValueModel('string', 'stringModel');
+      const rawModel = new EnumModel('test', undefined, [referenceEnumValueModel, stringEnumValueModel]);
+      
+      const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, {
+        metaModel: rawModel,
+        constrainedName: '',
+        options: undefined
+      }) as ConstrainedEnumModel;
+      const dependencies = model.getNearestDependencies();
+      expect(dependencies).toHaveLength(0);
+    });
+  });
+  describe('DictionaryModel', () => {
+    test('should return all reference dependencies', () => {
+      const stringModel = new StringModel('', undefined);
+      const referenceModel = new ReferenceModel('', undefined, stringModel);
+      const rawModel = new DictionaryModel('test', undefined, referenceModel, stringModel);
+      
+      const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, {
+        metaModel: rawModel,
+        constrainedName: '',
+        options: undefined
+      }) as ConstrainedDictionaryModel;
+      const dependencies = model.getNearestDependencies();
+      expect(dependencies).toHaveLength(1);
+      expect(dependencies[0]).toEqual(model.key);
+    });
+  });
+  describe('ArrayModel', () => {
+    test('should return all reference dependencies', () => {
+      const stringModel = new StringModel('', undefined);
+      const referenceModel = new ReferenceModel('', undefined, stringModel);
+      const rawModel = new ArrayModel('test', undefined, referenceModel);
+      
+      const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, {
+        metaModel: rawModel,
+        constrainedName: '',
+        options: undefined
+      }) as ConstrainedArrayModel;
+      const dependencies = model.getNearestDependencies();
+      expect(dependencies).toHaveLength(1);
+      expect(dependencies[0]).toEqual(model.valueModel);
+    });
+    test('should return nothing if no references are used', () => {
+      const stringModel = new StringModel('', undefined);
+      const rawModel = new ArrayModel('', undefined, stringModel);
+      
+      const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, {
+        metaModel: rawModel,
+        constrainedName: '',
+        options: undefined
+      });
+      const dependencies = model.getNearestDependencies();
+      expect(dependencies).toHaveLength(0);
+    });
+  });
+  describe('UnionModel', () => {
+    test('should return all reference dependencies', () => {
+      const stringModel = new StringModel('', undefined);
+      const referenceModel = new ReferenceModel('', undefined, stringModel);
+      const rawModel = new UnionModel('test', undefined, [referenceModel, stringModel]);
+      
+      const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, {
+        metaModel: rawModel,
+        constrainedName: '',
+        options: undefined
+      }) as ConstrainedUnionModel;
+      const dependencies = model.getNearestDependencies();
+      expect(dependencies).toHaveLength(1);
+      expect(dependencies[0]).toEqual(model.union[0]);
+    });
+  });
+});
diff --git a/test/models/OutputModel.spec.ts b/test/models/OutputModel.spec.ts
index d126291a..29bc9623 100644
--- a/test/models/OutputModel.spec.ts
+++ b/test/models/OutputModel.spec.ts
@@ -1,15 +1,12 @@
-import { CommonInputModel, CommonModel, ToOutputModelArg, OutputModel } from '../../src/models'; 
+import { InputMetaModel, CommonModel, ToOutputModelArg, OutputModel, ConstrainedAnyModel } from '../../src/models'; 
 
 describe('OutputModel', () => {
   test('should return an OutputModel', () => {
-    const doc: any = { $id: 'test' };
-    const commonModel = CommonModel.toCommonModel(doc);
-
     const ioutput: ToOutputModelArg = {
       result: 'result',
-      model: commonModel,
+      model: new ConstrainedAnyModel('', undefined, ''),
       modelName: 'someModel',
-      inputModel: new CommonInputModel(),
+      inputModel: new InputMetaModel(),
       dependencies: ['test']
     };
     const output = OutputModel.toOutputModel(ioutput);
@@ -22,14 +19,11 @@ describe('OutputModel', () => {
   });
 
   test('should return an array of OutputModel', () => {
-    const doc: any = { $id: 'test' };
-    const commonModel = CommonModel.toCommonModel(doc);
-
     const ioutput: ToOutputModelArg = {
       result: 'result',
-      model: commonModel,
+      model: new ConstrainedAnyModel('', undefined, ''),
       modelName: 'someModel',
-      inputModel: new CommonInputModel(),
+      inputModel: new InputMetaModel(),
       dependencies: ['test']
     };
     const output = OutputModel.toOutputModel([ioutput, ioutput]);