33 *
44 * Tests protocol parsers (modern 276-byte, legacy 140-byte), model detection,
55 * status mapping, multi-port discovery, timeout handling, deduplication,
6- * and backward compatibility with legacy API .
6+ * and monitor/event behavior .
77 */
88import { afterAll , beforeAll , describe , expect , it , vi } from 'vitest' ;
9- import * as dgram from 'node:dgram' ;
9+ import type * as dgram from 'node:dgram' ;
1010import { EventEmitter } from 'node:events' ;
1111import { PrinterDiscovery } from './PrinterDiscovery' ;
1212import {
@@ -16,7 +16,7 @@ import {
1616 type DiscoveredPrinter ,
1717 type DiscoveryOptions ,
1818} from '../models/PrinterDiscovery' ;
19- import { InvalidResponseError , SocketCreationError } from './network/DiscoveryErrors' ;
19+ import { InvalidResponseError } from './network/DiscoveryErrors' ;
2020
2121// Suppress logs during tests
2222const originalConsole = { ...console } ;
@@ -444,7 +444,7 @@ describe('PrinterDiscovery', () => {
444444
445445 protected async createDiscoverySocket ( ) : Promise < dgram . Socket > {
446446 const mockSocket = new EventEmitter ( ) as dgram . Socket ;
447- mockSocket . bind = vi . fn ( ( port , callback ) => {
447+ mockSocket . bind = vi . fn ( ( _port , callback ) => {
448448 if ( callback ) callback ( ) ;
449449 } ) ;
450450 mockSocket . setBroadcast = vi . fn ( ) ;
@@ -489,7 +489,7 @@ describe('PrinterDiscovery', () => {
489489
490490 protected async createDiscoverySocket ( ) : Promise < dgram . Socket > {
491491 const mockSocket = new EventEmitter ( ) as dgram . Socket ;
492- mockSocket . bind = vi . fn ( ( port , callback ) => {
492+ mockSocket . bind = vi . fn ( ( _port , callback ) => {
493493 if ( callback ) callback ( ) ;
494494 } ) ;
495495 mockSocket . setBroadcast = vi . fn ( ) ;
@@ -540,7 +540,7 @@ describe('PrinterDiscovery', () => {
540540 class TestPrinterDiscovery extends PrinterDiscovery {
541541 protected async createDiscoverySocket ( ) : Promise < dgram . Socket > {
542542 const mockSocket = new EventEmitter ( ) as dgram . Socket ;
543- mockSocket . bind = vi . fn ( ( port , callback ) => {
543+ mockSocket . bind = vi . fn ( ( _port , callback ) => {
544544 if ( callback ) callback ( ) ;
545545 } ) ;
546546 mockSocket . setBroadcast = vi . fn ( ) ;
@@ -567,7 +567,7 @@ describe('PrinterDiscovery', () => {
567567
568568 protected async createDiscoverySocket ( ) : Promise < dgram . Socket > {
569569 const mockSocket = new EventEmitter ( ) as dgram . Socket ;
570- mockSocket . bind = vi . fn ( ( port , callback ) => {
570+ mockSocket . bind = vi . fn ( ( _port , callback ) => {
571571 if ( callback ) callback ( ) ;
572572 } ) ;
573573 mockSocket . setBroadcast = vi . fn ( ) ;
@@ -612,6 +612,96 @@ describe('PrinterDiscovery', () => {
612612
613613 await endPromise ;
614614 } ) ;
615+
616+ it ( 'should emit end when stopped manually' , async ( ) => {
617+ class TestPrinterDiscovery extends PrinterDiscovery {
618+ protected async createDiscoverySocket ( ) : Promise < dgram . Socket > {
619+ const mockSocket = new EventEmitter ( ) as dgram . Socket ;
620+ mockSocket . bind = vi . fn ( ( _port , callback ) => {
621+ if ( callback ) callback ( ) ;
622+ } ) ;
623+ mockSocket . setBroadcast = vi . fn ( ) ;
624+ mockSocket . send = vi . fn ( ) ;
625+ mockSocket . close = vi . fn ( ) ;
626+ mockSocket . addMembership = vi . fn ( ) ;
627+ return mockSocket ;
628+ }
629+ }
630+
631+ const discovery = new TestPrinterDiscovery ( ) ;
632+ const monitor = discovery . monitor ( { timeout : 1000 , maxRetries : 10 } ) ;
633+
634+ const endPromise = new Promise < void > ( ( resolve , reject ) => {
635+ const timeoutHandle = setTimeout ( ( ) => {
636+ reject ( new Error ( 'Timed out waiting for end event' ) ) ;
637+ } , 500 ) ;
638+
639+ monitor . on ( 'error' , reject ) ;
640+ monitor . on ( 'end' , ( ) => {
641+ clearTimeout ( timeoutHandle ) ;
642+ resolve ( ) ;
643+ } ) ;
644+ } ) ;
645+
646+ setTimeout ( ( ) => {
647+ monitor . stop ( ) ;
648+ } , 50 ) ;
649+
650+ await endPromise ;
651+ } ) ;
652+
653+ it ( 'should honor idleTimeout after first discovery response' , async ( ) => {
654+ class TestPrinterDiscovery extends PrinterDiscovery {
655+ public mockSocket : dgram . Socket | null = null ;
656+
657+ protected async createDiscoverySocket ( ) : Promise < dgram . Socket > {
658+ const mockSocket = new EventEmitter ( ) as dgram . Socket ;
659+ mockSocket . bind = vi . fn ( ( _port , callback ) => {
660+ if ( callback ) callback ( ) ;
661+ } ) ;
662+ mockSocket . setBroadcast = vi . fn ( ) ;
663+ mockSocket . send = vi . fn ( ) ;
664+ mockSocket . close = vi . fn ( ) ;
665+ mockSocket . addMembership = vi . fn ( ) ;
666+ this . mockSocket = mockSocket ;
667+ return mockSocket ;
668+ }
669+ }
670+
671+ const discovery = new TestPrinterDiscovery ( ) ;
672+ const start = Date . now ( ) ;
673+ const monitor = discovery . monitor ( { timeout : 1000 , idleTimeout : 100 , maxRetries : 10 } ) ;
674+
675+ const endPromise = new Promise < void > ( ( resolve , reject ) => {
676+ const timeoutHandle = setTimeout ( ( ) => {
677+ reject ( new Error ( 'Timed out waiting for idleTimeout end event' ) ) ;
678+ } , 1000 ) ;
679+
680+ monitor . on ( 'error' , reject ) ;
681+ monitor . on ( 'end' , ( ) => {
682+ clearTimeout ( timeoutHandle ) ;
683+ resolve ( ) ;
684+ } ) ;
685+ } ) ;
686+
687+ setTimeout ( ( ) => {
688+ const mockSocket = discovery . mockSocket ;
689+ if ( mockSocket ) {
690+ const buffer = createModernBuffer ( { name : 'Idle Timeout Test' } ) ;
691+ mockSocket . emit ( 'message' , buffer , {
692+ address : '192.168.1.160' ,
693+ port : 8899 ,
694+ family : 'IPv4' ,
695+ size : 276 ,
696+ } ) ;
697+ }
698+ } , 50 ) ;
699+
700+ await endPromise ;
701+ const elapsedMs = Date . now ( ) - start ;
702+
703+ expect ( elapsedMs ) . toBeLessThan ( 500 ) ;
704+ } ) ;
615705 } ) ;
616706
617707 describe ( 'Broadcast Address Calculation' , ( ) => {
@@ -637,15 +727,18 @@ describe('PrinterDiscovery', () => {
637727describe ( 'DiscoveryOptions' , ( ) => {
638728 // Test discovery class with mocked socket
639729 class TestPrinterDiscovery extends PrinterDiscovery {
730+ public mockSocket : dgram . Socket | null = null ;
731+
640732 protected async createDiscoverySocket ( ) : Promise < dgram . Socket > {
641733 const mockSocket = new EventEmitter ( ) as dgram . Socket ;
642- mockSocket . bind = vi . fn ( ( port , callback ) => {
734+ mockSocket . bind = vi . fn ( ( _port , callback ) => {
643735 if ( callback ) callback ( ) ;
644736 } ) ;
645737 mockSocket . setBroadcast = vi . fn ( ) ;
646738 mockSocket . send = vi . fn ( ) ;
647739 mockSocket . close = vi . fn ( ) ;
648740 mockSocket . addMembership = vi . fn ( ) ;
741+ this . mockSocket = mockSocket ;
649742 return mockSocket ;
650743 }
651744 }
@@ -692,4 +785,22 @@ describe('DiscoveryOptions', () => {
692785 const printers = await discovery . discover ( options ) ;
693786 expect ( printers ) . toHaveLength ( 0 ) ;
694787 } ) ;
788+
789+ it ( 'should not send packets when multicast and broadcast are both disabled' , async ( ) => {
790+ const discovery = new TestPrinterDiscovery ( ) ;
791+
792+ const options : DiscoveryOptions = {
793+ timeout : 50 ,
794+ idleTimeout : 25 ,
795+ maxRetries : 1 ,
796+ useMulticast : false ,
797+ useBroadcast : false ,
798+ ports : [ 8899 , 19000 , 48899 ] ,
799+ } ;
800+
801+ const printers = await discovery . discover ( options ) ;
802+ expect ( printers ) . toHaveLength ( 0 ) ;
803+ expect ( discovery . mockSocket ) . not . toBeNull ( ) ;
804+ expect ( ( discovery . mockSocket ?. send as ReturnType < typeof vi . fn > ) ) . not . toHaveBeenCalled ( ) ;
805+ } ) ;
695806} ) ;
0 commit comments