import type { IRunExecutionData } from 'n8n-workflow'; import { InvalidExecutionMetadataError } from '@/errors/invalid-execution-metadata.error'; import { setWorkflowExecutionMetadata, setAllWorkflowExecutionMetadata, KV_LIMIT, getWorkflowExecutionMetadata, getAllWorkflowExecutionMetadata, } from '@/ExecutionMetadata'; describe('Execution Metadata functions', () => { test('setWorkflowExecutionMetadata will set a value', () => { const metadata = {}; const executionData = { resultData: { metadata, }, } as IRunExecutionData; setWorkflowExecutionMetadata(executionData, 'test1', 'value1'); expect(metadata).toEqual({ test1: 'value1', }); }); test('setAllWorkflowExecutionMetadata will set multiple values', () => { const metadata = {}; const executionData = { resultData: { metadata, }, } as IRunExecutionData; setAllWorkflowExecutionMetadata(executionData, { test1: 'value1', test2: 'value2', }); expect(metadata).toEqual({ test1: 'value1', test2: 'value2', }); }); test('setWorkflowExecutionMetadata should only convert numbers to strings', () => { const metadata = {}; const executionData = { resultData: { metadata, }, } as IRunExecutionData; expect(() => setWorkflowExecutionMetadata(executionData, 'test1', 1234)).not.toThrow( InvalidExecutionMetadataError, ); expect(metadata).toEqual({ test1: '1234', }); expect(() => setWorkflowExecutionMetadata(executionData, 'test2', {})).toThrow( InvalidExecutionMetadataError, ); expect(metadata).not.toEqual({ test1: '1234', test2: {}, }); }); test('setAllWorkflowExecutionMetadata should not convert values to strings and should set other values correctly', () => { const metadata = {}; const executionData = { resultData: { metadata, }, } as IRunExecutionData; expect(() => setAllWorkflowExecutionMetadata(executionData, { test1: {} as unknown as string, test2: [] as unknown as string, test3: 'value3', test4: 'value4', }), ).toThrow(InvalidExecutionMetadataError); expect(metadata).toEqual({ test3: 'value3', test4: 'value4', }); }); test('setWorkflowExecutionMetadata should validate key characters', () => { const metadata = {}; const executionData = { resultData: { metadata, }, } as IRunExecutionData; expect(() => setWorkflowExecutionMetadata(executionData, 'te$t1$', 1234)).toThrow( InvalidExecutionMetadataError, ); expect(metadata).not.toEqual({ test1: '1234', }); }); test('setWorkflowExecutionMetadata should limit the number of metadata entries', () => { const metadata = {}; const executionData = { resultData: { metadata, }, } as IRunExecutionData; const expected: Record = {}; for (let i = 0; i < KV_LIMIT; i++) { expected[`test${i + 1}`] = `value${i + 1}`; } for (let i = 0; i < KV_LIMIT + 10; i++) { setWorkflowExecutionMetadata(executionData, `test${i + 1}`, `value${i + 1}`); } expect(metadata).toEqual(expected); }); test('getWorkflowExecutionMetadata should return a single value for an existing key', () => { const metadata: Record = { test1: 'value1' }; const executionData = { resultData: { metadata, }, } as IRunExecutionData; expect(getWorkflowExecutionMetadata(executionData, 'test1')).toBe('value1'); }); test('getWorkflowExecutionMetadata should return undefined for an unset key', () => { const metadata: Record = { test1: 'value1' }; const executionData = { resultData: { metadata, }, } as IRunExecutionData; expect(getWorkflowExecutionMetadata(executionData, 'test2')).toBeUndefined(); }); test('getAllWorkflowExecutionMetadata should return all metadata', () => { const metadata: Record = { test1: 'value1', test2: 'value2' }; const executionData = { resultData: { metadata, }, } as IRunExecutionData; expect(getAllWorkflowExecutionMetadata(executionData)).toEqual(metadata); }); test('getAllWorkflowExecutionMetadata should not an object that modifies internal state', () => { const metadata: Record = { test1: 'value1', test2: 'value2' }; const executionData = { resultData: { metadata, }, } as IRunExecutionData; getAllWorkflowExecutionMetadata(executionData).test1 = 'changed'; expect(metadata.test1).not.toBe('changed'); expect(metadata.test1).toBe('value1'); }); test('setWorkflowExecutionMetadata should truncate long keys', () => { const metadata = {}; const executionData = { resultData: { metadata, }, } as IRunExecutionData; setWorkflowExecutionMetadata( executionData, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab', 'value1', ); expect(metadata).toEqual({ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: 'value1', }); }); test('setWorkflowExecutionMetadata should truncate long values', () => { const metadata = {}; const executionData = { resultData: { metadata, }, } as IRunExecutionData; setWorkflowExecutionMetadata( executionData, 'test1', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab', ); expect(metadata).toEqual({ test1: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', }); }); });