mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(core): Integrate object store as binary data manager (#7253)
Depends on: #7225 | Story: [PAY-848](https://linear.app/n8n/issue/PAY-848) This PR integrates the object store service as a new binary data manager for Enterprise.
This commit is contained in:
147
packages/core/test/ObjectStore.manager.test.ts
Normal file
147
packages/core/test/ObjectStore.manager.test.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import { ObjectStoreManager } from '@/BinaryData/ObjectStore.manager';
|
||||
import { ObjectStoreService } from '@/ObjectStore/ObjectStore.service.ee';
|
||||
import { isStream } from '@/ObjectStore/utils';
|
||||
import { mockInstance, toStream } from './utils';
|
||||
|
||||
jest.mock('fs/promises');
|
||||
|
||||
const objectStoreService = mockInstance(ObjectStoreService);
|
||||
const objectStoreManager = new ObjectStoreManager(objectStoreService);
|
||||
|
||||
const toFileId = (workflowId: string, executionId: string, fileUuid: string) =>
|
||||
`workflows/${workflowId}/executions/${executionId}/binary_data/${fileUuid}`;
|
||||
|
||||
const workflowId = 'ObogjVbqpNOQpiyV';
|
||||
const executionId = '999';
|
||||
const fileUuid = '71f6209b-5d48-41a2-a224-80d529d8bb32';
|
||||
const fileId = toFileId(workflowId, executionId, fileUuid);
|
||||
const prefix = `workflows/${workflowId}/executions/${executionId}/binary_data/`;
|
||||
|
||||
const otherWorkflowId = 'FHio8ftV6SrCAfPJ';
|
||||
const otherExecutionId = '888';
|
||||
const otherFileUuid = '71f6209b-5d48-41a2-a224-80d529d8bb33';
|
||||
const otherFileId = toFileId(otherWorkflowId, otherExecutionId, otherFileUuid);
|
||||
|
||||
const mockBuffer = Buffer.from('Test data');
|
||||
const mockStream = toStream(mockBuffer);
|
||||
|
||||
beforeAll(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('store()', () => {
|
||||
it('should store a buffer', async () => {
|
||||
const metadata = { mimeType: 'text/plain' };
|
||||
|
||||
const result = await objectStoreManager.store(workflowId, executionId, mockBuffer, metadata);
|
||||
|
||||
expect(result.fileId.startsWith(prefix)).toBe(true);
|
||||
expect(result.fileSize).toBe(mockBuffer.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPath()', () => {
|
||||
it('should return a path', async () => {
|
||||
const path = objectStoreManager.getPath(fileId);
|
||||
|
||||
expect(path).toBe(fileId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAsBuffer()', () => {
|
||||
it('should return a buffer', async () => {
|
||||
// @ts-expect-error Overload signature seemingly causing the return type to be misinferred
|
||||
objectStoreService.get.mockResolvedValue(mockBuffer);
|
||||
|
||||
const result = await objectStoreManager.getAsBuffer(fileId);
|
||||
|
||||
expect(Buffer.isBuffer(result)).toBe(true);
|
||||
expect(objectStoreService.get).toHaveBeenCalledWith(fileId, { mode: 'buffer' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAsStream()', () => {
|
||||
it('should return a stream', async () => {
|
||||
objectStoreService.get.mockResolvedValue(mockStream);
|
||||
|
||||
const stream = await objectStoreManager.getAsStream(fileId);
|
||||
|
||||
expect(isStream(stream)).toBe(true);
|
||||
expect(objectStoreService.get).toHaveBeenCalledWith(fileId, { mode: 'stream' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMetadata()', () => {
|
||||
it('should return metadata', async () => {
|
||||
const mimeType = 'text/plain';
|
||||
const fileName = 'file.txt';
|
||||
|
||||
objectStoreService.getMetadata.mockResolvedValue({
|
||||
'content-length': '1',
|
||||
'content-type': mimeType,
|
||||
'x-amz-meta-filename': fileName,
|
||||
});
|
||||
|
||||
const metadata = await objectStoreManager.getMetadata(fileId);
|
||||
|
||||
expect(metadata).toEqual(expect.objectContaining({ fileSize: 1, mimeType, fileName }));
|
||||
expect(objectStoreService.getMetadata).toHaveBeenCalledWith(fileId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('copyByFileId()', () => {
|
||||
it('should copy by file ID and return the file ID', async () => {
|
||||
const targetFileId = await objectStoreManager.copyByFileId(workflowId, executionId, fileId);
|
||||
|
||||
expect(targetFileId.startsWith(prefix)).toBe(true);
|
||||
expect(objectStoreService.get).toHaveBeenCalledWith(fileId, { mode: 'buffer' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('copyByFilePath()', () => {
|
||||
test('should copy by file path and return the file ID and size', async () => {
|
||||
const sourceFilePath = 'path/to/file/in/filesystem';
|
||||
const metadata = { mimeType: 'text/plain' };
|
||||
|
||||
fs.readFile = jest.fn().mockResolvedValue(mockBuffer);
|
||||
|
||||
const result = await objectStoreManager.copyByFilePath(
|
||||
workflowId,
|
||||
executionId,
|
||||
sourceFilePath,
|
||||
metadata,
|
||||
);
|
||||
|
||||
expect(result.fileId.startsWith(prefix)).toBe(true);
|
||||
expect(fs.readFile).toHaveBeenCalledWith(sourceFilePath);
|
||||
expect(result.fileSize).toBe(mockBuffer.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteMany()', () => {
|
||||
it('should delete many files by prefix', async () => {
|
||||
const ids = [
|
||||
{ workflowId, executionId },
|
||||
{ workflowId: otherWorkflowId, executionId: otherExecutionId },
|
||||
];
|
||||
|
||||
const promise = objectStoreManager.deleteMany(ids);
|
||||
|
||||
await expect(promise).resolves.not.toThrow();
|
||||
|
||||
expect(objectStoreService.deleteMany).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rename()', () => {
|
||||
it('should rename a file', async () => {
|
||||
const promise = objectStoreManager.rename(fileId, otherFileId);
|
||||
|
||||
await expect(promise).resolves.not.toThrow();
|
||||
|
||||
expect(objectStoreService.get).toHaveBeenCalledWith(fileId, { mode: 'buffer' });
|
||||
expect(objectStoreService.getMetadata).toHaveBeenCalledWith(fileId);
|
||||
expect(objectStoreService.deleteOne).toHaveBeenCalledWith(fileId);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user