@@ -33,7 +33,11 @@ vi.mock('@aws-sdk/client-bedrock-runtime', () => {
3333// Mock validateFileSecurely to bypass file validation in tests
3434vi . mock ( '../utils/security.utils' , ( ) => {
3535 return {
36- validateFileSecurely : vi . fn ( ) ,
36+ validateFileSecurely : vi . fn ( ) . mockImplementation ( ( buffer : Buffer , fileType : string ) => {
37+ if ( ! [ 'image/jpeg' , 'image/png' ] . includes ( fileType ) ) {
38+ throw new BadRequestException ( 'Only JPEG and PNG images are allowed' ) ;
39+ }
40+ } ) ,
3741 sanitizeMedicalData : vi . fn ( data => data ) ,
3842 RateLimiter : vi . fn ( ) . mockImplementation ( ( ) => ( {
3943 tryRequest : vi . fn ( ) . mockReturnValue ( true ) ,
@@ -63,7 +67,7 @@ describe('AwsBedrockService', () => {
6367 'aws.region' : 'us-east-1' ,
6468 'aws.aws.accessKeyId' : 'test-access-key' ,
6569 'aws.aws.secretAccessKey' : 'test-secret-key' ,
66- 'bedrock.model' : 'anthropic.claude-v2 ' ,
70+ 'bedrock.model' : 'anthropic.claude-3-7-sonnet-20250219-v1:0 ' ,
6771 'bedrock.maxTokens' : 2048 ,
6872 } ;
6973
@@ -99,30 +103,32 @@ describe('AwsBedrockService', () => {
99103 } ) ;
100104
101105 it ( 'should initialize with test environment values' , ( ) => {
102- expect ( service [ 'defaultModel' ] ) . toBe ( 'anthropic.claude-v2 ' ) ;
106+ expect ( service [ 'defaultModel' ] ) . toBe ( 'anthropic.claude-3-7-sonnet-20250219-v1:0 ' ) ;
103107 expect ( service [ 'defaultMaxTokens' ] ) . toBe ( 1000 ) ;
104108 } ) ;
105109 } ) ;
106110
107111 describe ( 'extractMedicalInfo' , ( ) => {
108- const mockFileBuffer = Buffer . from ( 'test file content' ) ;
109- const mockFileType = 'application/pdf' ;
112+ const mockImageBuffer = Buffer . from ( 'test image content' ) ;
113+ const mockImageTypes = [ 'image/jpeg' , 'image/png' ] ;
110114 const mockMedicalInfo = {
111- keyMedicalTerms : [ { term : 'Hypertension' , definition : 'High blood pressure' } ] ,
115+ keyMedicalTerms : [
116+ { term : 'Hemoglobin' , definition : 'Protein in red blood cells that carries oxygen' } ,
117+ ] ,
112118 labValues : [
113119 {
114- name : 'Blood Pressure ' ,
115- value : '140/90 ' ,
116- unit : 'mmHg ' ,
117- normalRange : '120/80 ' ,
118- isAbnormal : true ,
120+ name : 'Hemoglobin ' ,
121+ value : '14.5 ' ,
122+ unit : 'g/dL ' ,
123+ normalRange : '12.0-15.5 ' ,
124+ isAbnormal : false ,
119125 } ,
120126 ] ,
121127 diagnoses : [
122128 {
123- condition : 'Hypertension ' ,
124- details : 'Elevated blood pressure ' ,
125- recommendations : 'Lifestyle changes and monitoring' ,
129+ condition : 'Normal Blood Count ' ,
130+ details : 'All values within normal range ' ,
131+ recommendations : 'Continue routine monitoring' ,
126132 } ,
127133 ] ,
128134 metadata : {
@@ -145,45 +151,45 @@ ${JSON.stringify(mockMedicalInfo, null, 2)}
145151 } ;
146152
147153 beforeEach ( ( ) => {
148- // Mock the Bedrock client response
149154 mockBedrockClient . send . mockResolvedValue ( mockResponse ) ;
150155 } ) ;
151156
152- it ( 'should successfully extract medical information from a file' , async ( ) => {
153- const result = await service . extractMedicalInfo ( mockFileBuffer , mockFileType ) ;
154-
155- // Verify the result structure
156- expect ( result ) . toHaveProperty ( 'keyMedicalTerms' ) ;
157- expect ( result ) . toHaveProperty ( 'labValues' ) ;
158- expect ( result ) . toHaveProperty ( 'diagnoses' ) ;
159- expect ( result ) . toHaveProperty ( 'metadata' ) ;
160-
161- // Verify the command was called with correct parameters
162- expect ( InvokeModelCommand ) . toHaveBeenCalledWith (
163- expect . objectContaining ( {
164- modelId : 'anthropic.claude-v2' ,
165- contentType : 'application/json' ,
166- accept : 'application/json' ,
167- } ) ,
168- ) ;
169-
170- // Verify the content of the extracted information
171- expect ( result . keyMedicalTerms [ 0 ] . term ) . toBe ( 'Hypertension' ) ;
172- expect ( result . labValues [ 0 ] . name ) . toBe ( 'Blood Pressure' ) ;
173- expect ( result . diagnoses [ 0 ] . condition ) . toBe ( 'Hypertension' ) ;
174- expect ( result . metadata . isMedicalReport ) . toBe ( true ) ;
175- expect ( result . metadata . confidence ) . toBe ( 0.95 ) ;
176- } ) ;
157+ it . each ( mockImageTypes ) (
158+ 'should successfully extract medical information from %s' ,
159+ async imageType => {
160+ const result = await service . extractMedicalInfo ( mockImageBuffer , imageType ) ;
161+
162+ expect ( result ) . toHaveProperty ( 'keyMedicalTerms' ) ;
163+ expect ( result ) . toHaveProperty ( 'labValues' ) ;
164+ expect ( result ) . toHaveProperty ( 'diagnoses' ) ;
165+ expect ( result ) . toHaveProperty ( 'metadata' ) ;
166+
167+ expect ( InvokeModelCommand ) . toHaveBeenCalledWith (
168+ expect . objectContaining ( {
169+ modelId : expect . any ( String ) ,
170+ contentType : 'application/json' ,
171+ accept : 'application/json' ,
172+ body : expect . stringContaining ( imageType ) ,
173+ } ) ,
174+ ) ;
175+
176+ expect ( result . keyMedicalTerms [ 0 ] . term ) . toBe ( 'Hemoglobin' ) ;
177+ expect ( result . labValues [ 0 ] . name ) . toBe ( 'Hemoglobin' ) ;
178+ expect ( result . diagnoses [ 0 ] . condition ) . toBe ( 'Normal Blood Count' ) ;
179+ expect ( result . metadata . isMedicalReport ) . toBe ( true ) ;
180+ expect ( result . metadata . confidence ) . toBe ( 0.95 ) ;
181+ } ,
182+ ) ;
177183
178- it ( 'should reject non-medical reports ' , async ( ) => {
184+ it ( 'should reject non-medical images ' , async ( ) => {
179185 const nonMedicalInfo = {
180186 keyMedicalTerms : [ ] ,
181187 labValues : [ ] ,
182188 diagnoses : [ ] ,
183189 metadata : {
184190 isMedicalReport : false ,
185191 confidence : 0.1 ,
186- missingInformation : [ 'Not a medical document ' ] ,
192+ missingInformation : [ 'Not a medical image ' ] ,
187193 } ,
188194 } ;
189195
@@ -199,85 +205,93 @@ ${JSON.stringify(nonMedicalInfo, null, 2)}
199205 body : Buffer . from ( JSON . stringify ( nonMedicalResponse ) ) as any ,
200206 } ) ;
201207
202- await expect ( service . extractMedicalInfo ( mockFileBuffer , mockFileType ) ) . rejects . toThrow (
208+ await expect ( service . extractMedicalInfo ( mockImageBuffer , 'image/jpeg' ) ) . rejects . toThrow (
203209 BadRequestException ,
204210 ) ;
205211
206- await expect ( service . extractMedicalInfo ( mockFileBuffer , mockFileType ) ) . rejects . toThrow (
207- 'The provided document does not appear to be a medical report .' ,
212+ await expect ( service . extractMedicalInfo ( mockImageBuffer , 'image/jpeg' ) ) . rejects . toThrow (
213+ 'The provided image does not appear to be a medical document .' ,
208214 ) ;
209215 } ) ;
210216
211- it ( 'should handle low confidence medical reports ' , async ( ) => {
212- const lowConfidenceInfo = {
217+ it ( 'should handle low quality or unclear images ' , async ( ) => {
218+ const lowQualityInfo = {
213219 keyMedicalTerms : [ ] ,
214220 labValues : [ ] ,
215221 diagnoses : [ ] ,
216222 metadata : {
217223 isMedicalReport : true ,
218224 confidence : 0.5 ,
219- missingInformation : [ 'Unclear handwriting ' , 'Missing sections ' ] ,
225+ missingInformation : [ 'Image too blurry ' , 'Text not readable ' ] ,
220226 } ,
221227 } ;
222228
223- const lowConfidenceResponse = {
229+ const lowQualityResponse = {
224230 content : `Analysis results:
225231\`\`\`json
226- ${ JSON . stringify ( lowConfidenceInfo , null , 2 ) }
232+ ${ JSON . stringify ( lowQualityInfo , null , 2 ) }
227233\`\`\`` ,
228234 } ;
229235
230236 mockBedrockClient . send . mockResolvedValue ( {
231237 $metadata : { } ,
232- body : Buffer . from ( JSON . stringify ( lowConfidenceResponse ) ) as any ,
238+ body : Buffer . from ( JSON . stringify ( lowQualityResponse ) ) as any ,
233239 } ) ;
234240
235- await expect ( service . extractMedicalInfo ( mockFileBuffer , mockFileType ) ) . rejects . toThrow (
241+ await expect ( service . extractMedicalInfo ( mockImageBuffer , 'image/jpeg' ) ) . rejects . toThrow (
236242 BadRequestException ,
237243 ) ;
238244
239- await expect ( service . extractMedicalInfo ( mockFileBuffer , mockFileType ) ) . rejects . toThrow (
240- 'Low confidence in medical report analysis' ,
245+ await expect ( service . extractMedicalInfo ( mockImageBuffer , 'image/jpeg' ) ) . rejects . toThrow (
246+ 'Low confidence in medical image analysis' ,
241247 ) ;
242248 } ) ;
243249
244- it ( 'should handle missing information in medical reports' , async ( ) => {
245- const missingInfoData = {
246- keyMedicalTerms : [ { term : 'Hypertension' , definition : 'High blood pressure' } ] ,
250+ it ( 'should handle partially visible information in images' , async ( ) => {
251+ const partialInfo = {
252+ keyMedicalTerms : [
253+ { term : 'Hemoglobin' , definition : 'Protein in red blood cells that carries oxygen' } ,
254+ ] ,
247255 labValues : [ ] ,
248256 diagnoses : [ ] ,
249257 metadata : {
250258 isMedicalReport : true ,
251259 confidence : 0.8 ,
252- missingInformation : [ 'Lab values ' , 'Recommendations ' ] ,
260+ missingInformation : [ 'Bottom portion of image cut off ' , 'Some values not visible ' ] ,
253261 } ,
254262 } ;
255263
256- const missingInfoResponse = {
264+ const partialResponse = {
257265 content : `Here's what I found:
258266\`\`\`json
259- ${ JSON . stringify ( missingInfoData , null , 2 ) }
267+ ${ JSON . stringify ( partialInfo , null , 2 ) }
260268\`\`\`` ,
261269 } ;
262270
263271 mockBedrockClient . send . mockResolvedValue ( {
264272 $metadata : { } ,
265- body : Buffer . from ( JSON . stringify ( missingInfoResponse ) ) as any ,
273+ body : Buffer . from ( JSON . stringify ( partialResponse ) ) as any ,
266274 } ) ;
267275
268- const result = await service . extractMedicalInfo ( mockFileBuffer , mockFileType ) ;
276+ const result = await service . extractMedicalInfo ( mockImageBuffer , 'image/jpeg' ) ;
269277
270- expect ( result . metadata . missingInformation ) . toContain ( 'Lab values ' ) ;
271- expect ( result . metadata . missingInformation ) . toContain ( 'Recommendations ' ) ;
278+ expect ( result . metadata . missingInformation ) . toContain ( 'Bottom portion of image cut off ' ) ;
279+ expect ( result . metadata . missingInformation ) . toContain ( 'Some values not visible ' ) ;
272280 expect ( result . metadata . confidence ) . toBe ( 0.8 ) ;
273281 } ) ;
274282
275- it ( 'should handle errors when file processing fails' , async ( ) => {
276- const error = new Error ( 'Processing failed' ) ;
283+ it ( 'should reject unsupported file types' , async ( ) => {
284+ await expect ( service . extractMedicalInfo ( mockImageBuffer , 'application/pdf' ) ) . rejects . toThrow (
285+ 'Only JPEG and PNG images are allowed' ,
286+ ) ;
287+ } ) ;
288+
289+ it ( 'should handle errors when image processing fails' , async ( ) => {
290+ const error = new Error ( 'Image processing failed' ) ;
277291 mockBedrockClient . send . mockRejectedValue ( error ) ;
278292
279- await expect ( service . extractMedicalInfo ( mockFileBuffer , mockFileType ) ) . rejects . toThrow (
280- 'Failed to extract medical information: Processing failed' ,
293+ await expect ( service . extractMedicalInfo ( mockImageBuffer , 'image/jpeg' ) ) . rejects . toThrow (
294+ 'Failed to extract medical information from image: Image processing failed' ,
281295 ) ;
282296 } ) ;
283297
@@ -288,21 +302,9 @@ ${JSON.stringify(missingInfoData, null, 2)}
288302 } ;
289303 mockBedrockClient . send . mockResolvedValue ( invalidResponse ) ;
290304
291- await expect ( service . extractMedicalInfo ( mockFileBuffer , mockFileType ) ) . rejects . toThrow (
305+ await expect ( service . extractMedicalInfo ( mockImageBuffer , 'image/jpeg' ) ) . rejects . toThrow (
292306 'Failed to extract JSON from response' ,
293307 ) ;
294308 } ) ;
295-
296- it ( 'should handle different file types' , async ( ) => {
297- const imageFileType = 'image/jpeg' ;
298- await service . extractMedicalInfo ( mockFileBuffer , imageFileType ) ;
299-
300- // Verify the command was called with the correct file type
301- expect ( InvokeModelCommand ) . toHaveBeenCalledWith (
302- expect . objectContaining ( {
303- body : expect . stringContaining ( imageFileType ) ,
304- } ) ,
305- ) ;
306- } ) ;
307309 } ) ;
308310} ) ;
0 commit comments