UNPKG

box-ui-elements

Version:
904 lines (819 loc) • 37.3 kB
import includes from 'lodash/includes'; import isArray from 'lodash/isArray'; import MetadataQueryAPIHelper from '../MetadataQueryAPIHelper'; import { JSON_PATCH_OP_ADD, JSON_PATCH_OP_REMOVE, JSON_PATCH_OP_REPLACE, JSON_PATCH_OP_TEST, } from '../../../common/constants'; import { FIELD_METADATA, FIELD_ITEM_NAME, FIELD_EXTENSION, FIELD_PERMISSIONS } from '../../../constants'; describe('elements/content-explorer/MetadataQueryAPIHelper', () => { let metadataQueryAPIHelper; const templateScope = 'enterprise_12345'; const templateKey = 'awesomeTemplate'; const metadataInstance = { instance: 'instance' }; const metadataInstanceId1 = 'metadataInstanceId1'; const metadataInstanceId2 = 'metadataInstanceId2'; const options = [ { id: '3887b73d-0087-43bb-947e-0dff1543bdfb', key: 'yes', }, { id: '3393eed2-f254-4fd0-a7ff-cd9a5f75e222', key: 'no', }, ]; const template = { id: 'cdb8c36d-4470-41df-90ba', type: 'metadata_template', templateKey, scope: templateScope, displayName: 'Test Template', hidden: false, fields: [ { id: '854045ee-a219-47ef-93ec-6e3b3417b68f', type: 'string', key: 'type', displayName: 'type', hidden: false, description: 'type', }, { id: '04af7602-7cad-4d60-b843-acc14b0ef587', type: 'float', key: 'year', displayName: 'year', hidden: false, description: 'year', }, { id: '9e5849a1-02f4-4a9a-b626-91fe46a89f2a', type: 'enum', key: 'approved', displayName: 'approved', hidden: false, description: 'approved yes/no', options, }, ], }; const templateSchemaResponse = { data: template, }; const nextMarker = 'marker1234567890'; const metadataQueryResponse = { entries: [ { type: 'file', id: '1234', name: 'filename1.pdf', size: 10000, metadata: { [templateScope]: { [templateKey]: { $id: metadataInstanceId1, $parent: 'file_998877', $type: 'awesomeTemplateKey-asdlk-1234-asd1', $typeScope: 'enterprise_2222', $typeVersion: 0, $version: 0, type: 'bill', // metadata template field year: 2017, // metadata template field approved: 'yes', // metadata template field }, }, }, }, { type: 'file', id: '9876', name: 'filename2.mp4', size: 50000, metadata: { [templateScope]: { [templateKey]: { $id: metadataInstanceId2, $parent: 'file_998877', $type: 'awesomeTemplateKey-asdlk-1234-asd1', $typeScope: 'enterprise_2222', $typeVersion: 0, $version: 0, type: 'receipt', // metadata template field year: 2018, // metadata template field approved: 'no', // metadata template field }, }, }, }, ], next_marker: nextMarker, }; const flattenedMetadataEntries = [ { enterprise: { fields: [ { displayName: 'type', key: `${FIELD_METADATA}.${templateScope}.${templateKey}.type`, value: 'bill', type: 'string', }, { displayName: 'year', key: `${FIELD_METADATA}.${templateScope}.${templateKey}.year`, value: 2017, type: 'float', }, { displayName: 'approved', key: `${FIELD_METADATA}.${templateScope}.${templateKey}.approved`, value: 'yes', type: 'enum', options, }, ], id: metadataInstanceId1, }, }, { enterprise: { fields: [ { displayName: 'type', key: `${FIELD_METADATA}.${templateScope}.${templateKey}.type`, value: 'receipt', type: 'string', }, { displayName: 'year', key: `${FIELD_METADATA}.${templateScope}.${templateKey}.year`, value: 2018, type: 'float', }, { displayName: 'approved', key: `${FIELD_METADATA}.${templateScope}.${templateKey}.approved`, value: 'no', type: 'enum', options, }, ], id: metadataInstanceId2, }, }, ]; const dataWithTypes = { items: metadataQueryResponse.entries, nextMarker: metadataQueryResponse.next_marker, }; const getSchemaByTemplateKeyFunc = jest.fn().mockReturnValueOnce(Promise.resolve(templateSchemaResponse)); const queryMetadataFunc = jest.fn().mockReturnValueOnce(Promise.resolve(metadataQueryResponse)); const updateMetadataFunc = jest.fn().mockReturnValueOnce(Promise.resolve(metadataInstance)); const api = { getMetadataAPI: () => { return { getSchemaByTemplateKey: getSchemaByTemplateKeyFunc, updateMetadata: updateMetadataFunc, }; }, getMetadataQueryAPI: () => { return { queryMetadata: queryMetadataFunc, }; }, }; const mdQuery = { ancestor_folder_id: '672838458', from: `${templateScope}.${templateKey}`, query: 'query', query_params: {}, fields: [ FIELD_ITEM_NAME, `metadata.${templateScope}.${templateKey}.type`, `metadata.${templateScope}.${templateKey}.year`, `metadata.${templateScope}.${templateKey}.approved`, ], }; beforeEach(() => { metadataQueryAPIHelper = new MetadataQueryAPIHelper(api); metadataQueryAPIHelper.templateKey = templateKey; metadataQueryAPIHelper.templateScope = templateScope; metadataQueryAPIHelper.metadataTemplate = template; metadataQueryAPIHelper.metadataQuery = mdQuery; // Reset mocks before each test getSchemaByTemplateKeyFunc.mockClear(); getSchemaByTemplateKeyFunc.mockResolvedValue(templateSchemaResponse); }); describe('flattenMetadata()', () => { const { entries } = metadataQueryResponse; test.each` entryIndex | metadataResponseEntry | flattenedMetadataEntry ${0} | ${entries[0].metadata} | ${flattenedMetadataEntries[0]} ${1} | ${entries[1].metadata} | ${flattenedMetadataEntries[1]} `( 'should return correct flattened metadata for entry $entryIndex', ({ metadataResponseEntry, flattenedMetadataEntry }) => { const result = metadataQueryAPIHelper.flattenMetadata(metadataResponseEntry); expect(result).toEqual(flattenedMetadataEntry); }, ); test('should return empty object when instance is not found', () => { expect(metadataQueryAPIHelper.flattenMetadata(undefined)).toEqual({}); }); test('should return fields even when template fields are empty', () => { metadataQueryAPIHelper.metadataTemplate = { ...template, fields: [] }; const result = metadataQueryAPIHelper.flattenMetadata(entries[0].metadata); expect(result.enterprise.fields).toHaveLength(3); expect(result.enterprise.fields[0].type).toBeUndefined(); }); test('should handle missing template field gracefully', () => { const metadataWithMissingField = { [templateScope]: { [templateKey]: { $id: metadataInstanceId1, type: 'bill', // year field is missing approved: 'yes', }, }, }; const result = metadataQueryAPIHelper.flattenMetadata(metadataWithMissingField); expect(result.enterprise.fields).toHaveLength(3); expect(result.enterprise.fields.find(f => f.key.includes('year'))?.value).toBeUndefined(); }); }); describe('getDataWithTypes()', () => { test('should return data with types and set template object on the instance', () => { metadataQueryAPIHelper.metadataQueryResponseData = metadataQueryResponse; const result = metadataQueryAPIHelper.getDataWithTypes(templateSchemaResponse); expect(result).toEqual(dataWithTypes); expect(metadataQueryAPIHelper.metadataTemplate).toEqual(template); }); test('should handle undefined template schema response', () => { metadataQueryAPIHelper.metadataQueryResponseData = metadataQueryResponse; const result = metadataQueryAPIHelper.getDataWithTypes(undefined); expect(result).toEqual(dataWithTypes); expect(metadataQueryAPIHelper.metadataTemplate).toBeUndefined(); }); }); describe('getTemplateSchemaInfo()', () => { test('should set instance properties and make xhr call to get template info', async () => { const result = await metadataQueryAPIHelper.getTemplateSchemaInfo(metadataQueryResponse); expect(getSchemaByTemplateKeyFunc).toHaveBeenCalledWith(templateKey); expect(result).toEqual(templateSchemaResponse); expect(metadataQueryAPIHelper.metadataQueryResponseData).toEqual(metadataQueryResponse); expect(metadataQueryAPIHelper.templateScope).toEqual(templateScope); expect(metadataQueryAPIHelper.templateKey).toEqual(templateKey); }); test('should make xhr call to get metadata template info even when response has zero entries', async () => { const emptyEntriesResponse = { entries: [], next_marker: nextMarker }; const result = await metadataQueryAPIHelper.getTemplateSchemaInfo(emptyEntriesResponse); expect(getSchemaByTemplateKeyFunc).toHaveBeenCalledWith(templateKey); expect(result).toEqual(templateSchemaResponse); expect(metadataQueryAPIHelper.metadataQueryResponseData).toEqual(emptyEntriesResponse); expect(metadataQueryAPIHelper.templateScope).toEqual(templateScope); expect(metadataQueryAPIHelper.templateKey).toEqual(templateKey); }); test('should make xhr call to get metadata template info even when response has null entries', async () => { const nullEntriesResponse = { entries: null, next_marker: nextMarker }; const result = await metadataQueryAPIHelper.getTemplateSchemaInfo(nullEntriesResponse); expect(getSchemaByTemplateKeyFunc).toHaveBeenCalledWith(templateKey); expect(result).toEqual(templateSchemaResponse); expect(metadataQueryAPIHelper.metadataQueryResponseData).toEqual(nullEntriesResponse); expect(metadataQueryAPIHelper.templateScope).toEqual(templateScope); expect(metadataQueryAPIHelper.templateKey).toEqual(templateKey); }); test('should make xhr call to get metadata template info even when response has undefined entries', async () => { const undefinedEntriesResponse = { next_marker: nextMarker }; const result = await metadataQueryAPIHelper.getTemplateSchemaInfo(undefinedEntriesResponse); expect(getSchemaByTemplateKeyFunc).toHaveBeenCalledWith(templateKey); expect(result).toEqual(templateSchemaResponse); expect(metadataQueryAPIHelper.metadataQueryResponseData).toEqual(undefinedEntriesResponse); expect(metadataQueryAPIHelper.templateScope).toEqual(templateScope); expect(metadataQueryAPIHelper.templateKey).toEqual(templateKey); }); test('should extract template scope and key from metadata query from field', async () => { // Test with different scope and key in the query const differentScope = 'enterprise_99999'; const differentKey = 'differentTemplate'; metadataQueryAPIHelper.metadataQuery = { ...mdQuery, from: `${differentScope}.${differentKey}`, }; await metadataQueryAPIHelper.getTemplateSchemaInfo(metadataQueryResponse); expect(getSchemaByTemplateKeyFunc).toHaveBeenCalledWith(differentKey); expect(metadataQueryAPIHelper.templateScope).toEqual(differentScope); expect(metadataQueryAPIHelper.templateKey).toEqual(differentKey); }); }); describe('queryMetadata()', () => { test('should return a promise that resolves with metadata query result', async () => { const result = metadataQueryAPIHelper.queryMetadata(); expect(result).toBeInstanceOf(Promise); expect(queryMetadataFunc).toBeCalledWith( mdQuery, expect.any(Function), // resolve expect.any(Function), // reject { forceFetch: true }, ); }); test('should handle API errors properly', async () => { const error = new Error('API Error'); queryMetadataFunc.mockImplementationOnce((query, resolve, reject) => { reject(error); }); await expect(metadataQueryAPIHelper.queryMetadata()).rejects.toThrow('API Error'); }); }); describe('fetchMetadataQueryResults()', () => { test('should fetch metadata query results, template info, and call successCallback with data with data types', async () => { const successCallback = jest.fn(); const errorCallback = jest.fn(); metadataQueryAPIHelper.queryMetadata = jest .fn() .mockReturnValueOnce(Promise.resolve(metadataQueryResponse)); metadataQueryAPIHelper.getTemplateSchemaInfo = jest.fn().mockReturnValueOnce(Promise.resolve(template)); metadataQueryAPIHelper.getDataWithTypes = jest.fn().mockReturnValueOnce(dataWithTypes); await metadataQueryAPIHelper.fetchMetadataQueryResults(mdQuery, successCallback, errorCallback); expect(metadataQueryAPIHelper.queryMetadata).toBeCalled(); expect(metadataQueryAPIHelper.getTemplateSchemaInfo).toBeCalledWith(metadataQueryResponse); expect(metadataQueryAPIHelper.getDataWithTypes).toBeCalledWith(template); expect(successCallback).toBeCalledWith(dataWithTypes, template); expect(errorCallback).not.toHaveBeenCalled(); }); test('should call error callback when the promise chain throws exception during API data fetch', async () => { const err = new Error(); const successCallback = jest.fn(); const errorCallback = jest.fn(); metadataQueryAPIHelper.queryMetadata = jest .fn() .mockReturnValueOnce(Promise.resolve(metadataQueryResponse)); metadataQueryAPIHelper.getTemplateSchemaInfo = jest.fn().mockReturnValueOnce(Promise.reject(err)); metadataQueryAPIHelper.getDataWithTypes = jest.fn().mockReturnValueOnce(dataWithTypes); await metadataQueryAPIHelper.fetchMetadataQueryResults(mdQuery, successCallback, errorCallback); expect(metadataQueryAPIHelper.queryMetadata).toBeCalled(); expect(metadataQueryAPIHelper.getTemplateSchemaInfo).toBeCalledWith(metadataQueryResponse); expect(metadataQueryAPIHelper.getDataWithTypes).not.toHaveBeenCalled(); expect(successCallback).not.toHaveBeenCalled(); expect(errorCallback).toBeCalledWith(err); }); test('should handle query metadata errors', async () => { const err = new Error('Query failed'); const successCallback = jest.fn(); const errorCallback = jest.fn(); metadataQueryAPIHelper.queryMetadata = jest.fn().mockReturnValueOnce(Promise.reject(err)); await metadataQueryAPIHelper.fetchMetadataQueryResults(mdQuery, successCallback, errorCallback); expect(errorCallback).toBeCalledWith(err); expect(successCallback).not.toHaveBeenCalled(); }); }); describe('createJSONPatchOperations()', () => { const field = 'amount'; const testOp = { op: JSON_PATCH_OP_TEST, path: `/${field}`, value: 100, }; const addOp = { op: JSON_PATCH_OP_ADD, path: `/${field}`, value: 200, }; const replaceOp = { op: JSON_PATCH_OP_REPLACE, path: `/${field}`, value: 200, }; const removeOp = { op: JSON_PATCH_OP_REMOVE, path: `/${field}`, }; test.each` oldValue | newValue | ops ${undefined} | ${200} | ${[addOp]} ${null} | ${200} | ${[addOp]} ${100} | ${200} | ${[testOp, replaceOp]} ${100} | ${undefined} | ${[testOp, removeOp]} ${100} | ${null} | ${[testOp, removeOp]} `('should return valid JSON patch object', ({ oldValue, newValue, ops }) => { expect(metadataQueryAPIHelper.createJSONPatchOperations(field, oldValue, newValue)).toEqual(ops); }); }); describe('getMetadataQueryFields()', () => { test('should get metadata instance fields array from the query', () => { const expectedResponse = ['type', 'year', 'approved']; expect(metadataQueryAPIHelper.getMetadataQueryFields()).toEqual(expectedResponse); }); test('should handle query with no metadata fields', () => { metadataQueryAPIHelper.metadataQuery = { ...mdQuery, fields: [FIELD_ITEM_NAME, 'created_at'], }; expect(metadataQueryAPIHelper.getMetadataQueryFields()).toEqual([]); }); test('should handle query with empty fields array', () => { metadataQueryAPIHelper.metadataQuery = { ...mdQuery, fields: [], }; expect(metadataQueryAPIHelper.getMetadataQueryFields()).toEqual([]); }); test('should handle query with undefined fields', () => { metadataQueryAPIHelper.metadataQuery = { ...mdQuery, fields: undefined, }; expect(metadataQueryAPIHelper.getMetadataQueryFields()).toEqual([]); }); }); describe('updateMetadata()', () => { test('should update the metadata by calling Metadata api function', async () => { const file = 'file'; const field = 'amount'; const oldValue = 100; const newValue = 200; const successCallback = jest.fn(); const errorCallback = jest.fn(); const JSONPatchOps = { jsonPatch: 'jsonPatch' }; metadataQueryAPIHelper.createJSONPatchOperations = jest.fn().mockReturnValueOnce(JSONPatchOps); await metadataQueryAPIHelper.updateMetadata( file, field, oldValue, newValue, successCallback, errorCallback, ); expect(metadataQueryAPIHelper.createJSONPatchOperations).toHaveBeenCalledWith(field, oldValue, newValue); expect(metadataQueryAPIHelper.api.getMetadataAPI().updateMetadata).toHaveBeenCalledWith( file, template, JSONPatchOps, successCallback, errorCallback, ); }); }); describe('verifyQueryFields()', () => { const mdQueryWithEmptyFields = { ancestor_folder_id: '672838458', from: 'enterprise_1234.templateKey', query: 'query', query_params: {}, }; const mdQueryWithoutNameField = { ancestor_folder_id: '672838458', from: 'enterprise_1234.templateKey', query: 'query', query_params: {}, fields: ['created_at', 'metadata.enterprise_1234.templateKey.type'], }; const mdQueryWithNameField = { ancestor_folder_id: '672838458', from: 'enterprise_1234.templateKey', query: 'query', query_params: {}, fields: [FIELD_ITEM_NAME, 'metadata.enterprise_1234.templateKey.type'], }; const mdQueryWithoutExtensionField = { ancestor_folder_id: '672838458', from: 'enterprise_1234.templateKey', query: 'query', query_params: {}, fields: [FIELD_ITEM_NAME, 'metadata.enterprise_1234.templateKey.type'], }; const mdQueryWithBothFields = { ancestor_folder_id: '672838458', from: 'enterprise_1234.templateKey', query: 'query', query_params: {}, fields: [FIELD_ITEM_NAME, FIELD_EXTENSION, 'metadata.enterprise_1234.templateKey.type'], }; test.each` index | metadataQuery ${1} | ${mdQueryWithEmptyFields} ${2} | ${mdQueryWithoutNameField} ${3} | ${mdQueryWithNameField} ${4} | ${mdQueryWithoutExtensionField} ${5} | ${mdQueryWithBothFields} `( 'should verify the metadata query object and add required fields if necessary', ({ index, metadataQuery }) => { const updatedMetadataQuery = metadataQueryAPIHelper.verifyQueryFields(metadataQuery); expect(isArray(updatedMetadataQuery.fields)).toBe(true); expect(includes(updatedMetadataQuery.fields, FIELD_ITEM_NAME)).toBe(true); expect(includes(updatedMetadataQuery.fields, FIELD_EXTENSION)).toBe(true); expect(includes(updatedMetadataQuery.fields, FIELD_PERMISSIONS)).toBe(true); if (index === 2) { // Verify "name", "extension" and "permission" are added to pre-existing fields expect(updatedMetadataQuery.fields).toEqual([ ...mdQueryWithoutNameField.fields, FIELD_ITEM_NAME, FIELD_EXTENSION, FIELD_PERMISSIONS, ]); } if (index === 4) { // Verify "extension" and "permission" are added when "name" exists but "extension" and "permission" don't expect(updatedMetadataQuery.fields).toEqual([ ...mdQueryWithoutExtensionField.fields, FIELD_EXTENSION, FIELD_PERMISSIONS, ]); } if (index === 5) { // Verify "permission" is added expect(updatedMetadataQuery.fields).toEqual([...mdQueryWithBothFields.fields, FIELD_PERMISSIONS]); } }, ); test('should handle query with non-array fields', () => { const mdQueryWithNonArrayFields = { ancestor_folder_id: '672838458', from: 'enterprise_1234.templateKey', query: 'query', query_params: {}, fields: 'not-an-array', }; const updatedMetadataQuery = metadataQueryAPIHelper.verifyQueryFields(mdQueryWithNonArrayFields); expect(isArray(updatedMetadataQuery.fields)).toBe(true); expect(includes(updatedMetadataQuery.fields, FIELD_ITEM_NAME)).toBe(true); expect(includes(updatedMetadataQuery.fields, FIELD_EXTENSION)).toBe(true); }); }); describe('buildMDQueryParams()', () => { test('should return empty result when no filters provided', () => { const result = metadataQueryAPIHelper.buildMetadataQueryParams({}); expect(result).toEqual({ queryParams: {}, query: '', }); }); test('should return empty result when filters is null', () => { const result = metadataQueryAPIHelper.buildMetadataQueryParams(null); expect(result).toEqual({ queryParams: {}, query: '', }); }); test('should handle date/float field with greater than filter', () => { const filters = { year: { fieldType: 'float', value: { range: { gt: 2020 } }, }, }; const result = metadataQueryAPIHelper.buildMetadataQueryParams(filters); expect(result.query).toBe('(year >= :arg_year_1)'); expect(result.queryParams.arg_year_1).toBe(2020); }); test('should handle date/float field with less than filter', () => { const filters = { year: { fieldType: 'date', value: { range: { lt: 2023 } }, }, }; const result = metadataQueryAPIHelper.buildMetadataQueryParams(filters); expect(result.query).toBe('(year <= :arg_year_1)'); expect(result.queryParams.arg_year_1).toBe(2023); }); test('should handle date/float field with range array filter', () => { const filters = { year: { fieldType: 'float', value: { range: { gt: 2020, lt: 2023 } }, }, }; const result = metadataQueryAPIHelper.buildMetadataQueryParams(filters); expect(result.query).toBe('(year >= :arg_year_1 AND year <= :arg_year_2)'); expect(result.queryParams.arg_year_1).toBe(2020); expect(result.queryParams.arg_year_2).toBe(2023); }); test('should handle enum field with single value', () => { const filters = { status: { fieldType: 'enum', value: 'active', }, }; const result = metadataQueryAPIHelper.buildMetadataQueryParams(filters); expect(result.query).toBe('(status HASANY (:arg_status_1))'); expect(result.queryParams.arg_status_1).toBe('active'); }); test('should handle enum field with multiple values', () => { const filters = { status: { fieldType: 'enum', value: ['active', 'pending'], }, }; const result = metadataQueryAPIHelper.buildMetadataQueryParams(filters); expect(result.query).toBe('(status HASANY (:arg_status_1, :arg_status_2))'); expect(result.queryParams.arg_status_1).toBe('active'); expect(result.queryParams.arg_status_2).toBe('pending'); }); test('should handle multiSelect field', () => { const filters = { tags: { fieldType: 'multiSelect', value: ['tag1', 'tag2'], }, }; const result = metadataQueryAPIHelper.buildMetadataQueryParams(filters); expect(result.query).toBe('(tags HASANY (:arg_tags_1, :arg_tags_2))'); expect(result.queryParams.arg_tags_1).toBe('tag1'); expect(result.queryParams.arg_tags_2).toBe('tag2'); }); test('should handle string field with search value', () => { const filters = { name: { fieldType: 'string', value: ['search term'], }, }; const result = metadataQueryAPIHelper.buildMetadataQueryParams(filters); expect(result.query).toBe('(name ILIKE :arg_name_1)'); expect(result.queryParams.arg_name_1).toBe('%search term%'); }); test('should handle mimetype filter specifically', () => { const filters = { 'mimetype-filter': { fieldType: 'enum', value: ['pdfType', 'documentType'], }, }; const result = metadataQueryAPIHelper.buildMetadataQueryParams(filters); expect(result.query).toBe( '(item.extension IN (:arg_mimetype_filter_1, :arg_mimetype_filter_2, :arg_mimetype_filter_3, :arg_mimetype_filter_4, :arg_mimetype_filter_5, :arg_mimetype_filter_6))', ); expect(result.queryParams.arg_mimetype_filter_1).toBe('pdf'); expect(result.queryParams.arg_mimetype_filter_2).toBe('doc'); expect(result.queryParams.arg_mimetype_filter_3).toBe('docx'); expect(result.queryParams.arg_mimetype_filter_4).toBe('gdoc'); expect(result.queryParams.arg_mimetype_filter_5).toBe('rtf'); expect(result.queryParams.arg_mimetype_filter_6).toBe('txt'); }); test('should handle multiple filters of different types', () => { const filters = { year: { fieldType: 'float', value: { range: { gt: 2020 } }, }, status: { fieldType: 'enum', value: ['active'], }, name: { fieldType: 'string', value: ['search'], }, }; const result = metadataQueryAPIHelper.buildMetadataQueryParams(filters); expect(result.query).toBe( '(year >= :arg_year_1) AND (status HASANY (:arg_status_2)) AND (name ILIKE :arg_name_3)', ); expect(Object.keys(result.queryParams)).toHaveLength(3); }); test('should handle filter with null/undefined value', () => { const filters = { field: { fieldType: 'string', value: null, }, }; const result = metadataQueryAPIHelper.buildMetadataQueryParams(filters); expect(result.query).toBe(''); expect(Object.keys(result.queryParams)).toHaveLength(0); }); test('should handle filter with empty string value', () => { const filters = { field: { fieldType: 'string', value: '', }, }; const result = metadataQueryAPIHelper.buildMetadataQueryParams(filters); expect(result.query).toBe(''); expect(Object.keys(result.queryParams)).toHaveLength(0); }); test('should handle unknown field type with array value', () => { const filters = { field: { fieldType: 'unknown', value: ['value1', 'value2'], }, }; const result = metadataQueryAPIHelper.buildMetadataQueryParams(filters); expect(result.query).toBeFalsy(); expect(result.queryParams.arg_field_1).toBeUndefined(); expect(result.queryParams.arg_field_2).toBeUndefined(); }); test('should handle empty array values for enum/multiSelect', () => { const filters = { status: { fieldType: 'enum', value: [], }, }; const result = metadataQueryAPIHelper.buildMetadataQueryParams(filters); expect(result.query).toBe(''); expect(Object.keys(result.queryParams)).toHaveLength(0); }); test('should handle empty string array for string field', () => { const filters = { name: { fieldType: 'string', value: [''], }, }; const result = metadataQueryAPIHelper.buildMetadataQueryParams(filters); expect(result.query).toBe(''); expect(Object.keys(result.queryParams)).toHaveLength(0); }); }); describe('verifyQueryFields with filters', () => { test('should build query and query_params when filters are provided', () => { const metadataQuery = { ancestor_folder_id: '672838458', from: 'enterprise_1234.templateKey', fields: [FIELD_ITEM_NAME], }; const filters = { status: { fieldType: 'enum', value: ['active'], }, }; const result = metadataQueryAPIHelper.verifyQueryFields(metadataQuery, filters); expect(result.query).toBe('(status HASANY (:arg_status_1))'); expect(result.query_params).toEqual({ arg_status_1: 'active', }); expect(result.fields).toContain(FIELD_ITEM_NAME); expect(result.fields).toContain(FIELD_EXTENSION); }); test('should handle multiple filters with AND logic', () => { const metadataQuery = { ancestor_folder_id: '672838458', from: 'enterprise_1234.templateKey', fields: [FIELD_ITEM_NAME], }; const filters = { status: { fieldType: 'enum', value: ['active'], }, year: { fieldType: 'float', value: { range: { gt: 2020 } }, }, }; const result = metadataQueryAPIHelper.verifyQueryFields(metadataQuery, filters); expect(result.query).toContain('AND'); expect(result.query).toContain('HASANY'); expect(result.query).toContain('>='); expect(Object.keys(result.query_params)).toHaveLength(2); }); test('should merge existing query_params with filter query params', () => { const metadataQuery = { ancestor_folder_id: '672838458', from: 'enterprise_1234.templateKey', fields: [FIELD_ITEM_NAME], query: '(existing_field = :existing_param)', query_params: { existing_param: 'existing_value', }, }; const filters = { status: { fieldType: 'enum', value: ['active'], }, }; const result = metadataQueryAPIHelper.verifyQueryFields(metadataQuery, filters); expect(result.query).toBe('(existing_field = :existing_param) AND (status HASANY (:arg_status_1))'); expect(result.query_params).toEqual({ existing_param: 'existing_value', arg_status_1: 'active', }); expect(result.fields).toContain(FIELD_ITEM_NAME); expect(result.fields).toContain(FIELD_EXTENSION); }); test('should not modify query when no filters provided', () => { const metadataQuery = { ancestor_folder_id: '672838458', from: 'enterprise_1234.templateKey', fields: [FIELD_ITEM_NAME], }; const result = metadataQueryAPIHelper.verifyQueryFields(metadataQuery); expect(result.query).toBeUndefined(); expect(result.query_params).toBeUndefined(); expect(result.fields).toContain(FIELD_ITEM_NAME); expect(result.fields).toContain(FIELD_EXTENSION); }); }); });