11import { ConfigService } from '@nestjs/config' ;
2- import { BadRequestException } from '@nestjs/common' ;
32import { AwsBedrockService } from './aws-bedrock.service' ;
43import { 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
226vi . 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- / F a i l e d t o e x t r a c t m e d i c a l i n f o r m a t i o n f r o m i m a g e : I m a g e p r o c e s s i n g f a i l e d / ,
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- / F a i l e d t o e x t r a c t m e d i c a l i n f o r m a t i o n f r o m i m a g e : I n v a l i d r e s p o n s e f o r m a t / ,
389- ) ;
390- } ) ;
391- } ) ;
39265} ) ;
0 commit comments