diff --git a/reverse_engineering/mappers/field.js b/reverse_engineering/mappers/field.js index 1947994..aa8b8b1 100644 --- a/reverse_engineering/mappers/field.js +++ b/reverse_engineering/mappers/field.js @@ -1,11 +1,38 @@ /** * @import {FieldDefinitionNode, TypeNode, InputValueDefinitionNode, ValueNode} from "graphql" - * @import {DefinitionNameToTypeNameMap, FieldTypeProperties, InputTypeFieldProperties, PreProcessedFieldData} from "./../../shared/types/types" + * @import {DefinitionNameToTypeNameMap, FieldsOrder, FieldTypeProperties, InputTypeFieldProperties, PreProcessedFieldData, REFieldsSchemaProperties, REPropertiesSchema} from "./../../shared/types/types" */ const { mapDirectivesUsage } = require('./directiveUsage'); const { astNodeKind } = require('../constants/graphqlAST'); const { BUILT_IN_SCALAR_LIST } = require('../constants/types'); +const { sortByName } = require('../helpers/sortByName'); + +/** + * Maps a list of fields to a schema + * + * @param {object} params + * @param {FieldDefinitionNode[] | InputValueDefinitionNode[]} params.fields - The fields to map + * @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map + * @param {FieldsOrder} params.fieldsOrder - The fields order + * @returns {REFieldsSchemaProperties} The mapped schema + */ +function getFieldsSchema({ fields, definitionCategoryByNameMap, fieldsOrder }) { + const properties = fields ? fields.map(field => mapField({ field, definitionCategoryByNameMap })) : []; + const required = properties.filter(property => property.required).map(property => property.name); + const convertedProperties = sortByName({ items: properties, fieldsOrder }).reduce( + (acc, property) => { + acc[property.name] = property; + return acc; + }, + /** @type {REPropertiesSchema} */ {}, + ); + + return { + properties: convertedProperties, + required, + }; +} /** * Maps a field @@ -147,4 +174,5 @@ function isBuiltInScalar({ typeName }) { module.exports = { mapField, + getFieldsSchema, }; diff --git a/reverse_engineering/mappers/rootSchemaTypes.js b/reverse_engineering/mappers/rootSchema.js similarity index 93% rename from reverse_engineering/mappers/rootSchemaTypes.js rename to reverse_engineering/mappers/rootSchema.js index 4daf951..e42c8c1 100644 --- a/reverse_engineering/mappers/rootSchemaTypes.js +++ b/reverse_engineering/mappers/rootSchema.js @@ -16,7 +16,7 @@ const { mapDirectivesUsage } = require('./directiveUsage'); * @param {string} params.graphName - The name of the graph * @returns {ContainerInfo} The mapped container */ -function mapRootSchemaTypesToContainer({ rootSchemaNode, graphName = 'New Graph' }) { +function mapRootSchemaToContainer({ rootSchemaNode, graphName = 'New Graph' }) { if (!rootSchemaNode) { return { name: graphName }; } @@ -56,5 +56,5 @@ function mapSchemaRootTypes({ schemaRootTypes }) { } module.exports = { - mapRootSchemaTypesToContainer, + mapRootSchemaToContainer, }; diff --git a/reverse_engineering/mappers/rootTypes.js b/reverse_engineering/mappers/rootTypes.js new file mode 100644 index 0000000..a5eecea --- /dev/null +++ b/reverse_engineering/mappers/rootTypes.js @@ -0,0 +1,68 @@ +/** + * @import {ObjectTypeDefinitionNode} from "graphql" + * @import {DefinitionNameToTypeNameMap, FieldsOrder, RootTypeEntity} from "../../shared/types/types" + */ + +const { mapDirectivesUsage } = require('./directiveUsage'); +const { getFieldsSchema } = require('./field'); + +/** + * Maps the schema root types to entities + * + * @param {object} params + * @param {ObjectTypeDefinitionNode[]} params.rootTypeNodes - The schema root type nodes + * @param {string[]} params.schemaRootTypesMap - The schema root types names map + * @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map + * @param {FieldsOrder} params.fieldsOrder - The fields order + * @returns {RootTypeEntity[]} The mapped container + */ +function mapRootTypesToEntities({ rootTypeNodes, schemaRootTypesMap, definitionCategoryByNameMap, fieldsOrder }) { + if (!rootTypeNodes) { + return []; + } + + return rootTypeNodes.map(rootTypeNode => { + const { properties, required } = getFieldsSchema({ + fields: [...(rootTypeNode.fields || [])], + definitionCategoryByNameMap, + fieldsOrder, + }); + return { + name: rootTypeNode.name.value, + data: { + type: 'object', + description: rootTypeNode.description?.value || '', + operationType: getOperationType({ typeName: rootTypeNode.name.value, schemaRootTypesMap }), + typeDirectives: mapDirectivesUsage({ directives: [...(rootTypeNode.directives || [])] }), + properties, + required, + }, + }; + }); +} + +/** + * Maps the operation type + * + * @param {object} params + * @param {string} params.typeName - The type name + * @param {string[]} params.schemaRootTypesMap - The schema root types names map + * @returns {string} The operation type + */ +function getOperationType({ typeName, schemaRootTypesMap }) { + const [queryType, mutationType, subscriptionType] = schemaRootTypesMap; + if (typeName === queryType) { + return 'Query'; + } + if (typeName === mutationType) { + return 'Mutation'; + } + if (typeName === subscriptionType) { + return 'Subscription'; + } + return ''; +} + +module.exports = { + mapRootTypesToEntities, +}; diff --git a/reverse_engineering/mappers/schema.js b/reverse_engineering/mappers/schema.js index 7d0956c..bdd3e87 100644 --- a/reverse_engineering/mappers/schema.js +++ b/reverse_engineering/mappers/schema.js @@ -3,10 +3,12 @@ * @import {Logger, FileREEntityResponseData, FieldsOrder} from "../../shared/types/types" */ -const { Kind } = require('graphql'); -const { mapRootSchemaTypesToContainer } = require('./rootSchemaTypes'); +const { mapRootSchemaToContainer } = require('./rootSchema'); const { findNodesByKind } = require('../helpers/findNodesByKind'); const { getTypeDefinitions } = require('./typeDefinitions/typeDefinitions'); +const { mapRootTypesToEntities } = require('./rootTypes'); +const { astNodeKind } = require('../constants/graphqlAST'); +const { getDefinitionCategoryByNameMap } = require('../helpers/getDefinitionCategoryByNameMap'); /** * Maps a GraphQL schema to a RE response @@ -23,8 +25,8 @@ function getMappedSchema({ schemaItems, graphName, logger, fieldsOrder }) { if (!schemaItems) { throw new Error('Schema items are empty'); } - const container = mapRootSchemaTypesToContainer({ - rootSchemaNode: findNodesByKind({ nodes: schemaItems, kind: Kind.SCHEMA_DEFINITION })[0], + const container = mapRootSchemaToContainer({ + rootSchemaNode: findNodesByKind({ nodes: schemaItems, kind: astNodeKind.SCHEMA_DEFINITION })[0], graphName, }); const rootTypeNames = [ @@ -33,23 +35,38 @@ function getMappedSchema({ schemaItems, graphName, logger, fieldsOrder }) { container.schemaRootTypes?.rootSubscription || 'Subscription', ]; - const typeDefinitions = getTypeDefinitions({ typeDefinitions: schemaItems, fieldsOrder, rootTypeNames }); + const definitionCategoryByNameMap = getDefinitionCategoryByNameMap({ nodes: schemaItems }); - return [ - // TODO: remove test collection - { - jsonSchema: '{ "type": "object", "operationType": "Query" }', - objectNames: { - collectionName: 'Test Collection', - }, - doc: { - bucketInfo: container, - collectionName: 'Test Collection', - dbName: container.name, - modelDefinitions: JSON.stringify(typeDefinitions), - }, + const rootTypeNodes = findNodesByKind({ + nodes: schemaItems, + kind: astNodeKind.OBJECT_TYPE_DEFINITION, + }).filter(node => rootTypeNames.includes(node.name.value)); + const entities = mapRootTypesToEntities({ + rootTypeNodes, + definitionCategoryByNameMap, + fieldsOrder, + schemaRootTypesMap: rootTypeNames, + }); + + const typeDefinitions = getTypeDefinitions({ + typeDefinitions: schemaItems, + fieldsOrder, + rootTypeNames, + definitionCategoryByNameMap, + }); + + return entities.map(entity => ({ + jsonSchema: JSON.stringify(entity.data), + objectNames: { + collectionName: entity.name, }, - ]; + doc: { + bucketInfo: container, + collectionName: entity.name, + dbName: container.name, + modelDefinitions: JSON.stringify(typeDefinitions), + }, + })); } catch (error) { logger.log('error', error, 'Failed to map GraphQL schema'); const errorMessage = error instanceof Error ? error.message : String(error); diff --git a/reverse_engineering/mappers/typeDefinitions/inputType.js b/reverse_engineering/mappers/typeDefinitions/inputType.js index 47d85ba..9f2c676 100644 --- a/reverse_engineering/mappers/typeDefinitions/inputType.js +++ b/reverse_engineering/mappers/typeDefinitions/inputType.js @@ -1,11 +1,10 @@ /** * @import {InputObjectTypeDefinitionNode} from "graphql" - * @import {DefinitionNameToTypeNameMap, FieldsOrder, REInputTypeDefinition, REPropertiesSchema} from "../../../shared/types/types" + * @import {DefinitionNameToTypeNameMap, FieldsOrder, REInputTypeDefinition} from "../../../shared/types/types" */ -const { sortByName } = require('../../helpers/sortByName'); const { mapDirectivesUsage } = require('../directiveUsage'); -const { mapField } = require('../field'); +const { getFieldsSchema } = require('../field'); /** * Maps input object type definitions @@ -32,22 +31,16 @@ function getInputObjectTypeDefinitions({ inputObjectTypes = [], definitionCatego * @returns {REInputTypeDefinition} The mapped input object type definition */ function mapInputObjectType({ inputObjectType, definitionCategoryByNameMap, fieldsOrder }) { - const properties = inputObjectType.fields - ? inputObjectType.fields.map(field => mapField({ field, definitionCategoryByNameMap })) - : []; - const required = properties.filter(property => property.required).map(property => property.name); - const convertedProperties = sortByName({ items: properties, fieldsOrder }).reduce( - (acc, property) => { - acc[property.name] = property; - return acc; - }, - /** @type {REPropertiesSchema} */ {}, - ); + const { properties, required } = getFieldsSchema({ + fields: [...(inputObjectType.fields || [])], + definitionCategoryByNameMap, + fieldsOrder, + }); return { type: 'input', name: inputObjectType.name.value, - properties: convertedProperties, + properties, required, description: inputObjectType.description?.value || '', typeDirectives: mapDirectivesUsage({ directives: [...(inputObjectType.directives || [])] }), diff --git a/reverse_engineering/mappers/typeDefinitions/interface.js b/reverse_engineering/mappers/typeDefinitions/interface.js index 3077830..2bd404c 100644 --- a/reverse_engineering/mappers/typeDefinitions/interface.js +++ b/reverse_engineering/mappers/typeDefinitions/interface.js @@ -1,11 +1,10 @@ /** * @import {InterfaceTypeDefinitionNode} from "graphql" - * @import {DefinitionNameToTypeNameMap, FieldsOrder, REInterfaceDefinition, REPropertiesSchema} from "../../../shared/types/types" + * @import {DefinitionNameToTypeNameMap, FieldsOrder, REInterfaceDefinition} from "../../../shared/types/types" */ -const { sortByName } = require('../../helpers/sortByName'); const { mapDirectivesUsage } = require('../directiveUsage'); -const { mapField } = require('../field'); +const { getFieldsSchema } = require('../field'); const { mapImplementsInterfaces } = require('../implementsInterfaces'); /** @@ -31,22 +30,16 @@ function getInterfaceDefinitions({ interfaces = [], definitionCategoryByNameMap, * @returns {REInterfaceDefinition} The mapped interface type definition */ function mapInterface({ interfaceType, definitionCategoryByNameMap, fieldsOrder }) { - const properties = interfaceType.fields - ? interfaceType.fields.map(field => mapField({ field, definitionCategoryByNameMap })) - : []; - const required = properties.filter(property => property.required).map(property => property.name); - const convertedProperties = sortByName({ items: properties, fieldsOrder }).reduce( - (acc, property) => { - acc[property.name] = property; - return acc; - }, - /** @type {REPropertiesSchema} */ {}, - ); + const { properties, required } = getFieldsSchema({ + fields: [...(interfaceType.fields || [])], + definitionCategoryByNameMap, + fieldsOrder, + }); return { type: 'interface', name: interfaceType.name.value, - properties: convertedProperties, + properties, required, description: interfaceType.description?.value || '', typeDirectives: mapDirectivesUsage({ directives: [...(interfaceType.directives || [])] }), diff --git a/reverse_engineering/mappers/typeDefinitions/objectType.js b/reverse_engineering/mappers/typeDefinitions/objectType.js index 4dc2d09..ae18064 100644 --- a/reverse_engineering/mappers/typeDefinitions/objectType.js +++ b/reverse_engineering/mappers/typeDefinitions/objectType.js @@ -3,9 +3,8 @@ * @import {DefinitionNameToTypeNameMap, FieldsOrder, REObjectTypeDefinition, REPropertiesSchema} from "../../../shared/types/types" */ -const { sortByName } = require('../../helpers/sortByName'); const { mapDirectivesUsage } = require('../directiveUsage'); -const { mapField } = require('../field'); +const { getFieldsSchema } = require('../field'); const { mapImplementsInterfaces } = require('../implementsInterfaces'); /** @@ -31,22 +30,16 @@ function getObjectTypeDefinitions({ objectTypes = [], definitionCategoryByNameMa * @returns {REObjectTypeDefinition} The mapped object type definition */ function mapObjectType({ objectType, definitionCategoryByNameMap, fieldsOrder }) { - const properties = objectType.fields - ? objectType.fields.map(field => mapField({ field, definitionCategoryByNameMap })) - : []; - const required = properties.filter(property => property.required).map(property => property.name); - const convertedProperties = sortByName({ items: properties, fieldsOrder }).reduce( - (acc, property) => { - acc[property.name] = property; - return acc; - }, - /** @type {REPropertiesSchema} */ {}, - ); + const { properties, required } = getFieldsSchema({ + fields: [...(objectType.fields || [])], + definitionCategoryByNameMap, + fieldsOrder, + }); return { type: 'object', name: objectType.name.value, - properties: convertedProperties, + properties, required, description: objectType.description?.value || '', typeDirectives: mapDirectivesUsage({ directives: [...(objectType.directives || [])] }), diff --git a/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js b/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js index 13b9505..b5f7500 100644 --- a/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js +++ b/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js @@ -15,12 +15,12 @@ * InterfaceStructureType, * REInterfaceDefinition, * REInputTypeDefinition, - * InputStructureType} from "../../../shared/types/types" + * InputStructureType, + * DefinitionNameToTypeNameMap} from "../../../shared/types/types" */ const { astNodeKind } = require('../../constants/graphqlAST'); const { findNodesByKind } = require('../../helpers/findNodesByKind'); -const { getDefinitionCategoryByNameMap } = require('../../helpers/getDefinitionCategoryByNameMap'); const { sortByName } = require('../../helpers/sortByName'); const { getCustomScalarTypeDefinitions } = require('./customScalar'); const { getDirectiveTypeDefinitions } = require('./directive'); @@ -36,11 +36,10 @@ const { getInputObjectTypeDefinitions } = require('./inputType'); * @param {DefinitionNode[]} params.typeDefinitions - The type definitions nodes * @param {FieldsOrder} params.fieldsOrder - The fields order * @param {string[]} params.rootTypeNames - The root type names + * @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map * @returns {REModelDefinitionsSchema} The mapped type definitions */ -function getTypeDefinitions({ typeDefinitions, fieldsOrder, rootTypeNames }) { - const definitionCategoryByNameMap = getDefinitionCategoryByNameMap({ nodes: typeDefinitions }); - +function getTypeDefinitions({ typeDefinitions, fieldsOrder, rootTypeNames, definitionCategoryByNameMap }) { const directives = getDirectiveTypeDefinitions({ directives: findNodesByKind({ nodes: typeDefinitions, kind: astNodeKind.DIRECTIVE_DEFINITION }), }); diff --git a/shared/types/fe.d.ts b/shared/types/fe.d.ts index 3b27499..685d24e 100644 --- a/shared/types/fe.d.ts +++ b/shared/types/fe.d.ts @@ -9,6 +9,7 @@ import { ObjectLikeDefinition, EnumDefinition, EnumValue, + EntityDetails, } from './shared'; export type FEStatement = { @@ -113,13 +114,10 @@ export type RootTypeNamesParameter = { subscription: string; }; -type EntityDetails = { - operationType?: string; - typeDirectives?: DirectivePropertyData[]; -}; +type FEEntityDetails = EntityDetails; export type EntityIdToJsonSchemaMap = Record; -export type EntityIdToPropertiesMap = Record; +export type EntityIdToPropertiesMap = Record; // API parameters diff --git a/shared/types/re.d.ts b/shared/types/re.d.ts index 2b9013a..0b16441 100644 --- a/shared/types/re.d.ts +++ b/shared/types/re.d.ts @@ -8,6 +8,7 @@ import { EnumValue, StructuredDirective, InputFieldDefaultValue, + EntityDetails, } from './shared'; type ContainerName = string; @@ -19,6 +20,14 @@ export type ContainerInfo = { graphDirectives?: StructuredDirective[]; // container graph directives }; +type REEntityDetails = EntityDetails & + ObjectLikeDefinition & { type: 'object' }; + +export type RootTypeEntity = { + name: string; // entity name + data: REEntityDetails; // specify the type for data +}; + export type FileREEntityResponseData = { jsonSchema: string; // entity JSON Schema objectNames: { @@ -201,6 +210,11 @@ export type REImplementsInterface = { export type REPropertiesSchema = Record>; type REObjectLikeDefinition = ObjectLikeDefinition & { name: string }; +export type REFieldsSchemaProperties = { + properties: REPropertiesSchema; + required: string[]; +}; + export type REObjectTypeDefinition = REObjectLikeDefinition & { type: 'object'; implementsInterfaces?: REImplementsInterface[]; diff --git a/shared/types/shared.d.ts b/shared/types/shared.d.ts index 16b6468..897244b 100644 --- a/shared/types/shared.d.ts +++ b/shared/types/shared.d.ts @@ -22,6 +22,11 @@ export type ContainerSchemaRootTypes = { rootSubscription?: string; // root subscription name }; +type EntityDetails = { + operationType: string; + typeDirectives?: TypeDirectives; +}; + export type DirectiveLocations = { argumentDefinition?: boolean; enum?: boolean; diff --git a/test/reverse_engineering/mappers/rootTypes.spec.js b/test/reverse_engineering/mappers/rootTypes.spec.js new file mode 100644 index 0000000..d4de03f --- /dev/null +++ b/test/reverse_engineering/mappers/rootTypes.spec.js @@ -0,0 +1,391 @@ +const { describe, it, mock, afterEach } = require('node:test'); +const assert = require('assert'); + +// Mock dependencies +const mapDirectivesUsageMock = mock.fn(() => []); +mock.module('../../../reverse_engineering/mappers/directiveUsage', { + namedExports: { + mapDirectivesUsage: mapDirectivesUsageMock, + }, +}); + +// Mock getFieldsSchema +const getFieldsSchemaMock = mock.fn(() => ({ properties: {}, required: [] })); +mock.module('../../../reverse_engineering/mappers/field', { + namedExports: { + getFieldsSchema: getFieldsSchemaMock, + }, +}); + +const { mapRootTypesToEntities } = require('../../../reverse_engineering/mappers/rootTypes'); + +describe('mapRootTypesToEntities', () => { + afterEach(() => { + mapDirectivesUsageMock.mock.resetCalls(); + getFieldsSchemaMock.mock.resetCalls(); + }); + + it('should return an empty array when rootTypeNodes is not provided', () => { + const result = mapRootTypesToEntities({ + rootTypeNodes: undefined, + schemaRootTypesMap: ['Query', 'Mutation', 'Subscription'], + definitionCategoryByNameMap: {}, + fieldsOrder: 'alphabetical', + }); + assert.deepStrictEqual(result, []); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 0); + }); + + it('should return an empty array when rootTypeNodes is an empty array', () => { + const result = mapRootTypesToEntities({ + rootTypeNodes: [], + schemaRootTypesMap: ['Query', 'Mutation', 'Subscription'], + definitionCategoryByNameMap: {}, + fieldsOrder: 'alphabetical', + }); + assert.deepStrictEqual(result, []); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 0); + }); + + it('should correctly map a query root type', () => { + const mockQueryType = { + name: { value: 'Query' }, + fields: [ + { + name: { value: 'user' }, + type: { kind: 'NAMED_TYPE', name: { value: 'User' } }, + directives: [], + }, + ], + directives: [], + description: { value: 'Root Query type' }, + }; + + // Mock getFieldsSchema to return user field + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: { + user: { + name: 'user', + $ref: '#model/definitions/Objects/User', + required: false, + }, + }, + required: [], + })); + + const expected = [ + { + name: 'Query', + data: { + type: 'object', + description: 'Root Query type', + operationType: 'Query', + typeDirectives: [], + properties: { + user: { + name: 'user', + $ref: '#model/definitions/Objects/User', + required: false, + }, + }, + required: [], + }, + }, + ]; + + const result = mapRootTypesToEntities({ + rootTypeNodes: [mockQueryType], + schemaRootTypesMap: ['Query', 'Mutation', 'Subscription'], + definitionCategoryByNameMap: { User: 'Objects' }, + fieldsOrder: 'alphabetical', + }); + + assert.deepStrictEqual(result, expected); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); + + // Verify the correct parameters were passed to getFieldsSchema + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.deepStrictEqual(fieldsSchemaParams.fields, mockQueryType.fields); + assert.deepStrictEqual(fieldsSchemaParams.definitionCategoryByNameMap, { User: 'Objects' }); + assert.strictEqual(fieldsSchemaParams.fieldsOrder, 'alphabetical'); + }); + + it('should correctly map a mutation root type', () => { + const mockMutationType = { + name: { value: 'Mutation' }, + fields: [ + { + name: { value: 'createUser' }, + type: { kind: 'NAMED_TYPE', name: { value: 'User' } }, + directives: [], + }, + ], + directives: [], + }; + + // Mock getFieldsSchema to return createUser field + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: { + createUser: { + name: 'createUser', + $ref: '#model/definitions/Objects/User', + required: false, + }, + }, + required: [], + })); + + const expected = [ + { + name: 'Mutation', + data: { + type: 'object', + description: '', + operationType: 'Mutation', + typeDirectives: [], + properties: { + createUser: { + name: 'createUser', + $ref: '#model/definitions/Objects/User', + required: false, + }, + }, + required: [], + }, + }, + ]; + + const result = mapRootTypesToEntities({ + rootTypeNodes: [mockMutationType], + schemaRootTypesMap: ['Query', 'Mutation', 'Subscription'], + definitionCategoryByNameMap: { User: 'Objects' }, + fieldsOrder: 'alphabetical', + }); + + assert.deepStrictEqual(result, expected); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); + }); + + it('should correctly map a subscription root type', () => { + const mockSubscriptionType = { + name: { value: 'Subscription' }, + fields: [ + { + name: { value: 'userUpdated' }, + type: { kind: 'NAMED_TYPE', name: { value: 'User' } }, + directives: [], + }, + ], + directives: [], + }; + + // Mock getFieldsSchema to return userUpdated field + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: { + userUpdated: { + name: 'userUpdated', + $ref: '#model/definitions/Objects/User', + required: false, + }, + }, + required: [], + })); + + const expected = [ + { + name: 'Subscription', + data: { + type: 'object', + description: '', + operationType: 'Subscription', + typeDirectives: [], + properties: { + userUpdated: { + name: 'userUpdated', + $ref: '#model/definitions/Objects/User', + required: false, + }, + }, + required: [], + }, + }, + ]; + + const result = mapRootTypesToEntities({ + rootTypeNodes: [mockSubscriptionType], + schemaRootTypesMap: ['Query', 'Mutation', 'Subscription'], + definitionCategoryByNameMap: { User: 'Objects' }, + fieldsOrder: 'alphabetical', + }); + + assert.deepStrictEqual(result, expected); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); + }); + + it('should correctly map a root type with custom name', () => { + const mockQueryType = { + name: { value: 'CustomQuery' }, + fields: [], + directives: [], + }; + + // Test with custom schema root types map + const result = mapRootTypesToEntities({ + rootTypeNodes: [mockQueryType], + schemaRootTypesMap: ['CustomQuery', 'CustomMutation', 'CustomSubscription'], + definitionCategoryByNameMap: {}, + fieldsOrder: 'alphabetical', + }); + + assert.strictEqual(result[0].data.operationType, 'Query'); + }); + + it('should correctly map multiple root types', () => { + const mockQueryType = { + name: { value: 'Query' }, + fields: [], + directives: [], + }; + + const mockMutationType = { + name: { value: 'Mutation' }, + fields: [], + directives: [], + }; + + const result = mapRootTypesToEntities({ + rootTypeNodes: [mockQueryType, mockMutationType], + schemaRootTypesMap: ['Query', 'Mutation', 'Subscription'], + definitionCategoryByNameMap: {}, + fieldsOrder: 'alphabetical', + }); + + assert.strictEqual(result.length, 2); + assert.strictEqual(result[0].name, 'Query'); + assert.strictEqual(result[0].data.operationType, 'Query'); + assert.strictEqual(result[1].name, 'Mutation'); + assert.strictEqual(result[1].data.operationType, 'Mutation'); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 2); + assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 2); + }); + + it('should correctly map a root type with directives', () => { + const mockDirectiveResult = [{ directiveName: '@deprecated', rawArgumentValues: 'reason: "Use new API"' }]; + mapDirectivesUsageMock.mock.mockImplementationOnce(() => mockDirectiveResult); + + const mockQueryType = { + name: { value: 'Query' }, + fields: [], + directives: [ + { + name: { value: 'deprecated' }, + arguments: [{ name: { value: 'reason' }, value: { value: 'Use new API' } }], + }, + ], + }; + + const result = mapRootTypesToEntities({ + rootTypeNodes: [mockQueryType], + schemaRootTypesMap: ['Query', 'Mutation', 'Subscription'], + definitionCategoryByNameMap: {}, + fieldsOrder: 'alphabetical', + }); + + assert.deepStrictEqual(result[0].data.typeDirectives, mockDirectiveResult); + assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); + assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0].directives, mockQueryType.directives); + }); + + it('should correctly map a root type with complex field structures', () => { + const mockQueryType = { + name: { value: 'Query' }, + fields: [ + { + name: { value: 'user' }, + type: { kind: 'NAMED_TYPE', name: { value: 'User' } }, + directives: [], + }, + { + name: { value: 'users' }, + type: { + kind: 'LIST_TYPE', + type: { kind: 'NAMED_TYPE', name: { value: 'User' } }, + }, + directives: [], + }, + ], + directives: [], + }; + + // Mock getFieldsSchema to return complex field structure + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: { + user: { + name: 'user', + $ref: '#model/definitions/Objects/User', + required: false, + }, + users: { + name: 'users', + required: false, + type: 'List', + items: [ + { + $ref: '#model/definitions/Objects/User', + required: false, + }, + ], + }, + }, + required: [], + })); + + const expected = [ + { + name: 'Query', + data: { + type: 'object', + description: '', + operationType: 'Query', + typeDirectives: [], + properties: { + user: { + name: 'user', + $ref: '#model/definitions/Objects/User', + required: false, + }, + users: { + name: 'users', + required: false, + type: 'List', + items: [ + { + $ref: '#model/definitions/Objects/User', + required: false, + }, + ], + }, + }, + required: [], + }, + }, + ]; + + const result = mapRootTypesToEntities({ + rootTypeNodes: [mockQueryType], + schemaRootTypesMap: ['Query', 'Mutation', 'Subscription'], + definitionCategoryByNameMap: { User: 'Objects' }, + fieldsOrder: 'alphabetical', + }); + + assert.deepStrictEqual(result, expected); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + + // Verify getFieldsSchema received the right definition map + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.deepStrictEqual(fieldsSchemaParams.definitionCategoryByNameMap, { User: 'Objects' }); + }); +}); diff --git a/test/reverse_engineering/mappers/typeDefinitions/inputType.spec.js b/test/reverse_engineering/mappers/typeDefinitions/inputType.spec.js index 90a5a66..5dfef0f 100644 --- a/test/reverse_engineering/mappers/typeDefinitions/inputType.spec.js +++ b/test/reverse_engineering/mappers/typeDefinitions/inputType.spec.js @@ -2,13 +2,6 @@ const { describe, it, mock, afterEach } = require('node:test'); const assert = require('assert'); // Mock dependencies -const sortByNameMock = mock.fn(({ items }) => items); -mock.module('../../../../reverse_engineering/helpers/sortByName', { - namedExports: { - sortByName: sortByNameMock, - }, -}); - const mapDirectivesUsageMock = mock.fn(() => []); mock.module('../../../../reverse_engineering/mappers/directiveUsage', { namedExports: { @@ -16,13 +9,11 @@ mock.module('../../../../reverse_engineering/mappers/directiveUsage', { }, }); -const mapFieldMock = mock.fn(({ field }) => ({ - name: field.name.value, - required: field.type.kind === 'NON_NULL_TYPE', -})); +// Mock getFieldsSchema instead of mapField +const getFieldsSchemaMock = mock.fn(() => ({ properties: {}, required: [] })); mock.module('../../../../reverse_engineering/mappers/field', { namedExports: { - mapField: mapFieldMock, + getFieldsSchema: getFieldsSchemaMock, }, }); @@ -30,9 +21,8 @@ const { getInputObjectTypeDefinitions } = require('../../../../reverse_engineeri describe('getInputObjectTypeDefinitions', () => { afterEach(() => { - sortByNameMock.mock.resetCalls(); mapDirectivesUsageMock.mock.resetCalls(); - mapFieldMock.mock.resetCalls(); + getFieldsSchemaMock.mock.resetCalls(); }); it('should return an empty array when no input object types are provided', () => { @@ -42,6 +32,7 @@ describe('getInputObjectTypeDefinitions', () => { fieldsOrder: {}, }); assert.deepStrictEqual(result, []); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 0); }); it('should correctly map a simple input object type with no fields', () => { @@ -51,6 +42,12 @@ describe('getInputObjectTypeDefinitions', () => { directives: [], }; + // Mock getFieldsSchema to return empty properties and required arrays + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'input', @@ -70,8 +67,13 @@ describe('getInputObjectTypeDefinitions', () => { assert.deepStrictEqual(result, expected); assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); - assert.strictEqual(sortByNameMock.mock.calls.length, 1); - assert.strictEqual(mapFieldMock.mock.calls.length, 0); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + + // Verify the correct parameters were passed to getFieldsSchema + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.deepStrictEqual(fieldsSchemaParams.fields, []); + assert.deepStrictEqual(fieldsSchemaParams.definitionCategoryByNameMap, {}); + assert.deepStrictEqual(fieldsSchemaParams.fieldsOrder, {}); }); it('should correctly map an input object type with fields', () => { @@ -93,7 +95,15 @@ describe('getInputObjectTypeDefinitions', () => { directives: [], }; - // Expected result with the properties based on the mocked mapField responses + // Mock getFieldsSchema to return properties and required fields + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: { + username: { name: 'username', required: true }, + email: { name: 'email', required: false }, + }, + required: ['username'], + })); + const expected = [ { type: 'input', @@ -116,8 +126,11 @@ describe('getInputObjectTypeDefinitions', () => { assert.deepStrictEqual(result, expected); assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); - assert.strictEqual(sortByNameMock.mock.calls.length, 1); - assert.strictEqual(mapFieldMock.mock.calls.length, 2); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + + // Verify that getFieldsSchema was called with the correct fields + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.deepStrictEqual(fieldsSchemaParams.fields, mockInputObjectType.fields); }); it('should correctly map an input object type with fields with default values', () => { @@ -140,11 +153,13 @@ describe('getInputObjectTypeDefinitions', () => { directives: [], }; - // Mock mapField to return field with default value - mapFieldMock.mock.mockImplementation(({ field }) => ({ - name: field.name.value, - required: field.type.kind === 'NON_NULL_TYPE', - default: field.defaultValue ? field.defaultValue.value : undefined, + // Mock getFieldsSchema to return fields with default values + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: { + limit: { name: 'limit', required: false, default: 10 }, + sortBy: { name: 'sortBy', required: false, default: 'createdAt' }, + }, + required: [], })); const expected = [ @@ -152,7 +167,7 @@ describe('getInputObjectTypeDefinitions', () => { type: 'input', name: 'FilterInput', properties: { - limit: { name: 'limit', required: false, default: '10' }, + limit: { name: 'limit', required: false, default: 10 }, sortBy: { name: 'sortBy', required: false, default: 'createdAt' }, }, required: [], @@ -169,8 +184,11 @@ describe('getInputObjectTypeDefinitions', () => { assert.deepStrictEqual(result, expected); assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); - assert.strictEqual(sortByNameMock.mock.calls.length, 1); - assert.strictEqual(mapFieldMock.mock.calls.length, 2); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + + // Verify getFieldsSchema received fields with default values + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.deepStrictEqual(fieldsSchemaParams.fields, mockInputObjectType.fields); }); it('should correctly map an input object type with directives', () => { @@ -190,6 +208,12 @@ describe('getInputObjectTypeDefinitions', () => { ], }; + // Mock getFieldsSchema for empty fields + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'input', @@ -209,12 +233,13 @@ describe('getInputObjectTypeDefinitions', () => { assert.deepStrictEqual(result, expected); assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); - assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0], { - directives: mockInputObjectType.directives, - }); + assert.deepStrictEqual( + mapDirectivesUsageMock.mock.calls[0].arguments[0].directives, + mockInputObjectType.directives, + ); }); - it('should correctly handle fields order', () => { + it('should correctly pass fields order to getFieldsSchema', () => { const mockInputObjectType = { name: { value: 'OrderedInput' }, fields: [ @@ -225,37 +250,37 @@ describe('getInputObjectTypeDefinitions', () => { directives: [], }; - mapFieldMock.mock.mockImplementation(({ field }) => ({ - name: field.name.value, - required: false, - })); - // Test both fieldsOrder options const testCases = [ { fieldsOrder: 'alphabetical', - expectedOrder: ['fieldA', 'fieldB', 'fieldC'], // Alphabetical order + mockResult: { + properties: { + fieldA: { name: 'fieldA' }, + fieldB: { name: 'fieldB' }, + fieldC: { name: 'fieldC' }, + }, + required: [], + }, }, { fieldsOrder: 'field', - expectedOrder: ['fieldB', 'fieldA', 'fieldC'], // Original order + mockResult: { + properties: { + fieldB: { name: 'fieldB' }, + fieldA: { name: 'fieldA' }, + fieldC: { name: 'fieldC' }, + }, + required: [], + }, }, ]; for (const testCase of testCases) { - sortByNameMock.mock.resetCalls(); - mapFieldMock.mock.resetCalls(); - - // Mock sortByName to simulate behavior based on fieldsOrder value - if (testCase.fieldsOrder === 'alphabetical') { - // For 'alphabetical', sort alphabetically - sortByNameMock.mock.mockImplementationOnce(({ items }) => { - return [...items].sort((a, b) => a.name.localeCompare(b.name)); - }); - } else { - // For 'field', maintain original order - sortByNameMock.mock.mockImplementationOnce(({ items }) => items); - } + getFieldsSchemaMock.mock.resetCalls(); + + // Mock getFieldsSchema to return properties in the correct order + getFieldsSchemaMock.mock.mockImplementationOnce(() => testCase.mockResult); const result = getInputObjectTypeDefinitions({ inputObjectTypes: [mockInputObjectType], @@ -263,21 +288,16 @@ describe('getInputObjectTypeDefinitions', () => { fieldsOrder: testCase.fieldsOrder, }); - // Check that sortByName was called correctly - assert.strictEqual(sortByNameMock.mock.calls.length, 1); - assert.strictEqual(sortByNameMock.mock.calls[0].arguments[0].fieldsOrder, testCase.fieldsOrder); - - // Verify the result structure - assert.strictEqual(result.length, 1); - assert.strictEqual(result[0].name, 'OrderedInput'); + // Verify the result matches what getFieldsSchema returned + assert.deepStrictEqual(result[0].properties, testCase.mockResult.properties); - const propertyNames = Object.keys(result[0].properties); - - // Verify the properties are in the expected order for this test case - assert.deepStrictEqual( - propertyNames, - testCase.expectedOrder, - `Field order should be ${testCase.expectedOrder.join(', ')} when fieldsOrder is "${testCase.fieldsOrder}"`, + // Verify that getFieldsSchema was called with the correct fieldsOrder + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.strictEqual( + fieldsSchemaParams.fieldsOrder, + testCase.fieldsOrder, + `getFieldsSchema should be called with fieldsOrder: "${testCase.fieldsOrder}"`, ); } }); @@ -296,6 +316,12 @@ describe('getInputObjectTypeDefinitions', () => { }, ]; + // Mock getFieldsSchema to return empty properties for both calls + getFieldsSchemaMock.mock.mockImplementation(() => ({ + properties: {}, + required: [], + })); + const result = getInputObjectTypeDefinitions({ inputObjectTypes: mockInputObjectTypes, definitionCategoryByNameMap: {}, @@ -306,6 +332,7 @@ describe('getInputObjectTypeDefinitions', () => { assert.strictEqual(result[0].name, 'Input1'); assert.strictEqual(result[1].name, 'Input2'); assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 2); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 2); }); it('should handle undefined fields', () => { @@ -315,6 +342,12 @@ describe('getInputObjectTypeDefinitions', () => { directives: [], }; + // Mock getFieldsSchema to return empty properties + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'input', @@ -334,12 +367,10 @@ describe('getInputObjectTypeDefinitions', () => { assert.deepStrictEqual(result, expected); - // Verify no fields were processed - assert.strictEqual(mapFieldMock.mock.calls.length, 0); - - // Verify sortByName was still called (but with empty array) - assert.strictEqual(sortByNameMock.mock.calls.length, 1); - assert.deepStrictEqual(sortByNameMock.mock.calls[0].arguments[0].items, []); + // Verify getFieldsSchema was called with empty array + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.deepStrictEqual(fieldsSchemaParams.fields, []); }); it('should handle undefined directives', () => { @@ -349,6 +380,12 @@ describe('getInputObjectTypeDefinitions', () => { // directives is undefined }; + // Mock getFieldsSchema to return empty properties + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'input', @@ -368,12 +405,12 @@ describe('getInputObjectTypeDefinitions', () => { assert.deepStrictEqual(result, expected); - // Verify the mapDirectivesUsage was called with empty directives array + // Verify mapDirectivesUsage was called with empty directives array assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0].directives, []); }); - it('should correctly handle input types with complex nested references', () => { + it('should correctly handle input types with complex field structures', () => { const mockInputObjectType = { name: { value: 'ComplexInput' }, fields: [ @@ -394,29 +431,28 @@ describe('getInputObjectTypeDefinitions', () => { directives: [], }; - // Mock mapField to return different types of fields - mapFieldMock.mock.mockImplementationOnce( - ({ field }) => ({ - name: field.name.value, - required: false, - $ref: '#model/definitions/Input objects/NestedInput', - }), - 0, - ); - mapFieldMock.mock.mockImplementationOnce( - ({ field }) => ({ - name: field.name.value, - required: false, - type: 'List', - items: [ - { - $ref: '#model/definitions/Input objects/NestedInput', - required: false, - }, - ], - }), - 1, - ); + // Mock getFieldsSchema to return a complex structure with references and lists + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: { + nestedInput: { + name: 'nestedInput', + required: false, + $ref: '#model/definitions/Input objects/NestedInput', + }, + inputList: { + name: 'inputList', + required: false, + type: 'List', + items: [ + { + $ref: '#model/definitions/Input objects/NestedInput', + required: false, + }, + ], + }, + }, + required: [], + })); const expected = [ { @@ -455,6 +491,10 @@ describe('getInputObjectTypeDefinitions', () => { }); assert.deepStrictEqual(result, expected); - assert.strictEqual(mapFieldMock.mock.calls.length, 2); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + + // Verify getFieldsSchema received the right definition map + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.deepStrictEqual(fieldsSchemaParams.definitionCategoryByNameMap, { 'NestedInput': 'Input objects' }); }); }); diff --git a/test/reverse_engineering/mappers/typeDefinitions/interface.spec.js b/test/reverse_engineering/mappers/typeDefinitions/interface.spec.js index 7ed8617..a0db4c0 100644 --- a/test/reverse_engineering/mappers/typeDefinitions/interface.spec.js +++ b/test/reverse_engineering/mappers/typeDefinitions/interface.spec.js @@ -2,13 +2,6 @@ const { describe, it, mock, afterEach } = require('node:test'); const assert = require('assert'); // Mock dependencies -const sortByNameMock = mock.fn(({ items }) => items); -mock.module('../../../../reverse_engineering/helpers/sortByName', { - namedExports: { - sortByName: sortByNameMock, - }, -}); - const mapDirectivesUsageMock = mock.fn(() => []); mock.module('../../../../reverse_engineering/mappers/directiveUsage', { namedExports: { @@ -16,13 +9,11 @@ mock.module('../../../../reverse_engineering/mappers/directiveUsage', { }, }); -const mapFieldMock = mock.fn(({ field }) => ({ - name: field.name.value, - required: field.type.kind === 'NON_NULL_TYPE', -})); +// Mock getFieldsSchema instead of mapField +const getFieldsSchemaMock = mock.fn(() => ({ properties: {}, required: [] })); mock.module('../../../../reverse_engineering/mappers/field', { namedExports: { - mapField: mapFieldMock, + getFieldsSchema: getFieldsSchemaMock, }, }); @@ -37,9 +28,8 @@ const { getInterfaceDefinitions } = require('../../../../reverse_engineering/map describe('getInterfaceDefinitions', () => { afterEach(() => { - sortByNameMock.mock.resetCalls(); mapDirectivesUsageMock.mock.resetCalls(); - mapFieldMock.mock.resetCalls(); + getFieldsSchemaMock.mock.resetCalls(); mapImplementsInterfacesMock.mock.resetCalls(); }); @@ -50,6 +40,7 @@ describe('getInterfaceDefinitions', () => { fieldsOrder: {}, }); assert.deepStrictEqual(result, []); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 0); }); it('should correctly map a simple interface with no fields', () => { @@ -60,6 +51,12 @@ describe('getInterfaceDefinitions', () => { interfaces: [], }; + // Mock getFieldsSchema to return empty properties and required arrays + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'interface', @@ -80,9 +77,14 @@ describe('getInterfaceDefinitions', () => { assert.deepStrictEqual(result, expected); assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); - assert.strictEqual(sortByNameMock.mock.calls.length, 1); - assert.strictEqual(mapFieldMock.mock.calls.length, 0); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); assert.strictEqual(mapImplementsInterfacesMock.mock.calls.length, 1); + + // Verify the correct parameters were passed to getFieldsSchema + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.deepStrictEqual(fieldsSchemaParams.fields, []); + assert.deepStrictEqual(fieldsSchemaParams.definitionCategoryByNameMap, {}); + assert.deepStrictEqual(fieldsSchemaParams.fieldsOrder, {}); }); it('should correctly map an interface with fields', () => { @@ -105,7 +107,15 @@ describe('getInterfaceDefinitions', () => { interfaces: [], }; - // Expected result with the properties based on the mocked mapField responses + // Mock getFieldsSchema to return properties and required fields + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: { + id: { name: 'id', required: true }, + name: { name: 'name', required: false }, + }, + required: ['id'], + })); + const expected = [ { type: 'interface', @@ -129,9 +139,12 @@ describe('getInterfaceDefinitions', () => { assert.deepStrictEqual(result, expected); assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); - assert.strictEqual(sortByNameMock.mock.calls.length, 1); - assert.strictEqual(mapFieldMock.mock.calls.length, 2); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); assert.strictEqual(mapImplementsInterfacesMock.mock.calls.length, 1); + + // Verify that getFieldsSchema was called with the correct fields + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.deepStrictEqual(fieldsSchemaParams.fields, mockInterface.fields); }); it('should correctly map an interface with directives', () => { @@ -150,6 +163,12 @@ describe('getInterfaceDefinitions', () => { interfaces: [], }; + // Mock getFieldsSchema for empty fields + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'interface', @@ -170,9 +189,7 @@ describe('getInterfaceDefinitions', () => { assert.deepStrictEqual(result, expected); assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); - assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0], { - directives: mockInterface.directives, - }); + assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0].directives, mockInterface.directives); assert.strictEqual(mapImplementsInterfacesMock.mock.calls.length, 1); }); @@ -187,6 +204,12 @@ describe('getInterfaceDefinitions', () => { const mockImplementsResult = [{ interface: 'Node' }, { interface: 'Entity' }]; mapImplementsInterfacesMock.mock.mockImplementationOnce(() => mockImplementsResult); + // Mock getFieldsSchema for empty fields + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'interface', @@ -212,7 +235,7 @@ describe('getInterfaceDefinitions', () => { }); }); - it('should correctly handle fields order', () => { + it('should correctly pass fields order to getFieldsSchema', () => { const mockInterface = { name: { value: 'OrderedInterface' }, fields: [ @@ -224,39 +247,38 @@ describe('getInterfaceDefinitions', () => { interfaces: [], }; - mapFieldMock.mock.mockImplementation(({ field }) => ({ - name: field.name.value, - required: false, - })); - // Test both fieldsOrder options const testCases = [ { fieldsOrder: 'alphabetical', - expectedOrder: ['fieldA', 'fieldB', 'fieldC'], // Alphabetical order + mockResult: { + properties: { + fieldA: { name: 'fieldA' }, + fieldB: { name: 'fieldB' }, + fieldC: { name: 'fieldC' }, + }, + required: [], + }, }, { fieldsOrder: 'field', - expectedOrder: ['fieldB', 'fieldA', 'fieldC'], // Original order + mockResult: { + properties: { + fieldB: { name: 'fieldB' }, + fieldA: { name: 'fieldA' }, + fieldC: { name: 'fieldC' }, + }, + required: [], + }, }, ]; for (const testCase of testCases) { - // Reset mocks before each test case - sortByNameMock.mock.resetCalls(); - mapFieldMock.mock.resetCalls(); + getFieldsSchemaMock.mock.resetCalls(); mapImplementsInterfacesMock.mock.resetCalls(); - // Mock sortByName to simulate behavior based on fieldsOrder value - if (testCase.fieldsOrder === 'alphabetical') { - // For 'alphabetical', sort alphabetically - sortByNameMock.mock.mockImplementationOnce(({ items }) => { - return [...items].sort((a, b) => a.name.localeCompare(b.name)); - }); - } else { - // For 'field', maintain original order - sortByNameMock.mock.mockImplementationOnce(({ items }) => items); - } + // Mock getFieldsSchema to return properties in the correct order + getFieldsSchemaMock.mock.mockImplementationOnce(() => testCase.mockResult); const result = getInterfaceDefinitions({ interfaces: [mockInterface], @@ -264,21 +286,26 @@ describe('getInterfaceDefinitions', () => { fieldsOrder: testCase.fieldsOrder, }); - // Check that sortByName was called correctly - assert.strictEqual(sortByNameMock.mock.calls.length, 1); - assert.strictEqual(sortByNameMock.mock.calls[0].arguments[0].fieldsOrder, testCase.fieldsOrder); + // Verify the result matches what getFieldsSchema returned + assert.deepStrictEqual(result[0].properties, testCase.mockResult.properties); - // Verify the result structure - assert.strictEqual(result.length, 1); - assert.strictEqual(result[0].name, 'OrderedInterface'); + // Verify that getFieldsSchema was called with the correct fieldsOrder + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.strictEqual( + fieldsSchemaParams.fieldsOrder, + testCase.fieldsOrder, + `getFieldsSchema should be called with fieldsOrder: "${testCase.fieldsOrder}"`, + ); + // Verify property order in the result const propertyNames = Object.keys(result[0].properties); + const expectedOrder = Object.keys(testCase.mockResult.properties); - // Verify the properties are in the expected order for this test case assert.deepStrictEqual( propertyNames, - testCase.expectedOrder, - `Field order should be ${testCase.expectedOrder.join(', ')} when fieldsOrder is "${testCase.fieldsOrder}"`, + expectedOrder, + `Field order should be ${expectedOrder.join(', ')} when fieldsOrder is "${testCase.fieldsOrder}"`, ); } }); @@ -299,6 +326,12 @@ describe('getInterfaceDefinitions', () => { }, ]; + // Mock getFieldsSchema to return empty properties for both calls + getFieldsSchemaMock.mock.mockImplementation(() => ({ + properties: {}, + required: [], + })); + const result = getInterfaceDefinitions({ interfaces: mockInterfaces, definitionCategoryByNameMap: {}, @@ -309,6 +342,7 @@ describe('getInterfaceDefinitions', () => { assert.strictEqual(result[0].name, 'Interface1'); assert.strictEqual(result[1].name, 'Interface2'); assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 2); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 2); assert.strictEqual(mapImplementsInterfacesMock.mock.calls.length, 2); }); @@ -320,6 +354,12 @@ describe('getInterfaceDefinitions', () => { interfaces: [], }; + // Mock getFieldsSchema to return empty properties + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'interface', @@ -340,12 +380,10 @@ describe('getInterfaceDefinitions', () => { assert.deepStrictEqual(result, expected); - // Verify no fields were processed - assert.strictEqual(mapFieldMock.mock.calls.length, 0); - - // Verify sortByName was still called (but with empty array) - assert.strictEqual(sortByNameMock.mock.calls.length, 1); - assert.deepStrictEqual(sortByNameMock.mock.calls[0].arguments[0].items, []); + // Verify getFieldsSchema was called with empty array + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.deepStrictEqual(fieldsSchemaParams.fields, []); }); it('should handle undefined directives', () => { @@ -356,6 +394,12 @@ describe('getInterfaceDefinitions', () => { interfaces: [], }; + // Mock getFieldsSchema to return empty properties + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'interface', @@ -376,7 +420,7 @@ describe('getInterfaceDefinitions', () => { assert.deepStrictEqual(result, expected); - // Verify the mapDirectivesUsage was called with empty directives array + // Verify mapDirectivesUsage was called with empty directives array assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0].directives, []); }); @@ -389,6 +433,12 @@ describe('getInterfaceDefinitions', () => { // interfaces is undefined }; + // Mock getFieldsSchema to return empty properties + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'interface', @@ -413,4 +463,98 @@ describe('getInterfaceDefinitions', () => { assert.strictEqual(mapImplementsInterfacesMock.mock.calls.length, 1); assert.deepStrictEqual(mapImplementsInterfacesMock.mock.calls[0].arguments[0].implementsInterfaces, []); }); + + it('should correctly handle interfaces with complex field structures', () => { + const mockInterface = { + name: { value: 'ComplexInterface' }, + fields: [ + { + name: { value: 'reference' }, + type: { kind: 'NAMED_TYPE', name: { value: 'SomeType' } }, + directives: [], + }, + { + name: { value: 'list' }, + type: { + kind: 'LIST_TYPE', + type: { kind: 'NAMED_TYPE', name: { value: 'OtherType' } }, + }, + directives: [], + }, + ], + directives: [], + interfaces: [], + }; + + // Mock getFieldsSchema to return a complex structure with references and lists + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: { + reference: { + name: 'reference', + required: false, + $ref: '#model/definitions/Objects/SomeType', + }, + list: { + name: 'list', + required: false, + type: 'List', + items: [ + { + $ref: '#model/definitions/Objects/OtherType', + required: false, + }, + ], + }, + }, + required: [], + })); + + const expected = [ + { + type: 'interface', + name: 'ComplexInterface', + properties: { + reference: { + name: 'reference', + required: false, + $ref: '#model/definitions/Objects/SomeType', + }, + list: { + name: 'list', + required: false, + type: 'List', + items: [ + { + $ref: '#model/definitions/Objects/OtherType', + required: false, + }, + ], + }, + }, + required: [], + description: '', + typeDirectives: [], + implementsInterfaces: [], + }, + ]; + + const result = getInterfaceDefinitions({ + interfaces: [mockInterface], + definitionCategoryByNameMap: { + SomeType: 'Objects', + OtherType: 'Objects', + }, + fieldsOrder: {}, + }); + + assert.deepStrictEqual(result, expected); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + + // Verify getFieldsSchema received the right definition map + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.deepStrictEqual(fieldsSchemaParams.definitionCategoryByNameMap, { + SomeType: 'Objects', + OtherType: 'Objects', + }); + }); }); diff --git a/test/reverse_engineering/mappers/typeDefinitions/objectType.spec.js b/test/reverse_engineering/mappers/typeDefinitions/objectType.spec.js index 9cae004..9d5aa12 100644 --- a/test/reverse_engineering/mappers/typeDefinitions/objectType.spec.js +++ b/test/reverse_engineering/mappers/typeDefinitions/objectType.spec.js @@ -2,13 +2,6 @@ const { describe, it, mock, afterEach } = require('node:test'); const assert = require('assert'); // Mock dependencies -const sortByNameMock = mock.fn(({ items }) => items); -mock.module('../../../../reverse_engineering/helpers/sortByName', { - namedExports: { - sortByName: sortByNameMock, - }, -}); - const mapDirectivesUsageMock = mock.fn(() => []); mock.module('../../../../reverse_engineering/mappers/directiveUsage', { namedExports: { @@ -16,14 +9,11 @@ mock.module('../../../../reverse_engineering/mappers/directiveUsage', { }, }); -const mapFieldMock = mock.fn(({ field }) => ({ - name: field.name.value, - required: field.type.kind === 'NON_NULL_TYPE', - // Add other properties that mapField would return -})); +// Mock getFieldsSchema instead of mapField +const getFieldsSchemaMock = mock.fn(() => ({ properties: {}, required: [] })); mock.module('../../../../reverse_engineering/mappers/field', { namedExports: { - mapField: mapFieldMock, + getFieldsSchema: getFieldsSchemaMock, }, }); @@ -38,9 +28,8 @@ const { getObjectTypeDefinitions } = require('../../../../reverse_engineering/ma describe('getObjectTypeDefinitions', () => { afterEach(() => { - sortByNameMock.mock.resetCalls(); mapDirectivesUsageMock.mock.resetCalls(); - mapFieldMock.mock.resetCalls(); + getFieldsSchemaMock.mock.resetCalls(); mapImplementsInterfacesMock.mock.resetCalls(); }); @@ -51,6 +40,7 @@ describe('getObjectTypeDefinitions', () => { fieldsOrder: {}, }); assert.deepStrictEqual(result, []); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 0); }); it('should correctly map a simple object type with no fields', () => { @@ -61,6 +51,12 @@ describe('getObjectTypeDefinitions', () => { interfaces: [], }; + // Mock getFieldsSchema to return empty properties and required arrays + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'object', @@ -81,12 +77,14 @@ describe('getObjectTypeDefinitions', () => { assert.deepStrictEqual(result, expected); assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); - assert.strictEqual(sortByNameMock.mock.calls.length, 1); - assert.strictEqual(mapFieldMock.mock.calls.length, 0); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); assert.strictEqual(mapImplementsInterfacesMock.mock.calls.length, 1); - assert.deepStrictEqual(mapImplementsInterfacesMock.mock.calls[0].arguments[0], { - implementsInterfaces: mockObjectType.interfaces, - }); + + // Verify the correct parameters were passed to getFieldsSchema + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.deepStrictEqual(fieldsSchemaParams.fields, []); + assert.deepStrictEqual(fieldsSchemaParams.definitionCategoryByNameMap, {}); + assert.deepStrictEqual(fieldsSchemaParams.fieldsOrder, {}); }); it('should correctly map an object type with fields', () => { @@ -106,15 +104,18 @@ describe('getObjectTypeDefinitions', () => { }, ], directives: [], + interfaces: [], }; - // Setup the mapField mock to return expected values - mapFieldMock.mock.mockImplementation(({ field }) => ({ - name: field.name.value, - required: field.type.kind === 'NON_NULL_TYPE', + // Mock getFieldsSchema to return properties and required fields + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: { + id: { name: 'id', required: true }, + name: { name: 'name', required: false }, + }, + required: ['id'], })); - // Expected result with the properties based on the mocked mapField responses const expected = [ { type: 'object', @@ -138,8 +139,12 @@ describe('getObjectTypeDefinitions', () => { assert.deepStrictEqual(result, expected); assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); - assert.strictEqual(sortByNameMock.mock.calls.length, 1); - assert.strictEqual(mapFieldMock.mock.calls.length, 2); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + assert.strictEqual(mapImplementsInterfacesMock.mock.calls.length, 1); + + // Verify that getFieldsSchema was called with the correct fields + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.deepStrictEqual(fieldsSchemaParams.fields, mockObjectType.fields); }); it('should correctly map an object type with directives', () => { @@ -158,6 +163,12 @@ describe('getObjectTypeDefinitions', () => { interfaces: [], }; + // Mock getFieldsSchema for empty fields + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'object', @@ -178,12 +189,9 @@ describe('getObjectTypeDefinitions', () => { assert.deepStrictEqual(result, expected); assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); - assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0], { - directives: mockObjectType.directives, - }); + assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0].directives, mockObjectType.directives); }); - // Add new test for implements interfaces it('should correctly map an object type that implements interfaces', () => { const mockObjectType = { name: { value: 'User' }, @@ -195,6 +203,12 @@ describe('getObjectTypeDefinitions', () => { const mockInterfacesResult = [{ interface: 'Node' }, { interface: 'Entity' }]; mapImplementsInterfacesMock.mock.mockImplementationOnce(() => mockInterfacesResult); + // Mock getFieldsSchema for empty fields + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'object', @@ -232,38 +246,38 @@ describe('getObjectTypeDefinitions', () => { interfaces: [], }; - mapFieldMock.mock.mockImplementation(({ field }) => ({ - name: field.name.value, - required: false, - })); - // Test both fieldsOrder options const testCases = [ { fieldsOrder: 'alphabetical', - expectedOrder: ['fieldA', 'fieldB', 'fieldC'], // Alphabetical order + mockResult: { + properties: { + fieldA: { name: 'fieldA' }, + fieldB: { name: 'fieldB' }, + fieldC: { name: 'fieldC' }, + }, + required: [], + }, }, { fieldsOrder: 'field', - expectedOrder: ['fieldB', 'fieldA', 'fieldC'], // Original order + mockResult: { + properties: { + fieldB: { name: 'fieldB' }, + fieldA: { name: 'fieldA' }, + fieldC: { name: 'fieldC' }, + }, + required: [], + }, }, ]; for (const testCase of testCases) { - // Reset mocks before each test case - sortByNameMock.mock.resetCalls(); - mapFieldMock.mock.resetCalls(); - - // Mock sortByName to simulate behavior based on fieldsOrder value - if (testCase.fieldsOrder === 'alphabetical') { - // For 'alphabetical', sort alphabetically - sortByNameMock.mock.mockImplementationOnce(({ items }) => { - return [...items].sort((a, b) => a.name.localeCompare(b.name)); - }); - } else { - // For 'field', maintain original order - sortByNameMock.mock.mockImplementationOnce(({ items }) => items); - } + getFieldsSchemaMock.mock.resetCalls(); + mapImplementsInterfacesMock.mock.resetCalls(); + + // Mock getFieldsSchema to return properties in the correct order + getFieldsSchemaMock.mock.mockImplementationOnce(() => testCase.mockResult); const result = getObjectTypeDefinitions({ objectTypes: [mockObjectType], @@ -271,21 +285,26 @@ describe('getObjectTypeDefinitions', () => { fieldsOrder: testCase.fieldsOrder, }); - // Check that sortByName was called correctly - assert.strictEqual(sortByNameMock.mock.calls.length, 1); - assert.strictEqual(sortByNameMock.mock.calls[0].arguments[0].fieldsOrder, testCase.fieldsOrder); + // Verify the result matches what getFieldsSchema returned + assert.deepStrictEqual(result[0].properties, testCase.mockResult.properties); - // Verify the result structure - assert.strictEqual(result.length, 1); - assert.strictEqual(result[0].name, 'OrderedType'); + // Verify that getFieldsSchema was called with the correct fieldsOrder + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.strictEqual( + fieldsSchemaParams.fieldsOrder, + testCase.fieldsOrder, + `getFieldsSchema should be called with fieldsOrder: "${testCase.fieldsOrder}"`, + ); + // Verify property order in the result const propertyNames = Object.keys(result[0].properties); + const expectedOrder = Object.keys(testCase.mockResult.properties); - // Verify the properties are in the expected order for this test case assert.deepStrictEqual( propertyNames, - testCase.expectedOrder, - `Field order should be ${testCase.expectedOrder.join(', ')} when fieldsOrder is "${testCase.fieldsOrder}"`, + expectedOrder, + `Field order should be ${expectedOrder.join(', ')} when fieldsOrder is "${testCase.fieldsOrder}"`, ); } }); @@ -306,6 +325,12 @@ describe('getObjectTypeDefinitions', () => { }, ]; + // Mock getFieldsSchema to return empty properties for both calls + getFieldsSchemaMock.mock.mockImplementation(() => ({ + properties: {}, + required: [], + })); + const result = getObjectTypeDefinitions({ objectTypes: mockObjectTypes, definitionCategoryByNameMap: {}, @@ -316,6 +341,8 @@ describe('getObjectTypeDefinitions', () => { assert.strictEqual(result[0].name, 'Type1'); assert.strictEqual(result[1].name, 'Type2'); assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 2); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 2); + assert.strictEqual(mapImplementsInterfacesMock.mock.calls.length, 2); }); it('should handle undefined fields', () => { @@ -326,6 +353,12 @@ describe('getObjectTypeDefinitions', () => { interfaces: [], }; + // Mock getFieldsSchema to return empty properties + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'object', @@ -346,12 +379,10 @@ describe('getObjectTypeDefinitions', () => { assert.deepStrictEqual(result, expected); - // Verify no fields were processed - assert.strictEqual(mapFieldMock.mock.calls.length, 0); - - // Verify sortByName was still called (but with empty array) - assert.strictEqual(sortByNameMock.mock.calls.length, 1); - assert.deepStrictEqual(sortByNameMock.mock.calls[0].arguments[0].items, []); + // Verify getFieldsSchema was called with empty array + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.deepStrictEqual(fieldsSchemaParams.fields, []); }); it('should handle undefined directives', () => { @@ -362,6 +393,12 @@ describe('getObjectTypeDefinitions', () => { interfaces: [], }; + // Mock getFieldsSchema to return empty properties + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'object', @@ -382,12 +419,11 @@ describe('getObjectTypeDefinitions', () => { assert.deepStrictEqual(result, expected); - // Verify the mapDirectivesUsage was called with empty directives array + // Verify mapDirectivesUsage was called with empty directives array assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1); assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0].directives, []); }); - // Add test for undefined interfaces it('should handle undefined interfaces', () => { const mockObjectType = { name: { value: 'TypeWithoutInterfaces' }, @@ -396,6 +432,12 @@ describe('getObjectTypeDefinitions', () => { // interfaces is undefined }; + // Mock getFieldsSchema to return empty properties + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: {}, + required: [], + })); + const expected = [ { type: 'object', @@ -420,4 +462,98 @@ describe('getObjectTypeDefinitions', () => { assert.strictEqual(mapImplementsInterfacesMock.mock.calls.length, 1); assert.deepStrictEqual(mapImplementsInterfacesMock.mock.calls[0].arguments[0].implementsInterfaces, []); }); + + it('should correctly handle objects with complex field structures', () => { + const mockObjectType = { + name: { value: 'ComplexObject' }, + fields: [ + { + name: { value: 'reference' }, + type: { kind: 'NAMED_TYPE', name: { value: 'SomeType' } }, + directives: [], + }, + { + name: { value: 'list' }, + type: { + kind: 'LIST_TYPE', + type: { kind: 'NAMED_TYPE', name: { value: 'OtherType' } }, + }, + directives: [], + }, + ], + directives: [], + interfaces: [], + }; + + // Mock getFieldsSchema to return a complex structure with references and lists + getFieldsSchemaMock.mock.mockImplementationOnce(() => ({ + properties: { + reference: { + name: 'reference', + required: false, + $ref: '#model/definitions/Objects/SomeType', + }, + list: { + name: 'list', + required: false, + type: 'List', + items: [ + { + $ref: '#model/definitions/Objects/OtherType', + required: false, + }, + ], + }, + }, + required: [], + })); + + const expected = [ + { + type: 'object', + name: 'ComplexObject', + properties: { + reference: { + name: 'reference', + required: false, + $ref: '#model/definitions/Objects/SomeType', + }, + list: { + name: 'list', + required: false, + type: 'List', + items: [ + { + $ref: '#model/definitions/Objects/OtherType', + required: false, + }, + ], + }, + }, + required: [], + description: '', + typeDirectives: [], + implementsInterfaces: [], + }, + ]; + + const result = getObjectTypeDefinitions({ + objectTypes: [mockObjectType], + definitionCategoryByNameMap: { + SomeType: 'Objects', + OtherType: 'Objects', + }, + fieldsOrder: {}, + }); + + assert.deepStrictEqual(result, expected); + assert.strictEqual(getFieldsSchemaMock.mock.calls.length, 1); + + // Verify getFieldsSchema received the right definition map + const fieldsSchemaParams = getFieldsSchemaMock.mock.calls[0].arguments[0]; + assert.deepStrictEqual(fieldsSchemaParams.definitionCategoryByNameMap, { + SomeType: 'Objects', + OtherType: 'Objects', + }); + }); });