/* eslint-disable import/first, import/order */ import { mockAggCompassRequests, mockCreateComponent, mockCreateExternalAlias, mockDeleteExternalAlias, mockGetComponent, mockGetComponentByExternalAlias, mockUpdateComponent, } from './helpers/mockCompassRequests'; mockAggCompassRequests(); import api from '../../index'; import { ConfigFileActions, CreateCompassComponentExternalAliasInput, DeleteCompassComponentExternalAliasInput, UpdateComponentInput, } from '@atlassian/forge-graphql-types'; import { MOCK_CAPITALIZED_COMPONENT_NAME, MOCK_CAPITALIZED_NEW_PATH, MOCK_CLOUD_ID, MOCK_COMPONENT_CAPITALIZED_OLD_PATH, MOCK_COMPONENT_ID, MOCK_COMPONENT_ID_2, MOCK_COMPONENT_NAME, MOCK_COMPONENT_OLD_PATH, MOCK_COMPONENT_PATH, MOCK_DEDUPLICATION_ID, MOCK_NEW_PATH, getManagedComponentAliases, getMockedCompassYaml, getMockedComponent, } from '../fixtures/mocks'; import { ModificationNotSupportedForThatType, UnableToFindComponentDuringUpdateError, UnexpectedConfigFileActionError, } from '../../requests/config-as-code-requests/helpers/createOrUpdateComponent/errors'; import { DEFAULT_COMPONENT_TYPE_ID, EXTERNAL_ALIAS_SOURCE_COMPONENT_NAME, EXTERNAL_ALIAS_SOURCE_COMPONENT_PATH_TO_FILE, } from '../../requests/config-as-code-requests/helpers/createOrUpdateComponent/constants'; import { generateExternalIdWithPrefix } from '../../requests/config-as-code-requests/helpers/createOrUpdateComponent/helpers'; import { createOrUpdateComponent } from '../../requests/config-as-code-requests/helpers/createOrUpdateComponent/createOrUpdateComponent'; import { InvalidConfigFileError } from '../../requests/config-as-code-requests/helpers/validate-config-file/models/errors'; import { mockUpdateDataManager } from '../compass-requests/helpers/requestMocks'; import { mockAggConfigAsCodeRequests, mockUnlinkComponent, } from './helpers/mockConfigAsCodeRequests'; import { COMPONENT_NOT_FOUND } from '../../helpers'; import { HELLO_CLOUD_ID, HELLO_RESTRICTED_TYPE } from '../../helpers/constants'; const compassApp = api.compass.asApp(); const compassConfigAsCodeApp = api.compass.configAsCode.asApp(); const expectedDefaultUpdateParams = { customFields: [], dataManager: { externalSourceURL: undefined, }, description: 'description', fields: { tier: ['1'], }, id: MOCK_COMPONENT_ID, links: [], name: MOCK_COMPONENT_NAME, ownerId: 'ownerId', relationships: [{ type: 'DEPENDS_ON', nodeId: MOCK_COMPONENT_ID_2 }], typeId: 'service', } as UpdateComponentInput; const updateDataManagerSpy = jest.spyOn(compassApp, 'updateDataManager'); describe('createComponentFromYaml', () => { beforeEach(() => { jest.resetAllMocks(); mockAggCompassRequests(); mockAggConfigAsCodeRequests(compassConfigAsCodeApp); mockUpdateComponent.mockResolvedValueOnce({ success: true, data: { component: {} }, errors: [], }); }); it('should throw ModificationNotSupportedForThatType error for restricted type on HELLO_CLOUD_ID if the component has been synced', async () => { const restrictedTypeComponent = getMockedComponent({ typeId: HELLO_RESTRICTED_TYPE, }); mockGetComponent.mockResolvedValueOnce({ success: true, data: { component: restrictedTypeComponent }, errors: [], }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ // Get the alias, does exist success: true, data: { component: restrictedTypeComponent }, errors: [], }); await expect( createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: HELLO_CLOUD_ID, componentYaml: getMockedCompassYaml({ id: restrictedTypeComponent.id, typeId: 'OTHER', }), deduplicationId: MOCK_DEDUPLICATION_ID, configFileAction: ConfigFileActions.UPDATE, newPath: MOCK_NEW_PATH, }), ).rejects.toThrow(ModificationNotSupportedForThatType); expect(mockCreateComponent).not.toBeCalled(); expect(mockUpdateComponent).not.toBeCalled(); }); it('should throw ModificationNotSupportedForThatType error for restricted type on HELLO_CLOUD_ID if the component has not been synced', async () => { const restrictedTypeComponent = getMockedComponent({ typeId: HELLO_RESTRICTED_TYPE, }); mockGetComponent.mockResolvedValueOnce({ success: true, data: { component: restrictedTypeComponent }, errors: [], }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ // Get the alias, does exist success: true, data: {}, errors: [], }); await expect( createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: HELLO_CLOUD_ID, componentYaml: getMockedCompassYaml({ id: restrictedTypeComponent.id, typeId: 'OTHER', }), deduplicationId: MOCK_DEDUPLICATION_ID, configFileAction: ConfigFileActions.UPDATE, newPath: MOCK_NEW_PATH, }), ).rejects.toThrow(ModificationNotSupportedForThatType); expect(mockCreateComponent).not.toBeCalled(); expect(mockUpdateComponent).not.toBeCalled(); }); it('should not create new component, because component already exists and this is an update', async () => { const componentPath = 'src/location'; const component = getMockedComponent({ externalAliases: getManagedComponentAliases({ path: componentPath }), }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ // Get the alias, does exist success: true, data: { component }, errors: [], }); await createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, componentYaml: getMockedCompassYaml({ name: component.name, id: null }), deduplicationId: MOCK_DEDUPLICATION_ID, configFileAction: ConfigFileActions.UPDATE, oldPath: componentPath, newPath: componentPath, // note: no change }); expect(mockCreateComponent).not.toBeCalled(); expect(mockCreateExternalAlias).not.toBeCalled(); // no alias needs updating expect(mockUpdateComponent).toBeCalledWith( expect.objectContaining(expectedDefaultUpdateParams), ); }); it('should not create new component, because component already exists and this is an update, but it should update the path alias', async () => { const oldComponentPath = 'src/location'; const component = getMockedComponent({ externalAliases: getManagedComponentAliases({ path: oldComponentPath }), }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ // Get the alias, does exist success: true, data: { component }, errors: [], }); await createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, componentYaml: getMockedCompassYaml({ id: null }), deduplicationId: MOCK_DEDUPLICATION_ID, configFileAction: ConfigFileActions.UPDATE, oldPath: oldComponentPath, newPath: MOCK_NEW_PATH, // note: there is a change }); expect(mockCreateComponent).not.toBeCalled(); expect(mockDeleteExternalAlias).toBeCalledWith({ componentId: component.id, externalAlias: { externalId: `${MOCK_DEDUPLICATION_ID}:${oldComponentPath}`, externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_PATH_TO_FILE, }, } as DeleteCompassComponentExternalAliasInput); expect(mockCreateExternalAlias).toBeCalledWith({ componentId: component.id, externalAlias: { externalId: `${MOCK_DEDUPLICATION_ID}:${MOCK_NEW_PATH}`, externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_PATH_TO_FILE, }, } as CreateCompassComponentExternalAliasInput); expect(mockUpdateComponent).toBeCalledWith( expect.objectContaining(expectedDefaultUpdateParams), ); }); it('should deal with a create call with a collision alias match by creating a component with errors set in the data manager', async () => { const component = getMockedComponent({ externalAliases: getManagedComponentAliases({}), }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ // Get the invalid alias, does exist success: true, data: { component }, errors: [], }); mockCreateComponent.mockResolvedValueOnce({ success: true, data: { component }, errors: [], }); mockUpdateDataManager(updateDataManagerSpy); await expect( createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, componentYaml: getMockedCompassYaml({ id: null }), deduplicationId: MOCK_DEDUPLICATION_ID, configFileAction: ConfigFileActions.CREATE, newPath: MOCK_NEW_PATH, }), ).rejects.toThrow(InvalidConfigFileError); expect(mockCreateComponent).toBeCalled(); expect(updateDataManagerSpy).toMatchSnapshot(); }); it('should deal with an update call with a collision alias match', async () => { const oldComponentPath = 'some/path'; const component = getMockedComponent({ externalAliases: getManagedComponentAliases({ path: MOCK_NEW_PATH }), }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: true, data: { component }, errors: [], }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: true, data: { component }, errors: [], }); mockUpdateDataManager(updateDataManagerSpy); await expect( createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, componentYaml: getMockedCompassYaml({ id: null }), deduplicationId: MOCK_DEDUPLICATION_ID, configFileAction: ConfigFileActions.UPDATE, newPath: MOCK_NEW_PATH, oldPath: oldComponentPath, }), ).rejects.toThrow(InvalidConfigFileError); expect(mockDeleteExternalAlias).toBeCalledWith({ componentId: component.id, externalAlias: { externalId: component.externalAliases[0].externalAliasId, externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_NAME, }, }); expect(updateDataManagerSpy).toMatchSnapshot(); }); it('should not delete name alias in UPDATE action in case when component doesn`t have name alias and have the same oldPath and newPath', async () => { const component = getMockedComponent({ externalAliases: getManagedComponentAliases({ path: MOCK_NEW_PATH, }), }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: true, data: { component }, errors: [], }); const componentByPathAlias = getMockedComponent({ externalAliases: [ getManagedComponentAliases({ path: MOCK_COMPONENT_PATH })[1], ], }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: true, data: { component: componentByPathAlias }, errors: [], }); mockUpdateDataManager(updateDataManagerSpy); await expect( createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, componentYaml: getMockedCompassYaml({ id: null }), deduplicationId: MOCK_DEDUPLICATION_ID, configFileAction: ConfigFileActions.UPDATE, newPath: MOCK_COMPONENT_PATH, oldPath: MOCK_COMPONENT_PATH, }), ).rejects.toThrow(InvalidConfigFileError); expect(mockDeleteExternalAlias).not.toBeCalled(); expect(updateDataManagerSpy).toMatchSnapshot(); }); it('should throw error in case when no component by path alias', async () => { const oldComponentPath = 'some/path'; const component = getMockedComponent({ externalAliases: getManagedComponentAliases({ path: MOCK_NEW_PATH }), }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: true, data: { component }, errors: [], }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: false, data: { component: null }, errors: [], }); await expect( createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, componentYaml: getMockedCompassYaml({ id: null }), deduplicationId: MOCK_DEDUPLICATION_ID, configFileAction: ConfigFileActions.UPDATE, newPath: MOCK_NEW_PATH, oldPath: oldComponentPath, }), ).rejects.toThrow(UnableToFindComponentDuringUpdateError); }); it('should create new component of a specified non default type, because component not found by externalAlias', async () => { const nonDefaultComponentType = 'application'; const component = getMockedComponent({ externalAliases: getManagedComponentAliases({}), typeId: nonDefaultComponentType, }); mockGetComponentByExternalAlias.mockResolvedValue({ success: true, // checking the name and path alias, both return null data: { component: null }, errors: [], }); mockCreateComponent.mockResolvedValue({ success: true, errors: [], data: { component: { id: component.id, name: component.name } }, }); mockGetComponent.mockResolvedValue({ success: true, errors: [], data: { component }, }); await createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, componentYaml: getMockedCompassYaml({ name: component.name, id: null, typeId: nonDefaultComponentType, fields: {}, }), deduplicationId: MOCK_DEDUPLICATION_ID, configFileAction: ConfigFileActions.CREATE, newPath: MOCK_NEW_PATH, }); expect(mockCreateComponent).toBeCalledWith({ cloudId: MOCK_CLOUD_ID, name: component.name, typeId: component.typeId, externalAlias: { externalId: `${MOCK_DEDUPLICATION_ID}:${MOCK_NEW_PATH}`, externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_PATH_TO_FILE, }, options: { createdFromFile: true, }, }); expect(mockCreateExternalAlias).toBeCalledWith({ componentId: component.id, externalAlias: { externalId: generateExternalIdWithPrefix( MOCK_DEDUPLICATION_ID, component.name, ), externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_NAME, }, }); expect(mockUpdateComponent).toBeCalledWith( expect.objectContaining({ ...expectedDefaultUpdateParams, fields: null, typeId: nonDefaultComponentType, }), ); }); it('should create new component of a specified non default type with capitalized name and path, because component not found by externalAlias', async () => { const nonDefaultComponentType = 'application'; const component = getMockedComponent({ externalAliases: getManagedComponentAliases({}), typeId: nonDefaultComponentType, }); mockGetComponentByExternalAlias.mockResolvedValue({ success: true, // checking the name and path alias, both return null data: { component: null }, errors: [], }); mockCreateComponent.mockResolvedValue({ success: true, errors: [], data: { component: { id: component.id, name: component.name } }, }); mockGetComponent.mockResolvedValue({ success: true, errors: [], data: { component }, }); await createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, componentYaml: getMockedCompassYaml({ name: MOCK_CAPITALIZED_COMPONENT_NAME, id: null, typeId: nonDefaultComponentType, fields: {}, }), deduplicationId: MOCK_DEDUPLICATION_ID, configFileAction: ConfigFileActions.CREATE, newPath: MOCK_CAPITALIZED_NEW_PATH, }); expect(mockCreateComponent).toBeCalledWith({ cloudId: MOCK_CLOUD_ID, name: MOCK_CAPITALIZED_COMPONENT_NAME, typeId: component.typeId, externalAlias: { externalId: `${MOCK_DEDUPLICATION_ID}:${MOCK_NEW_PATH}`, externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_PATH_TO_FILE, }, options: { createdFromFile: true, }, }); expect(mockCreateExternalAlias).toBeCalledWith({ componentId: component.id, externalAlias: { externalId: generateExternalIdWithPrefix( MOCK_DEDUPLICATION_ID, component.name, ), externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_NAME, }, }); expect(mockUpdateComponent).toBeCalledWith( expect.objectContaining({ ...expectedDefaultUpdateParams, name: MOCK_CAPITALIZED_COMPONENT_NAME, fields: null, typeId: nonDefaultComponentType, }), ); }); it('should create new component, with the type as DEFAULT_COMPONENT_TYPE_ID if typeId is undefined', async () => { const component = getMockedComponent({ externalAliases: getManagedComponentAliases({}), }); mockGetComponentByExternalAlias.mockResolvedValue({ success: true, // checking the invalid key alias and checking the valid key alias data: { component: null }, errors: [], }); mockCreateComponent.mockResolvedValue({ success: true, errors: [], data: { component: { id: component.id, name: component.name } }, }); mockGetComponent.mockResolvedValue({ success: true, errors: [], data: { component }, }); await createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, componentYaml: getMockedCompassYaml({ name: component.name, id: null, typeId: null, }), deduplicationId: MOCK_DEDUPLICATION_ID, configFileAction: ConfigFileActions.CREATE, newPath: MOCK_NEW_PATH, }); expect(mockCreateComponent).toBeCalledWith({ cloudId: MOCK_CLOUD_ID, externalAlias: { externalId: `${MOCK_DEDUPLICATION_ID}:${MOCK_NEW_PATH}`, externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_PATH_TO_FILE, }, name: component.name, options: { createdFromFile: true, }, typeId: DEFAULT_COMPONENT_TYPE_ID, }); expect(mockCreateExternalAlias).toBeCalledWith({ componentId: component.id, externalAlias: { externalId: `${MOCK_DEDUPLICATION_ID}:${component.name}`, externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_NAME, }, }); expect(mockUpdateComponent).toBeCalledWith( expect.objectContaining(expectedDefaultUpdateParams), ); }); it('should not make new component, because getComponentByExtenalAlias returns unexpected error', async () => { mockGetComponentByExternalAlias.mockResolvedValue({ success: false, // checking the invalid key alias and checking the valid key alias data: { component: null }, errors: [{ message: 'Error' }], }); await expect( createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, componentYaml: getMockedCompassYaml({ id: null }), deduplicationId: MOCK_DEDUPLICATION_ID, configFileAction: ConfigFileActions.UPDATE, newPath: MOCK_NEW_PATH, oldPath: MOCK_COMPONENT_OLD_PATH, }), ).rejects.toThrow(UnableToFindComponentDuringUpdateError); expect(mockCreateComponent).not.toBeCalled(); }); it('should create new component with UPDATE action, if component not found by path', async () => { const component = getMockedComponent({ externalAliases: getManagedComponentAliases({}), }); mockGetComponentByExternalAlias.mockResolvedValue({ success: false, data: { component: null }, errors: [{ message: COMPONENT_NOT_FOUND }], }); mockCreateComponent.mockResolvedValue({ success: true, errors: [], data: { component: { id: component.id, name: component.name } }, }); mockGetComponent.mockResolvedValue({ success: true, errors: [], data: { component }, }); await createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, componentYaml: getMockedCompassYaml({ id: null }), deduplicationId: MOCK_DEDUPLICATION_ID, configFileAction: ConfigFileActions.UPDATE, newPath: MOCK_NEW_PATH, oldPath: MOCK_COMPONENT_OLD_PATH, }); expect(mockCreateComponent).toBeCalledWith({ cloudId: MOCK_CLOUD_ID, externalAlias: { externalId: `${MOCK_DEDUPLICATION_ID}:${MOCK_NEW_PATH}`, externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_PATH_TO_FILE, }, name: component.name, options: { createdFromFile: true, }, typeId: DEFAULT_COMPONENT_TYPE_ID, }); expect(mockCreateExternalAlias).toBeCalledWith({ componentId: component.id, externalAlias: { externalId: `${MOCK_DEDUPLICATION_ID}:${component.name}`, externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_NAME, }, }); expect(mockUpdateComponent).toBeCalledWith( expect.objectContaining(expectedDefaultUpdateParams), ); }); it('should not make new component, when the configFileAction is not recognized', async () => { const component = getMockedComponent({ externalAliases: getManagedComponentAliases({}), }); mockGetComponentByExternalAlias.mockResolvedValue({ success: true, // checking the invalid key alias and checking the valid key alias data: { component: null }, errors: [], }); mockGetComponent.mockResolvedValue({ success: true, errors: [], data: { component }, }); await expect( createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, componentYaml: getMockedCompassYaml({ id: null }), deduplicationId: MOCK_DEDUPLICATION_ID, configFileAction: 'random' as unknown as ConfigFileActions.UPDATE, newPath: MOCK_NEW_PATH, }), ).rejects.toThrow(UnexpectedConfigFileActionError); expect(mockCreateComponent).not.toBeCalled(); }); it('should update path alias, because component have another path externalAlias', async () => { const componentPathToFileExternalAlias = { externalAliasId: `${MOCK_DEDUPLICATION_ID}:${MOCK_COMPONENT_OLD_PATH}`, externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_PATH_TO_FILE, }; const component = getMockedComponent({ externalAliases: getManagedComponentAliases({ path: MOCK_COMPONENT_OLD_PATH, }), }); mockGetComponentByExternalAlias.mockResolvedValue({ success: true, data: { component }, errors: [], }); mockCreateExternalAlias.mockResolvedValue({ success: true, errors: [], data: { componentPathToFileExternalAlias }, }); await createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, deduplicationId: MOCK_DEDUPLICATION_ID, oldPath: MOCK_COMPONENT_OLD_PATH, newPath: MOCK_COMPONENT_PATH, componentYaml: getMockedCompassYaml({ id: null }), configFileAction: ConfigFileActions.UPDATE, }); expect(mockDeleteExternalAlias).toBeCalledWith({ componentId: component.id, externalAlias: { externalId: componentPathToFileExternalAlias.externalAliasId, externalSource: componentPathToFileExternalAlias.externalSource, }, }); expect(mockCreateExternalAlias).toBeCalledWith({ componentId: component.id, externalAlias: { externalId: generateExternalIdWithPrefix( MOCK_DEDUPLICATION_ID, MOCK_COMPONENT_PATH, ), externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_PATH_TO_FILE, }, }); expect(mockUpdateComponent).toBeCalledWith( expect.objectContaining(expectedDefaultUpdateParams), ); }); it('should throw an error on UPDATE action in case, when component doesn`t exist by name and path alias', async () => { mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: false, data: { component: null }, errors: [], }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: false, errors: [{ message: 'Error' }], data: { component: null }, }); await expect( createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, componentYaml: getMockedCompassYaml({ id: null }), deduplicationId: MOCK_DEDUPLICATION_ID, configFileAction: ConfigFileActions.UPDATE, newPath: MOCK_NEW_PATH, oldPath: MOCK_COMPONENT_OLD_PATH, }), ).rejects.toThrow(UnableToFindComponentDuringUpdateError); }); it('should update path alias in UPDATE action in case when component didn`t find by name alias', async () => { const component = getMockedComponent({ externalAliases: getManagedComponentAliases({ path: MOCK_COMPONENT_OLD_PATH, }), }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: false, data: { component: null }, errors: [], }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: true, errors: [], data: { component }, }); await createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, deduplicationId: MOCK_DEDUPLICATION_ID, oldPath: MOCK_COMPONENT_OLD_PATH, newPath: MOCK_NEW_PATH, componentYaml: getMockedCompassYaml({ id: null }), configFileAction: ConfigFileActions.UPDATE, }); expect(mockDeleteExternalAlias).toBeCalledWith({ componentId: component.id, externalAlias: { externalId: component.externalAliases[1].externalAliasId, externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_PATH_TO_FILE, }, }); expect(mockCreateExternalAlias).toBeCalledWith({ componentId: component.id, externalAlias: { externalId: generateExternalIdWithPrefix( MOCK_DEDUPLICATION_ID, MOCK_NEW_PATH, ), externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_PATH_TO_FILE, }, }); }); it('should update name alias on UPDATE action in case, when name alias in component is different with name in input', async () => { const newName = 'another name'; const component = getMockedComponent({ externalAliases: getManagedComponentAliases({ path: MOCK_COMPONENT_OLD_PATH, }), }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: false, data: { component: null }, errors: [], }); mockGetComponentByExternalAlias.mockResolvedValue({ success: true, errors: [], data: { component }, }); await createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, deduplicationId: MOCK_DEDUPLICATION_ID, oldPath: MOCK_COMPONENT_OLD_PATH, newPath: MOCK_COMPONENT_OLD_PATH, componentYaml: getMockedCompassYaml({ id: null, name: newName }), configFileAction: ConfigFileActions.UPDATE, }); expect(mockDeleteExternalAlias).toBeCalledWith({ componentId: component.id, externalAlias: { externalId: component.externalAliases[0].externalAliasId, externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_NAME, }, }); expect(mockCreateExternalAlias).toBeCalledWith({ componentId: component.id, externalAlias: { externalId: generateExternalIdWithPrefix( MOCK_DEDUPLICATION_ID, newName, ), externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_NAME, }, }); }); it('should not update name alias on UPDATE action in case, when name alias in component is the same with name in input', async () => { const component = getMockedComponent({ externalAliases: getManagedComponentAliases({ path: MOCK_COMPONENT_OLD_PATH, }), }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: false, data: { component: null }, errors: [], }); mockGetComponentByExternalAlias.mockResolvedValue({ success: true, errors: [], data: { component }, }); await createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, deduplicationId: MOCK_DEDUPLICATION_ID, oldPath: MOCK_COMPONENT_OLD_PATH, newPath: MOCK_COMPONENT_OLD_PATH, componentYaml: getMockedCompassYaml({ id: null }), configFileAction: ConfigFileActions.UPDATE, }); expect(mockDeleteExternalAlias).not.toBeCalledWith({ componentId: component.id, externalAlias: { externalId: component.externalAliases[0].externalAliasId, externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_NAME, }, }); expect(mockCreateExternalAlias).not.toBeCalledWith({ componentId: component.id, externalAlias: { externalId: generateExternalIdWithPrefix( MOCK_DEDUPLICATION_ID, MOCK_COMPONENT_NAME, ), externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_NAME, }, }); }); it('should create name alias on UPDATE action in case, when component doesn`t have name alias', async () => { const component = getMockedComponent({ externalAliases: [ getManagedComponentAliases({ path: MOCK_COMPONENT_OLD_PATH, })[1], ], }); mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: false, data: { component: null }, errors: [], }); mockGetComponentByExternalAlias.mockResolvedValue({ success: true, errors: [], data: { component }, }); await createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, deduplicationId: MOCK_DEDUPLICATION_ID, oldPath: MOCK_COMPONENT_OLD_PATH, newPath: MOCK_COMPONENT_OLD_PATH, componentYaml: getMockedCompassYaml({ id: null }), configFileAction: ConfigFileActions.UPDATE, }); expect(mockDeleteExternalAlias).not.toBeCalledWith({ componentId: component.id, externalAlias: { externalId: component.externalAliases[0].externalAliasId, externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_NAME, }, }); expect(mockCreateExternalAlias).toBeCalledWith({ componentId: component.id, externalAlias: { externalId: generateExternalIdWithPrefix( MOCK_DEDUPLICATION_ID, MOCK_COMPONENT_NAME, ), externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_NAME, }, }); }); it('should update component on UPDATE action in case, when id added to the file points to the component that the file was meant for', async () => { const component = getMockedComponent(); mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: true, errors: [], data: { component }, }); mockGetComponent.mockResolvedValue({ success: true, errors: [], data: { component }, }); await createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, deduplicationId: MOCK_DEDUPLICATION_ID, oldPath: MOCK_COMPONENT_OLD_PATH, newPath: MOCK_COMPONENT_PATH, componentYaml: getMockedCompassYaml(), configFileAction: ConfigFileActions.UPDATE, }); expect(mockUnlinkComponent).not.toBeCalled(); expect(mockUpdateComponent).toBeCalledWith( expect.objectContaining(expectedDefaultUpdateParams), ); }); it('should update component on UPDATE action in case, when id added to the file points to the component that the file was meant for and old_path is capitalized', async () => { const component = getMockedComponent(); mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: true, errors: [], data: { component }, }); mockGetComponent.mockResolvedValue({ success: true, errors: [], data: { component }, }); await createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, deduplicationId: MOCK_DEDUPLICATION_ID, oldPath: MOCK_COMPONENT_CAPITALIZED_OLD_PATH, newPath: MOCK_COMPONENT_PATH, componentYaml: getMockedCompassYaml(), configFileAction: ConfigFileActions.UPDATE, }); expect(mockGetComponentByExternalAlias).toBeCalledWith({ cloudId: MOCK_CLOUD_ID, externalSource: EXTERNAL_ALIAS_SOURCE_COMPONENT_PATH_TO_FILE, externalId: `${MOCK_DEDUPLICATION_ID}:${MOCK_COMPONENT_OLD_PATH}`, }); expect(mockUnlinkComponent).not.toBeCalled(); expect(mockUpdateComponent).toBeCalledWith( expect.objectContaining(expectedDefaultUpdateParams), ); }); it('should update component and unlink previous one on UPDATE action in case, when id added to the file points to a different component', async () => { const componentByOldPath = getMockedComponent({ id: MOCK_COMPONENT_ID_2, }); const component = getMockedComponent(); mockGetComponentByExternalAlias.mockResolvedValueOnce({ success: true, errors: [], data: { component: componentByOldPath }, }); mockGetComponent.mockResolvedValue({ success: true, errors: [], data: { component }, }); await createOrUpdateComponent({ requestApi: compassApp, configAsCodeApi: compassConfigAsCodeApp, cloudId: MOCK_CLOUD_ID, deduplicationId: MOCK_DEDUPLICATION_ID, oldPath: MOCK_COMPONENT_OLD_PATH, newPath: MOCK_COMPONENT_PATH, componentYaml: getMockedCompassYaml(), configFileAction: ConfigFileActions.UPDATE, }); const expectedUnlinkInput = { cloudId: MOCK_CLOUD_ID, filePath: MOCK_COMPONENT_OLD_PATH, componentId: MOCK_COMPONENT_ID_2, deduplicationId: MOCK_DEDUPLICATION_ID, }; expect(mockUnlinkComponent).toBeCalledWith(expectedUnlinkInput); expect(mockUpdateComponent).toBeCalledWith( expect.objectContaining(expectedDefaultUpdateParams), ); }); });