/** * * Copyright 2023-present InspectorRAGet Team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **/ import { describe, it, expect } from 'vitest'; import { camelCaseKeys, snakeCaseKeys, areObjectsIntersecting, } from '@/src/utilities/objects'; // --- camelCaseKeys --- describe('camelCaseKeys', () => { it('converts default snake_case keys to camelCase', () => { const input = { task_id: '1', model_id: 'm1', other_key: 'keep' }; const result = camelCaseKeys(input); expect(result).toEqual({ taskId: '1', modelId: 'm1', other_key: 'keep' }); }); it('leaves non-listed keys untouched', () => { const input = { foo_bar: 'value', task_id: 'x' }; const result = camelCaseKeys(input); expect(result).toHaveProperty('foo_bar', 'value'); expect(result).toHaveProperty('taskId', 'x'); }); it('handles nested objects recursively', () => { const input = { task_id: '1', nested: { model_id: 'm1' } }; const result = camelCaseKeys(input); expect(result).toEqual({ taskId: '1', nested: { modelId: 'm1' } }); }); it('handles arrays of objects', () => { const input = [{ task_id: '1' }, { task_id: '2' }]; const result = camelCaseKeys(input as any); expect(result).toEqual([{ taskId: '1' }, { taskId: '2' }]); }); it('returns primitives unchanged', () => { expect(camelCaseKeys('hello' as any)).toBe('hello'); expect(camelCaseKeys(42 as any)).toBe(42); expect(camelCaseKeys(null as any)).toBeNull(); }); it('uses custom keys list when provided', () => { const input = { foo_bar: 'val', task_id: 'keep' }; const result = camelCaseKeys(input, ['foo_bar']); expect(result).toHaveProperty('fooBar', 'val'); expect(result).toHaveProperty('task_id', 'keep'); }); it('handles empty objects', () => { expect(camelCaseKeys({})).toEqual({}); }); it('handles empty arrays', () => { expect(camelCaseKeys([] as any)).toEqual([]); }); it('does not recurse into labels — snake_case label keys pass through unchanged', () => { const input = { task_id: 't1', labels: { error_type: 'force_terminated', response_language: 'fr' }, }; const result = camelCaseKeys(input); expect(result).toEqual({ taskId: 't1', labels: { error_type: 'force_terminated', response_language: 'fr' }, }); }); it('does not recurse into labels when the value is null', () => { const input = { task_id: 't1', labels: { error_type: null } }; const result = camelCaseKeys(input); expect(result).toEqual({ taskId: 't1', labels: { error_type: null } }); }); it('converts all default keys', () => { const input = { task_id: 'a', model_id: 'b', depends_on: 'c', display_value: 'd', numeric_value: 'e', min_value: 'f', max_value: 'g', task_type: 'h', num_tasks: 'i', start_timestamp: 'j', end_timestamp: 'k', document_id: 'l', display_name: 'm', }; const result = camelCaseKeys(input); expect(result).toEqual({ taskId: 'a', modelId: 'b', dependsOn: 'c', displayValue: 'd', numericValue: 'e', minValue: 'f', maxValue: 'g', taskType: 'h', numTasks: 'i', startTimestamp: 'j', endTimestamp: 'k', documentId: 'l', displayName: 'm', }); }); }); // --- snakeCaseKeys --- describe('snakeCaseKeys', () => { it('converts default camelCase keys to snake_case', () => { const input = { taskId: '1', modelId: 'm1', otherKey: 'keep' }; const result = snakeCaseKeys(input); expect(result).toEqual({ task_id: '1', model_id: 'm1', otherKey: 'keep' }); }); it('handles nested objects recursively', () => { const input = { taskId: '1', nested: { modelId: 'm1' } }; const result = snakeCaseKeys(input); expect(result).toEqual({ task_id: '1', nested: { model_id: 'm1' } }); }); it('handles arrays of objects', () => { const input = [{ taskId: '1' }, { taskId: '2' }]; const result = snakeCaseKeys(input as any); expect(result).toEqual([{ task_id: '1' }, { task_id: '2' }]); }); it('returns primitives unchanged', () => { expect(snakeCaseKeys('hello' as any)).toBe('hello'); expect(snakeCaseKeys(42 as any)).toBe(42); }); it('uses custom keys list when provided', () => { const input = { fooBar: 'val', taskId: 'keep' }; const result = snakeCaseKeys(input, ['fooBar']); expect(result).toHaveProperty('foo_bar', 'val'); expect(result).toHaveProperty('taskId', 'keep'); }); it('does not recurse into labels — label keys pass through unchanged on export round-trip', () => { const input = { taskId: 't1', labels: { error_type: 'ast_decoder:decoder_failed' }, }; const result = snakeCaseKeys(input); expect(result).toEqual({ task_id: 't1', labels: { error_type: 'ast_decoder:decoder_failed' }, }); }); }); // --- areObjectsIntersecting --- describe('areObjectsIntersecting', () => { it('returns true when objects share a key-value pair', () => { const a = { color: 'red' }; const b = { color: 'red' }; expect(areObjectsIntersecting(a, b)).toBe(true); }); it('returns false when values differ for same key', () => { const a = { color: 'red' }; const b = { color: 'blue' }; expect(areObjectsIntersecting(a, b)).toBe(false); }); it('treats "all" as matching any value', () => { const a = { color: 'all' }; const b = { color: 'red' }; expect(areObjectsIntersecting(a, b)).toBe(true); }); it('"all" matches even when key is missing from b', () => { const a = { color: 'all' }; const b = { size: 'large' }; expect(areObjectsIntersecting(a, b)).toBe(true); }); it('returns false when a has a non-empty value but key is missing in b', () => { const a = { color: 'red' }; const b = { size: 'large' }; expect(areObjectsIntersecting(a, b)).toBe(false); }); it('handles array values with intersection', () => { const a = { color: ['red', 'blue'] }; const b = { color: ['blue', 'green'] }; expect(areObjectsIntersecting(a, b)).toBe(true); }); it('handles array values without intersection', () => { const a = { color: ['red', 'blue'] }; const b = { color: ['green', 'yellow'] }; expect(areObjectsIntersecting(a, b)).toBe(false); }); it('requires all keys in a to intersect (AND logic)', () => { const a = { color: 'red', size: 'large' }; const b = { color: 'red', size: 'small' }; expect(areObjectsIntersecting(a, b)).toBe(false); }); it('returns true when all keys in a intersect with b', () => { const a = { color: 'red', size: 'large' }; const b = { color: 'red', size: 'large' }; expect(areObjectsIntersecting(a, b)).toBe(true); }); it('returns false when a has non-empty value and b value is empty', () => { const a = { color: 'red' }; const b = { color: '' }; expect(areObjectsIntersecting(a, b)).toBe(false); }); });