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

548 lines
16 KiB
TypeScript
Raw Normal View History

import { cache, imageLoadPoolManager, Enums } from '@cornerstonejs/core';
import getInterleavedFrames from './getInterleavedFrames';
import zip from 'lodash.zip';
import compact from 'lodash.compact';
import flatten from 'lodash.flatten';
import interleaveCenterLoader from './interleaveCenterLoader';
jest.mock('@cornerstonejs/core', () => ({
cache: {
getVolume: jest.fn(),
},
imageLoadPoolManager: {
addRequest: jest.fn(),
},
Enums: {
RequestType: {
Prefetch: 'Prefetch',
},
},
}));
jest.mock('./getInterleavedFrames', () => jest.fn());
jest.mock('lodash.zip', () => jest.fn());
jest.mock('lodash.compact', () => jest.fn());
jest.mock('lodash.flatten', () => jest.fn());
describe('interleaveCenterLoader', () => {
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 mockDisplaySetsInfo = [
{
displaySetInstanceUID: 'test-volume-id',
displaySetOptions: {},
},
];
const mockMatchDetails = new Map([
[
'viewport-1',
{
displaySetsInfo: mockDisplaySetsInfo,
},
],
]);
const defaultParameters = {
data: {
viewportId: 'test-viewport-id',
volumeInputArray: [mockVolumeInput],
},
displaySetsMatchDetails: {},
viewportMatchDetails: mockMatchDetails,
};
beforeEach(() => {
jest.clearAllMocks();
(cache.getVolume as jest.Mock).mockReturnValue(mockVolume);
mockVolume.getImageLoadRequests.mockReturnValue([mockImageLoadRequest]);
(getInterleavedFrames as jest.Mock).mockReturnValue([{ imageId: 'test-image-id' }]);
(zip as jest.Mock).mockImplementation((...args) => args[0]);
(compact as jest.Mock).mockImplementation(input => input?.filter(Boolean) ?? []);
(flatten as jest.Mock).mockImplementation(input => input?.flat() ?? []);
});
it('should store viewport and volume input array mapping on first call', () => {
interleaveCenterLoader(defaultParameters);
expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId);
});
it('should return early when volume is not found', () => {
(cache.getVolume as jest.Mock).mockReturnValue(null);
const result = interleaveCenterLoader(defaultParameters);
expect(result).toBeUndefined();
expect(mockVolume.getImageLoadRequests).not.toHaveBeenCalled();
});
it('should return early when volume input array size does not match display set UIDs', () => {
const mockMatchDetailsWithDifferentSize = new Map([
[
'viewport-1',
{
displaySetsInfo: [
{ displaySetInstanceUID: 'display-set-1' },
{ displaySetInstanceUID: 'display-set-2' },
],
},
],
]);
const result = interleaveCenterLoader({
...defaultParameters,
viewportMatchDetails: mockMatchDetailsWithDifferentSize,
});
expect(result).toBeUndefined();
});
it('should process volume and create interleaved requests', () => {
const result = interleaveCenterLoader(defaultParameters);
expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId);
expect(mockVolume.getImageLoadRequests).toHaveBeenCalled();
expect(getInterleavedFrames).toHaveBeenCalledWith(['test-image-id']);
expect(zip).toHaveBeenCalled();
expect(compact).toHaveBeenCalled();
expect(flatten).toHaveBeenCalled();
expect(result).toBeInstanceOf(Map);
});
it('should add requests to imageLoadPoolManager with correct parameters', () => {
interleaveCenterLoader(defaultParameters);
expect(imageLoadPoolManager.addRequest).toHaveBeenCalledWith(
expect.any(Function),
Enums.RequestType.Prefetch,
mockImageLoadRequest.additionalDetails,
0
);
});
it('should bind callLoadImage with correct parameters', () => {
interleaveCenterLoader(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 volumes without image load requests', () => {
mockVolume.getImageLoadRequests.mockReturnValue([]);
const result = interleaveCenterLoader(defaultParameters);
expect(zip).toHaveBeenCalledWith();
expect(result).toBeInstanceOf(Map);
});
it('should handle volumes with empty requests array', () => {
mockVolume.getImageLoadRequests.mockReturnValue([]);
const result = interleaveCenterLoader(defaultParameters);
expect(getInterleavedFrames).not.toHaveBeenCalled();
expect(result).toBeInstanceOf(Map);
});
it('should handle volumes with null first request', () => {
mockVolume.getImageLoadRequests.mockReturnValue([null]);
const result = interleaveCenterLoader(defaultParameters);
expect(getInterleavedFrames).not.toHaveBeenCalled();
expect(result).toBeInstanceOf(Map);
});
it('should handle volumes with requests without imageId', () => {
const requestWithoutImageId = {
callLoadImage: jest.fn(),
additionalDetails: 'test-details',
};
mockVolume.getImageLoadRequests.mockReturnValue([requestWithoutImageId]);
const result = interleaveCenterLoader(defaultParameters);
expect(getInterleavedFrames).not.toHaveBeenCalled();
expect(result).toBeInstanceOf(Map);
});
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);
const mockMatchDetailsWithMultipleVolumes = new Map([
[
'viewport-1',
{
displaySetsInfo: [
{ displaySetInstanceUID: 'test-volume-id' },
{ displaySetInstanceUID: 'test-volume-id-2' },
],
},
],
]);
interleaveCenterLoader({
...defaultParameters,
data: {
...defaultParameters.data,
volumeInputArray: [mockVolumeInput, mockVolumeInput2],
},
viewportMatchDetails: mockMatchDetailsWithMultipleVolumes,
});
expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId);
expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput2.volumeId);
});
it('should handle duplicate volume IDs by not adding them again', () => {
const duplicateVolumeInput = {
volumeId: mockVolumeInput.volumeId,
};
interleaveCenterLoader({
...defaultParameters,
data: {
...defaultParameters.data,
volumeInputArray: [mockVolumeInput, duplicateVolumeInput],
},
});
expect(cache.getVolume).toHaveBeenCalledTimes(3);
});
it('should clear internal maps after processing', () => {
const firstResult = interleaveCenterLoader(defaultParameters);
const secondResult = interleaveCenterLoader(defaultParameters);
expect(firstResult).toBeInstanceOf(Map);
expect(secondResult).toBeInstanceOf(Map);
});
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]);
(getInterleavedFrames as jest.Mock).mockReturnValue([
{ imageId: 'test-image-id' },
{ imageId: 'test-image-id-2' },
]);
(compact as jest.Mock).mockReturnValue([mockImageLoadRequest, mockImageLoadRequest2]);
(flatten as jest.Mock).mockReturnValue([mockImageLoadRequest, mockImageLoadRequest2]);
interleaveCenterLoader(defaultParameters);
expect(getInterleavedFrames).toHaveBeenCalledWith(['test-image-id', 'test-image-id-2']);
expect(imageLoadPoolManager.addRequest).toHaveBeenCalledTimes(2);
});
it('should handle getInterleavedFrames returning empty array', () => {
(getInterleavedFrames as jest.Mock).mockReturnValue([]);
const result = interleaveCenterLoader(defaultParameters);
expect(imageLoadPoolManager.addRequest).not.toHaveBeenCalled();
expect(result).toBeInstanceOf(Map);
});
it('should handle zip returning empty result', () => {
(zip as jest.Mock).mockReturnValue([]);
const result = interleaveCenterLoader(defaultParameters);
expect(compact).toHaveBeenCalledWith([]);
expect(result).toBeInstanceOf(Map);
});
it('should handle compact filtering out null values', () => {
(compact as jest.Mock).mockReturnValue([]);
const result = interleaveCenterLoader(defaultParameters);
expect(imageLoadPoolManager.addRequest).not.toHaveBeenCalled();
expect(result).toBeInstanceOf(Map);
});
it('should map imageIds to requests correctly', () => {
const mockImageLoadRequest2 = {
imageId: 'test-image-id-2',
callLoadImage: jest.fn(),
};
mockVolume.getImageLoadRequests.mockReturnValue([mockImageLoadRequest, mockImageLoadRequest2]);
(getInterleavedFrames as jest.Mock).mockReturnValue([
{ imageId: 'test-image-id-2' },
{ imageId: 'test-image-id' },
]);
interleaveCenterLoader(defaultParameters);
expect(getInterleavedFrames).toHaveBeenCalledWith(['test-image-id', 'test-image-id-2']);
});
it('should return copy of viewport volume input array map', () => {
const result = interleaveCenterLoader(defaultParameters);
expect(result.has(defaultParameters.data.viewportId)).toBe(true);
expect(result.get(defaultParameters.data.viewportId)).toEqual(
defaultParameters.data.volumeInputArray
);
});
it('should handle multiple match details with different displaySets', () => {
const mockMultipleMatchDetails = new Map([
[
'viewport-1',
{
displaySetsInfo: [{ displaySetInstanceUID: 'display-set-1' }],
},
],
[
'viewport-2',
{
displaySetsInfo: [{ displaySetInstanceUID: 'display-set-2' }],
},
],
]);
const result = interleaveCenterLoader({
...defaultParameters,
viewportMatchDetails: mockMultipleMatchDetails,
});
expect(result).toBeUndefined();
});
it('should handle empty match details', () => {
const emptyMatchDetails = new Map();
const result = interleaveCenterLoader({
...defaultParameters,
viewportMatchDetails: emptyMatchDetails,
});
expect(result).toBeUndefined();
});
it('should handle empty volumeInputArray', () => {
const result = interleaveCenterLoader({
...defaultParameters,
data: {
...defaultParameters.data,
volumeInputArray: [],
},
});
expect(result).toBeUndefined();
});
it('should handle requests with missing properties gracefully', () => {
const incompleteRequest = {
imageId: 'test-image-id',
callLoadImage: jest.fn(),
};
mockVolume.getImageLoadRequests.mockReturnValue([incompleteRequest]);
(getInterleavedFrames as jest.Mock).mockReturnValue([{ imageId: 'test-image-id' }]);
(compact as jest.Mock).mockReturnValue([incompleteRequest]);
(flatten as jest.Mock).mockReturnValue([incompleteRequest]);
interleaveCenterLoader(defaultParameters);
expect(imageLoadPoolManager.addRequest).toHaveBeenCalledWith(
expect.any(Function),
Enums.RequestType.Prefetch,
undefined,
0
);
});
it('should process different viewport IDs separately', () => {
const firstResult = interleaveCenterLoader({
...defaultParameters,
data: {
viewportId: 'viewport-1',
volumeInputArray: [mockVolumeInput],
},
});
const secondResult = interleaveCenterLoader({
...defaultParameters,
data: {
viewportId: 'viewport-2',
volumeInputArray: [mockVolumeInput],
},
});
expect(firstResult.has('viewport-1')).toBe(true);
expect(secondResult.has('viewport-2')).toBe(true);
});
it('should accumulate state across multiple calls before processing', () => {
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(mockVolume)
.mockReturnValueOnce(mockVolume2);
const firstCall = interleaveCenterLoader({
...defaultParameters,
data: {
viewportId: 'viewport-1',
volumeInputArray: [mockVolumeInput],
},
viewportMatchDetails: new Map([
[
'viewport-1',
{
displaySetsInfo: [
{ displaySetInstanceUID: 'test-volume-id' },
{ displaySetInstanceUID: 'missing-volume-id' },
],
},
],
]),
});
expect(firstCall).toBeUndefined();
const secondCall = interleaveCenterLoader({
...defaultParameters,
data: {
viewportId: 'viewport-2',
volumeInputArray: [mockVolumeInput2],
},
viewportMatchDetails: new Map([
[
'viewport-1',
{
displaySetsInfo: [{ displaySetInstanceUID: 'test-volume-id' }],
},
],
[
'viewport-2',
{
displaySetsInfo: [{ displaySetInstanceUID: 'test-volume-id-2' }],
},
],
]),
});
expect(secondCall).toBeInstanceOf(Map);
expect(secondCall.has('viewport-1')).toBe(true);
expect(secondCall.has('viewport-2')).toBe(true);
});
it('should handle volume with undefined metadata', () => {
const mockVolumeWithoutMetadata = {
getImageLoadRequests: jest.fn().mockReturnValue([]),
};
(cache.getVolume as jest.Mock).mockReturnValue(mockVolumeWithoutMetadata);
expect(() => interleaveCenterLoader(defaultParameters)).toThrow();
});
it('should handle multiple volumes with same image requests', () => {
const mockVolume2 = {
metadata: { SeriesInstanceUID: 'test-series-uid-2' },
getImageLoadRequests: jest.fn().mockReturnValue([mockImageLoadRequest]),
};
const mockVolumeInput2 = { volumeId: 'test-volume-id-2' };
(cache.getVolume as jest.Mock)
.mockReturnValueOnce(mockVolume)
.mockReturnValueOnce(mockVolume2)
.mockReturnValueOnce(mockVolume)
.mockReturnValueOnce(mockVolume2);
const mockMatchDetailsWithBothVolumes = new Map([
[
'viewport-1',
{
displaySetsInfo: [
{ displaySetInstanceUID: 'test-volume-id' },
{ displaySetInstanceUID: 'test-volume-id-2' },
],
},
],
]);
(compact as jest.Mock).mockReturnValue([mockImageLoadRequest]);
(flatten as jest.Mock).mockReturnValue([mockImageLoadRequest]);
interleaveCenterLoader({
...defaultParameters,
data: {
...defaultParameters.data,
volumeInputArray: [mockVolumeInput, mockVolumeInput2],
},
viewportMatchDetails: mockMatchDetailsWithBothVolumes,
});
expect(imageLoadPoolManager.addRequest).toHaveBeenCalledTimes(2);
});
});