Skip to content

Commit 49099ad

Browse files
committed
Update AwsBedrockService and related tests to enhance model interaction and image processing capabilities
- Refactored AwsBedrockService to remove unused dependencies and streamline the model invocation process. - Updated the mock implementation in app.module.spec.ts to reflect changes in model response handling. - Enhanced test coverage in aws-bedrock.service.spec.ts by removing outdated tests and improving mock setups for medical information extraction. - Increased the maximum allowed file size for PDF uploads in security.utils.ts to accommodate larger documents.
1 parent c0f358f commit 49099ad

File tree

4 files changed

+70
-1895
lines changed

4 files changed

+70
-1895
lines changed

backend/src/app.module.spec.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ describe('AppModule', () => {
3434
})
3535
.overrideProvider(AwsBedrockService)
3636
.useValue({
37-
extractMedicalInfo: vi.fn().mockResolvedValue({}),
37+
listAvailableModels: vi.fn().mockResolvedValue({
38+
models: [],
39+
currentModelId: 'test-model-id',
40+
}),
3841
})
3942
.overrideProvider(PerplexityService)
4043
.useValue({
Lines changed: 0 additions & 327 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,7 @@
11
import { ConfigService } from '@nestjs/config';
2-
import { BadRequestException } from '@nestjs/common';
32
import { AwsBedrockService } from './aws-bedrock.service';
43
import { describe, it, expect, beforeEach, vi, beforeAll, afterAll } from 'vitest';
54

6-
// Mock validateFileSecurely to bypass file validation in tests
7-
vi.mock('../utils/security.utils', () => {
8-
return {
9-
validateFileSecurely: vi.fn().mockImplementation((buffer, fileType) => {
10-
if (!['image/jpeg', 'image/png', 'image/heic', 'image/heif'].includes(fileType)) {
11-
throw new BadRequestException('Only JPEG, PNG, and HEIC/HEIF images are allowed');
12-
}
13-
}),
14-
sanitizeMedicalData: vi.fn(data => data),
15-
RateLimiter: vi.fn().mockImplementation(() => ({
16-
tryRequest: vi.fn().mockReturnValue(true),
17-
})),
18-
};
19-
});
20-
215
// Mock the Logger
226
vi.mock('@nestjs/common', async () => {
237
const actual = (await vi.importActual('@nestjs/common')) as Record<string, any>;
@@ -66,13 +50,6 @@ describe('AwsBedrockService', () => {
6650

6751
// Create service instance
6852
service = new AwsBedrockService(mockConfigService);
69-
70-
// Mock private methods directly
71-
vi.spyOn(service as any, 'invokeBedrock').mockImplementation(() =>
72-
Promise.resolve({
73-
body: Buffer.from('{"mock": "response"}'),
74-
}),
75-
);
7653
});
7754

7855
describe('initialization', () => {
@@ -85,308 +62,4 @@ describe('AwsBedrockService', () => {
8562
expect(service['defaultMaxTokens']).toBe(1000);
8663
});
8764
});
88-
89-
describe('extractMedicalInfo', () => {
90-
const mockImageBuffer = Buffer.from('test image content');
91-
92-
it('should successfully extract medical information from image/jpeg', async () => {
93-
const mockMedicalInfo = {
94-
keyMedicalTerms: [
95-
{ term: 'Hemoglobin', definition: 'Protein in red blood cells that carries oxygen' },
96-
],
97-
labValues: [
98-
{
99-
name: 'Hemoglobin',
100-
value: '14.5',
101-
unit: 'g/dL',
102-
normalRange: '12.0-15.5',
103-
isAbnormal: false,
104-
},
105-
],
106-
diagnoses: [
107-
{
108-
condition: 'Normal Blood Count',
109-
details: 'All values within normal range',
110-
recommendations: 'Continue monitoring',
111-
},
112-
],
113-
metadata: {
114-
isMedicalReport: true,
115-
confidence: 0.95,
116-
missingInformation: [],
117-
},
118-
};
119-
120-
// Mock parseBedrockResponse method to return our expected data
121-
vi.spyOn(service as any, 'parseBedrockResponse').mockReturnValueOnce(mockMedicalInfo);
122-
123-
const result = await service.extractMedicalInfo(mockImageBuffer, 'image/jpeg');
124-
125-
expect(result).toHaveProperty('keyMedicalTerms');
126-
expect(result.keyMedicalTerms[0].term).toBe('Hemoglobin');
127-
expect(result.metadata.isMedicalReport).toBe(true);
128-
});
129-
130-
it('should successfully extract medical information from image/png', async () => {
131-
const mockMedicalInfo = {
132-
keyMedicalTerms: [{ term: 'Glucose', definition: 'Blood sugar level' }],
133-
labValues: [
134-
{ name: 'Glucose', value: '90', unit: 'mg/dL', normalRange: '70-100', isAbnormal: false },
135-
],
136-
diagnoses: [
137-
{
138-
condition: 'Normal Glucose',
139-
details: 'Normal blood sugar',
140-
recommendations: 'Continue healthy diet',
141-
},
142-
],
143-
metadata: {
144-
isMedicalReport: true,
145-
confidence: 0.92,
146-
missingInformation: [],
147-
},
148-
};
149-
150-
// Mock parseBedrockResponse method to return our expected data
151-
vi.spyOn(service as any, 'parseBedrockResponse').mockReturnValueOnce(mockMedicalInfo);
152-
153-
const result = await service.extractMedicalInfo(mockImageBuffer, 'image/png');
154-
155-
expect(result).toHaveProperty('keyMedicalTerms');
156-
expect(result.keyMedicalTerms[0].term).toBe('Glucose');
157-
expect(result.metadata.isMedicalReport).toBe(true);
158-
});
159-
160-
it('should successfully extract medical information from image/heic', async () => {
161-
const mockMedicalInfo = {
162-
keyMedicalTerms: [
163-
{ term: 'Cholesterol', definition: 'Lipid molecule found in cell membranes' },
164-
],
165-
labValues: [
166-
{
167-
name: 'Cholesterol',
168-
value: '180',
169-
unit: 'mg/dL',
170-
normalRange: '< 200',
171-
isAbnormal: false,
172-
},
173-
],
174-
diagnoses: [
175-
{
176-
condition: 'Normal Cholesterol',
177-
details: 'Within healthy range',
178-
recommendations: 'Continue heart-healthy diet',
179-
},
180-
],
181-
metadata: {
182-
isMedicalReport: true,
183-
confidence: 0.9,
184-
missingInformation: [],
185-
},
186-
};
187-
188-
// Mock parseBedrockResponse method to return our expected data
189-
vi.spyOn(service as any, 'parseBedrockResponse').mockReturnValueOnce(mockMedicalInfo);
190-
191-
const result = await service.extractMedicalInfo(mockImageBuffer, 'image/heic');
192-
193-
expect(result).toHaveProperty('keyMedicalTerms');
194-
expect(result.keyMedicalTerms[0].term).toBe('Cholesterol');
195-
expect(result.metadata.isMedicalReport).toBe(true);
196-
});
197-
198-
it('should successfully extract medical information from image/heif', async () => {
199-
const mockMedicalInfo = {
200-
keyMedicalTerms: [{ term: 'Triglycerides', definition: 'Type of fat found in blood' }],
201-
labValues: [
202-
{
203-
name: 'Triglycerides',
204-
value: '120',
205-
unit: 'mg/dL',
206-
normalRange: '< 150',
207-
isAbnormal: false,
208-
},
209-
],
210-
diagnoses: [
211-
{
212-
condition: 'Normal Triglycerides',
213-
details: 'Within healthy range',
214-
recommendations: 'Continue heart-healthy diet',
215-
},
216-
],
217-
metadata: {
218-
isMedicalReport: true,
219-
confidence: 0.88,
220-
missingInformation: [],
221-
},
222-
};
223-
224-
// Mock parseBedrockResponse method to return our expected data
225-
vi.spyOn(service as any, 'parseBedrockResponse').mockReturnValueOnce(mockMedicalInfo);
226-
227-
const result = await service.extractMedicalInfo(mockImageBuffer, 'image/heif');
228-
229-
expect(result).toHaveProperty('keyMedicalTerms');
230-
expect(result.keyMedicalTerms[0].term).toBe('Triglycerides');
231-
expect(result.metadata.isMedicalReport).toBe(true);
232-
});
233-
234-
it('should reject non-medical images', async () => {
235-
const nonMedicalInfo = {
236-
keyMedicalTerms: [],
237-
labValues: [],
238-
diagnoses: [],
239-
metadata: {
240-
isMedicalReport: false,
241-
confidence: 0.1,
242-
missingInformation: ['Not a medical image'],
243-
},
244-
};
245-
246-
// Mock parseBedrockResponse method to return our expected data
247-
vi.spyOn(service as any, 'parseBedrockResponse').mockReturnValueOnce(nonMedicalInfo);
248-
249-
const result = await service.extractMedicalInfo(mockImageBuffer, 'image/jpeg');
250-
expect(result.metadata.isMedicalReport).toBe(false);
251-
expect(result.metadata.missingInformation).toContain(
252-
'The image was not clearly identified as a medical document. Results may be limited.',
253-
);
254-
});
255-
256-
it('should handle low quality or unclear images', async () => {
257-
const lowQualityInfo = {
258-
keyMedicalTerms: [],
259-
labValues: [],
260-
diagnoses: [],
261-
metadata: {
262-
isMedicalReport: true,
263-
confidence: 0.3,
264-
missingInformation: ['Image too blurry', 'Text not readable'],
265-
},
266-
};
267-
268-
// Mock parseBedrockResponse method to return our expected data
269-
vi.spyOn(service as any, 'parseBedrockResponse').mockReturnValueOnce(lowQualityInfo);
270-
271-
const result = await service.extractMedicalInfo(mockImageBuffer, 'image/jpeg');
272-
expect(result.metadata.confidence).toBeLessThan(0.5);
273-
expect(result.metadata.missingInformation).toContain(
274-
'Low confidence in the analysis. Please verify results or try a clearer image.',
275-
);
276-
});
277-
278-
it('should handle partially visible information in images', async () => {
279-
const partialInfo = {
280-
keyMedicalTerms: [{ term: 'Partial term', definition: 'Only partially visible' }],
281-
labValues: [],
282-
diagnoses: [],
283-
metadata: {
284-
isMedicalReport: true,
285-
confidence: 0.7,
286-
missingInformation: ['Partial document visible', 'Some values not readable'],
287-
},
288-
};
289-
290-
// Mock parseBedrockResponse method to return our expected data
291-
vi.spyOn(service as any, 'parseBedrockResponse').mockReturnValueOnce(partialInfo);
292-
293-
const result = await service.extractMedicalInfo(mockImageBuffer, 'image/jpeg');
294-
295-
expect(result.metadata.missingInformation).toContain('Partial document visible');
296-
expect(result.keyMedicalTerms[0].term).toBe('Partial term');
297-
});
298-
299-
it('should reject unsupported file types', async () => {
300-
await expect(service.extractMedicalInfo(mockImageBuffer, 'image/gif')).rejects.toThrow(
301-
'Only JPEG, PNG, and HEIC/HEIF images are allowed',
302-
);
303-
});
304-
305-
it('should accept JPEG images with EXIF data from mobile phones', async () => {
306-
const mockMedicalInfo = {
307-
keyMedicalTerms: [
308-
{ term: 'BUN', definition: 'Blood Urea Nitrogen - kidney function test' },
309-
],
310-
labValues: [
311-
{ name: 'BUN', value: '15', unit: 'mg/dL', normalRange: '7-20', isAbnormal: false },
312-
],
313-
diagnoses: [
314-
{
315-
condition: 'Normal Kidney Function',
316-
details: 'BUN within normal limits',
317-
recommendations: 'Routine follow-up',
318-
},
319-
],
320-
metadata: {
321-
isMedicalReport: true,
322-
confidence: 0.95,
323-
missingInformation: [],
324-
},
325-
};
326-
327-
// Mock parseBedrockResponse method to return our expected data
328-
vi.spyOn(service as any, 'parseBedrockResponse').mockReturnValueOnce(mockMedicalInfo);
329-
330-
const result = await service.extractMedicalInfo(mockImageBuffer, 'image/jpeg');
331-
332-
expect(result).toHaveProperty('keyMedicalTerms');
333-
expect(result.keyMedicalTerms[0].term).toBe('BUN');
334-
expect(result.metadata.isMedicalReport).toBe(true);
335-
});
336-
337-
it('should accept HEIC/HEIF images from mobile phones', async () => {
338-
const mockMedicalInfo = {
339-
keyMedicalTerms: [{ term: 'Creatinine', definition: 'Waste product filtered by kidneys' }],
340-
labValues: [
341-
{
342-
name: 'Creatinine',
343-
value: '0.9',
344-
unit: 'mg/dL',
345-
normalRange: '0.7-1.3',
346-
isAbnormal: false,
347-
},
348-
],
349-
diagnoses: [
350-
{
351-
condition: 'Normal Kidney Function',
352-
details: 'Creatinine within normal limits',
353-
recommendations: 'Routine follow-up',
354-
},
355-
],
356-
metadata: {
357-
isMedicalReport: true,
358-
confidence: 0.93,
359-
missingInformation: [],
360-
},
361-
};
362-
363-
// Mock parseBedrockResponse method to return our expected data
364-
vi.spyOn(service as any, 'parseBedrockResponse').mockReturnValueOnce(mockMedicalInfo);
365-
366-
const result = await service.extractMedicalInfo(mockImageBuffer, 'image/heic');
367-
368-
expect(result).toHaveProperty('keyMedicalTerms');
369-
expect(result.keyMedicalTerms[0].term).toBe('Creatinine');
370-
expect(result.metadata.isMedicalReport).toBe(true);
371-
});
372-
373-
it('should handle errors when image processing fails', async () => {
374-
const error = new Error('Image processing failed');
375-
vi.spyOn(service as any, 'invokeBedrock').mockRejectedValueOnce(error);
376-
377-
await expect(service.extractMedicalInfo(mockImageBuffer, 'image/jpeg')).rejects.toThrow(
378-
/Failed to extract medical information from image: Image processing failed/,
379-
);
380-
});
381-
382-
it('should handle invalid response format', async () => {
383-
vi.spyOn(service as any, 'parseBedrockResponse').mockImplementationOnce(() => {
384-
throw new Error('Invalid response format');
385-
});
386-
387-
await expect(service.extractMedicalInfo(mockImageBuffer, 'image/jpeg')).rejects.toThrow(
388-
/Failed to extract medical information from image: Invalid response format/,
389-
);
390-
});
391-
});
39265
});

0 commit comments

Comments
 (0)