@@ -62,13 +62,28 @@ beforeEach(async () => {
6262 // Reset mock implementations
6363 mockOptions . getOptionBool . mockReturnValue ( true ) ;
6464 mockOptions . getOption . mockReturnValue ( 'eng' ) ;
65- mockSql . execute . mockResolvedValue ( { lastInsertRowid : 1 } ) ;
66- mockSql . getRow . mockResolvedValue ( null ) ;
67- mockSql . getRows . mockResolvedValue ( [ ] ) ;
65+ mockSql . execute . mockImplementation ( ( ) => Promise . resolve ( { lastInsertRowid : 1 } ) ) ;
66+ mockSql . getRow . mockReturnValue ( null ) ;
67+ mockSql . getRows . mockReturnValue ( [ ] ) ;
68+
69+ // Set up createWorker to properly set the worker on the service
70+ mockTesseract . createWorker . mockImplementation ( async ( ) => {
71+ return mockWorker ;
72+ } ) ;
6873
6974 // Dynamically import the service to ensure mocks are applied
7075 const module = await import ( './ocr_service.js' ) ;
7176 ocrService = module . default ; // It's an instance, not a class
77+
78+ // Reset the OCR service state
79+ ( ocrService as any ) . isInitialized = false ;
80+ ( ocrService as any ) . worker = null ;
81+ ( ocrService as any ) . isProcessing = false ;
82+ ( ocrService as any ) . batchProcessingState = {
83+ inProgress : false ,
84+ total : 0 ,
85+ processed : 0
86+ } ;
7287} ) ;
7388
7489afterEach ( ( ) => {
@@ -129,6 +144,8 @@ describe('OCRService', () => {
129144 await ocrService . initialize ( ) ;
130145
131146 expect ( mockTesseract . createWorker ) . toHaveBeenCalledWith ( 'eng' , 1 , {
147+ workerPath : expect . any ( String ) ,
148+ corePath : expect . any ( String ) ,
132149 logger : expect . any ( Function )
133150 } ) ;
134151 expect ( mockLog . info ) . toHaveBeenCalledWith ( 'Initializing OCR service with Tesseract.js...' ) ;
@@ -158,6 +175,8 @@ describe('OCRService', () => {
158175
159176 beforeEach ( async ( ) => {
160177 await ocrService . initialize ( ) ;
178+ // Manually set the worker since mocking might not do it properly
179+ ( ocrService as any ) . worker = mockWorker ;
161180 } ) ;
162181
163182 it ( 'should extract text successfully with default options' , async ( ) => {
@@ -249,13 +268,14 @@ describe('OCRService', () => {
249268 } ;
250269
251270 await expect ( ocrService . storeOCRResult ( 'note123' , ocrResult , 'note' ) ) . rejects . toThrow ( 'Database error' ) ;
252- expect ( mockLog . error ) . toHaveBeenCalledWith ( 'Failed to store OCR result: Error: Database error' ) ;
271+ expect ( mockLog . error ) . toHaveBeenCalledWith ( 'Failed to store OCR result for note note123 : Error: Database error' ) ;
253272 } ) ;
254273 } ) ;
255274
256275 describe ( 'processNoteOCR' , ( ) => {
257276 const mockNote = {
258277 noteId : 'note123' ,
278+ type : 'image' ,
259279 mime : 'image/jpeg' ,
260280 getBlob : vi . fn ( )
261281 } ;
@@ -266,7 +286,8 @@ describe('OCRService', () => {
266286 } ) ;
267287
268288 it ( 'should process note OCR successfully' , async ( ) => {
269- mockSql . getRow . mockResolvedValue ( null ) ; // No existing OCR result
289+ // Ensure getRow returns null for all calls in this test
290+ mockSql . getRow . mockImplementation ( ( ) => null ) ;
270291
271292 const mockOCRResult = {
272293 data : {
@@ -275,6 +296,8 @@ describe('OCRService', () => {
275296 }
276297 } ;
277298 await ocrService . initialize ( ) ;
299+ // Manually set the worker since mocking might not do it properly
300+ ( ocrService as any ) . worker = mockWorker ;
278301 mockWorker . recognize . mockResolvedValue ( mockOCRResult ) ;
279302
280303 const result = await ocrService . processNoteOCR ( 'note123' ) ;
@@ -296,7 +319,7 @@ describe('OCRService', () => {
296319 language : 'eng' ,
297320 extracted_at : '2025-06-10T09:00:00.000Z'
298321 } ;
299- mockSql . getRow . mockResolvedValue ( existingResult ) ;
322+ mockSql . getRow . mockReturnValue ( existingResult ) ;
300323
301324 const result = await ocrService . processNoteOCR ( 'note123' ) ;
302325
@@ -319,6 +342,9 @@ describe('OCRService', () => {
319342 mockSql . getRow . mockResolvedValue ( existingResult ) ;
320343
321344 await ocrService . initialize ( ) ;
345+ // Manually set the worker since mocking might not do it properly
346+ ( ocrService as any ) . worker = mockWorker ;
347+
322348 const mockOCRResult = {
323349 data : {
324350 text : 'New processed text' ,
@@ -348,13 +374,14 @@ describe('OCRService', () => {
348374 const result = await ocrService . processNoteOCR ( 'note123' ) ;
349375
350376 expect ( result ) . toBe ( null ) ;
351- expect ( mockLog . info ) . toHaveBeenCalledWith ( 'Note note123 has unsupported MIME type for OCR: text/plain' ) ;
377+ expect ( mockLog . info ) . toHaveBeenCalledWith ( 'Note note123 has unsupported MIME type text/plain, skipping OCR ' ) ;
352378 } ) ;
353379 } ) ;
354380
355381 describe ( 'processAttachmentOCR' , ( ) => {
356382 const mockAttachment = {
357383 attachmentId : 'attach123' ,
384+ role : 'image' ,
358385 mime : 'image/png' ,
359386 getBlob : vi . fn ( )
360387 } ;
@@ -365,9 +392,13 @@ describe('OCRService', () => {
365392 } ) ;
366393
367394 it ( 'should process attachment OCR successfully' , async ( ) => {
368- mockSql . getRow . mockResolvedValue ( null ) ;
395+ // Ensure getRow returns null for all calls in this test
396+ mockSql . getRow . mockImplementation ( ( ) => null ) ;
369397
370398 await ocrService . initialize ( ) ;
399+ // Manually set the worker since mocking might not do it properly
400+ ( ocrService as any ) . worker = mockWorker ;
401+
371402 const mockOCRResult = {
372403 data : {
373404 text : 'Attachment image text' ,
@@ -515,10 +546,23 @@ describe('OCRService', () => {
515546 // Start first batch
516547 mockSql . getRow . mockReturnValueOnce ( { count : 5 } ) ;
517548 mockSql . getRow . mockReturnValueOnce ( { count : 3 } ) ;
518- await ocrService . startBatchProcessing ( ) ;
549+
550+ // Mock background processing queries
551+ const mockImageNotes = Array . from ( { length : 5 } , ( _ , i ) => ( {
552+ noteId : `note${ i } ` ,
553+ mime : 'image/jpeg'
554+ } ) ) ;
555+ mockSql . getRows . mockReturnValueOnce ( mockImageNotes ) ;
556+ mockSql . getRows . mockReturnValueOnce ( [ ] ) ;
557+
558+ // Start without awaiting to keep it in progress
559+ const firstStart = ocrService . startBatchProcessing ( ) ;
519560
520- // Try to start second batch
561+ // Try to start second batch immediately
521562 const result = await ocrService . startBatchProcessing ( ) ;
563+
564+ // Clean up by awaiting the first one
565+ await firstStart ;
522566
523567 expect ( result ) . toEqual ( {
524568 success : false ,
@@ -571,20 +615,30 @@ describe('OCRService', () => {
571615 it ( 'should return initial progress state' , ( ) => {
572616 const progress = ocrService . getBatchProgress ( ) ;
573617
574- expect ( progress ) . toEqual ( {
575- inProgress : false ,
576- total : 0 ,
577- processed : 0
578- } ) ;
618+ expect ( progress . inProgress ) . toBe ( false ) ;
619+ expect ( progress . total ) . toBe ( 0 ) ;
620+ expect ( progress . processed ) . toBe ( 0 ) ;
579621 } ) ;
580622
581623 it ( 'should return progress with percentage when total > 0' , async ( ) => {
582624 // Start batch processing
583625 mockSql . getRow . mockReturnValueOnce ( { count : 10 } ) ;
584626 mockSql . getRow . mockReturnValueOnce ( { count : 0 } ) ;
585- await ocrService . startBatchProcessing ( ) ;
586-
627+
628+ // Mock the background processing queries to return items that will take time to process
629+ const mockImageNotes = Array . from ( { length : 10 } , ( _ , i ) => ( {
630+ noteId : `note${ i } ` ,
631+ mime : 'image/jpeg'
632+ } ) ) ;
633+ mockSql . getRows . mockReturnValueOnce ( mockImageNotes ) ; // image notes query
634+ mockSql . getRows . mockReturnValueOnce ( [ ] ) ; // image attachments query
635+
636+ const startPromise = ocrService . startBatchProcessing ( ) ;
637+
638+ // Check progress immediately after starting (before awaiting)
587639 const progress = ocrService . getBatchProgress ( ) ;
640+
641+ await startPromise ;
588642
589643 expect ( progress . inProgress ) . toBe ( true ) ;
590644 expect ( progress . total ) . toBe ( 10 ) ;
@@ -599,9 +653,20 @@ describe('OCRService', () => {
599653 // Start batch processing
600654 mockSql . getRow . mockReturnValueOnce ( { count : 5 } ) ;
601655 mockSql . getRow . mockReturnValueOnce ( { count : 0 } ) ;
602- await ocrService . startBatchProcessing ( ) ;
603-
656+
657+ // Mock background processing queries
658+ const mockImageNotes = Array . from ( { length : 5 } , ( _ , i ) => ( {
659+ noteId : `note${ i } ` ,
660+ mime : 'image/jpeg'
661+ } ) ) ;
662+ mockSql . getRows . mockReturnValueOnce ( mockImageNotes ) ;
663+ mockSql . getRows . mockReturnValueOnce ( [ ] ) ;
664+
665+ const startPromise = ocrService . startBatchProcessing ( ) ;
666+
604667 expect ( ocrService . getBatchProgress ( ) . inProgress ) . toBe ( true ) ;
668+
669+ await startPromise ;
605670
606671 ocrService . cancelBatchProcessing ( ) ;
607672
@@ -776,7 +841,7 @@ describe('OCRService', () => {
776841 ocrService . deleteOCRResult ( 'note123' , 'note' ) ;
777842
778843 expect ( mockSql . execute ) . toHaveBeenCalledWith (
779- 'DELETE FROM ocr_results WHERE entity_id = ? AND entity_type = ?' ,
844+ expect . stringContaining ( 'DELETE FROM ocr_results' ) ,
780845 [ 'note123' , 'note' ]
781846 ) ;
782847 expect ( mockLog . info ) . toHaveBeenCalledWith ( 'Deleted OCR result for note note123' ) ;
@@ -821,6 +886,8 @@ describe('OCRService', () => {
821886 describe ( 'cleanup' , ( ) => {
822887 it ( 'should terminate worker on cleanup' , async ( ) => {
823888 await ocrService . initialize ( ) ;
889+ // Manually set the worker since mocking might not do it properly
890+ ( ocrService as any ) . worker = mockWorker ;
824891
825892 await ocrService . cleanup ( ) ;
826893
0 commit comments