ohif-viewer/extensions/cornerstone/src/utils/nthLoader.test.ts

318 lines
9.5 KiB
TypeScript

import { cache, imageLoadPoolManager, Enums as csEnums } from '@cornerstonejs/core';
import interleaveNthLoader from './nthLoader';
import getNthFrames from './getNthFrames';
import interleave from './interleave';
jest.mock('@cornerstonejs/core', () => ({
...jest.requireActual('@cornerstonejs/core'),
cache: {
getVolume: jest.fn(),
},
imageLoadPoolManager: {
addRequest: jest.fn(),
},
}));
jest.mock('./getNthFrames', () => jest.fn());
jest.mock('./interleave', () => jest.fn());
describe('interleaveNthLoader', () => {
const mockVolumeInput = {
volumeId: 'test-volume-id',
};
const mockVolume = {
metadata: {
SeriesInstanceUID: 'test-series-uid',
},
getImageLoadRequests: jest.fn(),
};
const mockImageLoadRequest = {
imageId: 'test-image-id',
callLoadImage: jest.fn(),
additionalDetails: 'test-details',
imageIdIndex: 0,
options: { test: 'option' },
};
const defaultParameters = {
data: {
viewportId: 'test-viewport-id',
volumeInputArray: [mockVolumeInput],
},
displaySetsMatchDetails: {},
};
beforeEach(() => {
jest.clearAllMocks();
(cache.getVolume as jest.Mock).mockReturnValue(mockVolume);
mockVolume.getImageLoadRequests.mockReturnValue([mockImageLoadRequest]);
(getNthFrames as jest.Mock).mockImplementation(input => input);
(interleave as jest.Mock).mockImplementation(input => input.flat());
});
it('should store viewport and volume input array mapping', () => {
interleaveNthLoader(defaultParameters);
expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId);
});
it('should return early when volume is not found', () => {
(cache.getVolume as jest.Mock).mockReturnValue(null);
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
const result = interleaveNthLoader(defaultParameters);
expect(consoleSpy).toHaveBeenCalledWith("interleaveNthLoader::No volume, can't load it");
expect(result).toBeUndefined();
expect(mockVolume.getImageLoadRequests).not.toHaveBeenCalled();
consoleSpy.mockRestore();
});
it('should process volume input array and create image load requests', () => {
const result = interleaveNthLoader(defaultParameters);
expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId);
expect(mockVolume.getImageLoadRequests).toHaveBeenCalled();
expect(getNthFrames).toHaveBeenCalledWith([mockImageLoadRequest]);
expect(interleave).toHaveBeenCalledWith([[mockImageLoadRequest]]);
expect(result).toBeInstanceOf(Map);
});
it('should add requests to imageLoadPoolManager with correct parameters', () => {
interleaveNthLoader(defaultParameters);
expect(imageLoadPoolManager.addRequest).toHaveBeenCalledWith(
expect.any(Function),
csEnums.RequestType.Prefetch,
mockImageLoadRequest.additionalDetails,
0
);
});
it('should handle multiple volume inputs', () => {
const mockVolumeInput2 = {
volumeId: 'test-volume-id-2',
};
const mockVolume2 = {
metadata: {
SeriesInstanceUID: 'test-series-uid-2',
},
getImageLoadRequests: jest.fn().mockReturnValue([mockImageLoadRequest]),
};
(cache.getVolume as jest.Mock)
.mockReturnValueOnce(mockVolume)
.mockReturnValueOnce(mockVolume2)
.mockReturnValueOnce(mockVolume)
.mockReturnValueOnce(mockVolume2);
interleaveNthLoader({
...defaultParameters,
data: {
...defaultParameters.data,
volumeInputArray: [mockVolumeInput, mockVolumeInput2],
},
});
expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId);
expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput2.volumeId);
expect(mockVolume.getImageLoadRequests).toHaveBeenCalled();
expect(mockVolume2.getImageLoadRequests).toHaveBeenCalled();
});
it('should handle volumes without getImageLoadRequests method', () => {
const mockVolumeWithoutMethod = {
metadata: {
SeriesInstanceUID: 'test-series-uid',
},
};
(cache.getVolume as jest.Mock)
.mockReturnValueOnce(mockVolumeWithoutMethod)
.mockReturnValueOnce(mockVolumeWithoutMethod);
interleaveNthLoader(defaultParameters);
expect(getNthFrames).not.toHaveBeenCalled();
expect(interleave).toHaveBeenCalledWith([]);
});
it('should handle volumes with null getImageLoadRequests return', () => {
mockVolume.getImageLoadRequests.mockReturnValue(null);
interleaveNthLoader(defaultParameters);
expect(getNthFrames).not.toHaveBeenCalled();
expect(interleave).toHaveBeenCalledWith([]);
});
it('should filter out requests without imageId in first request', () => {
const requestWithoutImageId = {
callLoadImage: jest.fn(),
additionalDetails: 'test-details',
};
mockVolume.getImageLoadRequests
.mockReturnValueOnce([requestWithoutImageId, mockImageLoadRequest])
.mockReturnValueOnce([mockImageLoadRequest]);
interleaveNthLoader({
...defaultParameters,
data: {
...defaultParameters.data,
volumeInputArray: [mockVolumeInput, { volumeId: 'test-volume-id-2' }],
},
});
expect(getNthFrames).toHaveBeenCalledWith([mockImageLoadRequest]);
});
it('should handle empty image load requests', () => {
mockVolume.getImageLoadRequests.mockReturnValue([]);
interleaveNthLoader(defaultParameters);
expect(getNthFrames).not.toHaveBeenCalled();
expect(interleave).toHaveBeenCalledWith([]);
});
it('should bind callLoadImage with correct parameters', () => {
interleaveNthLoader(defaultParameters);
const addRequestCall = (imageLoadPoolManager.addRequest as jest.Mock).mock.calls[0];
const boundFunction = addRequestCall[0];
boundFunction();
expect(mockImageLoadRequest.callLoadImage).toHaveBeenCalledWith(
mockImageLoadRequest.imageId,
mockImageLoadRequest.imageIdIndex,
mockImageLoadRequest.options
);
});
it('should handle duplicate volume IDs by not adding them again', () => {
const duplicateVolumeInput = {
volumeId: mockVolumeInput.volumeId,
};
interleaveNthLoader({
...defaultParameters,
data: {
...defaultParameters.data,
volumeInputArray: [mockVolumeInput, duplicateVolumeInput],
},
});
expect(cache.getVolume).toHaveBeenCalledTimes(3);
expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId);
});
it('should clear internal maps after processing', () => {
const firstResult = interleaveNthLoader(defaultParameters);
const secondResult = interleaveNthLoader(defaultParameters);
expect(firstResult).toBeInstanceOf(Map);
expect(secondResult).toBeInstanceOf(Map);
expect(firstResult.get(defaultParameters.data.viewportId)).toEqual(
defaultParameters.data.volumeInputArray
);
});
it('should handle multiple image load requests', () => {
const mockImageLoadRequest2 = {
imageId: 'test-image-id-2',
callLoadImage: jest.fn(),
additionalDetails: 'test-details-2',
imageIdIndex: 1,
options: { test: 'option2' },
};
mockVolume.getImageLoadRequests.mockReturnValue([mockImageLoadRequest, mockImageLoadRequest2]);
(interleave as jest.Mock).mockReturnValue([mockImageLoadRequest, mockImageLoadRequest2]);
interleaveNthLoader(defaultParameters);
expect(imageLoadPoolManager.addRequest).toHaveBeenCalledTimes(2);
expect(imageLoadPoolManager.addRequest).toHaveBeenNthCalledWith(
1,
expect.any(Function),
csEnums.RequestType.Prefetch,
mockImageLoadRequest.additionalDetails,
0
);
expect(imageLoadPoolManager.addRequest).toHaveBeenNthCalledWith(
2,
expect.any(Function),
csEnums.RequestType.Prefetch,
mockImageLoadRequest2.additionalDetails,
0
);
});
it('should handle volume with undefined metadata', () => {
const mockVolumeWithoutMetadata = {
getImageLoadRequests: jest.fn().mockReturnValue([]),
};
(cache.getVolume as jest.Mock).mockReturnValueOnce(mockVolumeWithoutMetadata);
expect(() => interleaveNthLoader(defaultParameters)).toThrow();
});
it('should return copy of viewport volume input array map', () => {
const result = interleaveNthLoader(defaultParameters);
expect(result.has(defaultParameters.data.viewportId)).toBe(true);
expect(result.get(defaultParameters.data.viewportId)).toEqual(
defaultParameters.data.volumeInputArray
);
});
it('should handle requests with missing properties gracefully', () => {
const incompleteRequest = {
imageId: 'test-image-id',
callLoadImage: jest.fn(),
};
mockVolume.getImageLoadRequests.mockReturnValue([incompleteRequest]);
(interleave as jest.Mock).mockReturnValue([incompleteRequest]);
interleaveNthLoader(defaultParameters);
expect(imageLoadPoolManager.addRequest).toHaveBeenCalledWith(
expect.any(Function),
csEnums.RequestType.Prefetch,
undefined,
0
);
});
it('should process different viewport IDs separately', () => {
const firstResult = interleaveNthLoader({
...defaultParameters,
data: {
viewportId: 'viewport-1',
volumeInputArray: [mockVolumeInput],
},
});
const secondResult = interleaveNthLoader({
...defaultParameters,
data: {
viewportId: 'viewport-2',
volumeInputArray: [mockVolumeInput],
},
});
expect(firstResult.has('viewport-1')).toBe(true);
expect(secondResult.has('viewport-2')).toBe(true);
expect(firstResult.has('viewport-2')).toBe(false);
});
});