n8n/packages/@n8n/nodes-langchain/nodes/code/Code.node.test.ts

179 lines
6.9 KiB
TypeScript

import { LOG_LEVELS } from 'n8n-workflow';
import { transformLegacyLangchainImport, createSandboxLogger } from './Code.node';
describe('Code.node', () => {
describe('createSandboxLogger', () => {
const logLevelKeys = LOG_LEVELS.filter((level) => level !== 'silent');
function buildMockLogger() {
const logger = {
error: vi.fn(),
warn: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
// Sensitive properties that must never appear in the sandbox
globalConfig: vi.fn(),
instanceSettingsConfig: vi.fn(),
internalLogger: vi.fn(),
};
return logger;
}
it('should expose only non-silent log level methods', () => {
const mockLogger = buildMockLogger();
const sandboxLogger = createSandboxLogger(mockLogger);
expect(Object.keys(sandboxLogger)).toEqual(expect.arrayContaining(logLevelKeys));
expect(Object.keys(sandboxLogger)).toHaveLength(logLevelKeys.length);
});
it('should not expose sensitive properties on the sandbox logger', () => {
const mockLogger = buildMockLogger();
const sandboxLogger = createSandboxLogger(mockLogger);
expect((sandboxLogger as Record<string, unknown>).globalConfig).toBeUndefined();
expect((sandboxLogger as Record<string, unknown>).instanceSettingsConfig).toBeUndefined();
expect((sandboxLogger as Record<string, unknown>).internalLogger).toBeUndefined();
});
it('should not call sensitive properties when log methods are invoked', () => {
const mockLogger = buildMockLogger();
const sandboxLogger = createSandboxLogger(mockLogger);
for (const level of logLevelKeys) {
sandboxLogger[level]('test message');
}
expect(mockLogger.globalConfig).not.toHaveBeenCalled();
expect(mockLogger.instanceSettingsConfig).not.toHaveBeenCalled();
expect(mockLogger.internalLogger).not.toHaveBeenCalled();
});
it('should delegate log method calls to the original logger', () => {
const mockLogger = buildMockLogger();
const sandboxLogger = createSandboxLogger(mockLogger);
for (const level of logLevelKeys) {
sandboxLogger[level]('test message', { key: 'value' });
expect(mockLogger[level]).toHaveBeenCalledWith('test message', { key: 'value' });
}
});
});
describe('transformLegacyLangchainImport', () => {
describe('transforms legacy langchain imports to @langchain/classic', () => {
it('should transform langchain/chains to @langchain/classic/chains', () => {
const result = transformLegacyLangchainImport('langchain/chains');
expect(result).toBe('@langchain/classic/chains');
});
it('should transform langchain/agents to @langchain/classic/agents', () => {
const result = transformLegacyLangchainImport('langchain/agents');
expect(result).toBe('@langchain/classic/agents');
});
it('should transform langchain/memory to @langchain/classic/memory', () => {
const result = transformLegacyLangchainImport('langchain/memory');
expect(result).toBe('@langchain/classic/memory');
});
it('should transform langchain/retrievers to @langchain/classic/retrievers', () => {
const result = transformLegacyLangchainImport('langchain/retrievers');
expect(result).toBe('@langchain/classic/retrievers');
});
it('should transform langchain/tools to @langchain/classic/tools', () => {
const result = transformLegacyLangchainImport('langchain/tools');
expect(result).toBe('@langchain/classic/tools');
});
it('should transform langchain/output_parsers to @langchain/classic/output_parsers', () => {
const result = transformLegacyLangchainImport('langchain/output_parsers');
expect(result).toBe('@langchain/classic/output_parsers');
});
it('should transform nested paths like langchain/chains/combine_documents', () => {
const result = transformLegacyLangchainImport('langchain/chains/combine_documents');
expect(result).toBe('@langchain/classic/chains/combine_documents');
});
it('should transform langchain/embeddings/cache_backed', () => {
const result = transformLegacyLangchainImport('langchain/embeddings/cache_backed');
expect(result).toBe('@langchain/classic/embeddings/cache_backed');
});
it('should transform langchain/document_loaders/fs/text', () => {
const result = transformLegacyLangchainImport('langchain/document_loaders/fs/text');
expect(result).toBe('@langchain/classic/document_loaders/fs/text');
});
it('should transform langchain/text_splitter', () => {
const result = transformLegacyLangchainImport('langchain/text_splitter');
expect(result).toBe('@langchain/classic/text_splitter');
});
it('should transform langchain/experimental/autogpt', () => {
const result = transformLegacyLangchainImport('langchain/experimental/autogpt');
expect(result).toBe('@langchain/classic/experimental/autogpt');
});
});
describe('does not transform non-classic imports', () => {
it('should not transform @langchain/core imports', () => {
const result = transformLegacyLangchainImport('@langchain/core/prompts');
expect(result).toBe('@langchain/core/prompts');
});
it('should not transform @langchain/community imports', () => {
const result = transformLegacyLangchainImport(
'@langchain/community/tools/wikipedia_query_run',
);
expect(result).toBe('@langchain/community/tools/wikipedia_query_run');
});
it('should not transform @langchain/openai imports', () => {
const result = transformLegacyLangchainImport('@langchain/openai');
expect(result).toBe('@langchain/openai');
});
it('should not transform already correct @langchain/classic imports', () => {
const result = transformLegacyLangchainImport('@langchain/classic/chains');
expect(result).toBe('@langchain/classic/chains');
});
it('should return original module name for non-langchain imports', () => {
const result = transformLegacyLangchainImport('lodash');
expect(result).toBe('lodash');
});
});
describe('handles edge cases', () => {
it('should handle langchain/hub imports', () => {
const result = transformLegacyLangchainImport('langchain/hub');
expect(result).toBe('@langchain/classic/hub');
});
it('should handle langchain/indexes imports', () => {
const result = transformLegacyLangchainImport('langchain/indexes');
expect(result).toBe('@langchain/classic/indexes');
});
it('should handle langchain/sql_db imports', () => {
const result = transformLegacyLangchainImport('langchain/sql_db');
expect(result).toBe('@langchain/classic/sql_db');
});
it('should handle langchain/storage/in_memory', () => {
const result = transformLegacyLangchainImport('langchain/storage/in_memory');
expect(result).toBe('@langchain/classic/storage/in_memory');
});
it('should handle langchain/stores/message/in_memory', () => {
const result = transformLegacyLangchainImport('langchain/stores/message/in_memory');
expect(result).toBe('@langchain/classic/stores/message/in_memory');
});
});
});
});