@@ -77,6 +77,9 @@ describe('WebSocketAdapter', () => {
7777 slidingWindowRateLimiter ,
7878 settingsFactory ,
7979 )
80+
81+ // Reset send history so existing tests see a clean slate
82+ client . send . resetHistory ( )
8083 } )
8184
8285 afterEach ( ( ) => {
@@ -603,4 +606,94 @@ describe('WebSocketAdapter', () => {
603606 ipv6Adapter . removeAllListeners ( )
604607 } )
605608 } )
609+
610+ describe ( 'NIP-42 authentication' , ( ) => {
611+ it ( 'sends AUTH challenge message on construction' , ( ) => {
612+ const freshClient = {
613+ on : sandbox . stub ( ) . returnsThis ( ) ,
614+ send : sandbox . stub ( ) ,
615+ close : sandbox . stub ( ) ,
616+ ping : sandbox . stub ( ) ,
617+ pong : sandbox . stub ( ) ,
618+ readyState : WebSocket . OPEN ,
619+ removeAllListeners : sandbox . stub ( ) ,
620+ }
621+ const freshAdapter = new WebSocketAdapter (
622+ freshClient as any ,
623+ request ,
624+ webSocketServer as any ,
625+ createMessageHandler ,
626+ slidingWindowRateLimiter ,
627+ settingsFactory ,
628+ )
629+
630+ expect ( freshClient . send ) . to . have . been . calledOnce
631+ const sent = JSON . parse ( freshClient . send . firstCall . args [ 0 ] )
632+ expect ( sent [ 0 ] ) . to . equal ( 'AUTH' )
633+ expect ( sent [ 1 ] ) . to . be . a ( 'string' )
634+ expect ( sent [ 1 ] . length ) . to . be . greaterThan ( 0 )
635+ freshAdapter . removeAllListeners ( )
636+ } )
637+
638+ it ( 'getChallenge returns a non-empty string' , ( ) => {
639+ const challenge = adapter . getChallenge ( )
640+ expect ( challenge ) . to . be . a ( 'string' )
641+ expect ( challenge . length ) . to . be . greaterThan ( 0 )
642+ } )
643+
644+ it ( 'getChallenge returns consistent value for the same adapter' , ( ) => {
645+ const c1 = adapter . getChallenge ( )
646+ const c2 = adapter . getChallenge ( )
647+ expect ( c1 ) . to . equal ( c2 )
648+ } )
649+
650+ it ( 'getAuthenticatedPubkeys returns empty set initially' , ( ) => {
651+ const pubkeys = adapter . getAuthenticatedPubkeys ( )
652+ expect ( pubkeys . size ) . to . equal ( 0 )
653+ } )
654+
655+ it ( 'addAuthenticatedPubkey adds a pubkey' , ( ) => {
656+ const pubkey = 'a' . repeat ( 64 )
657+ adapter . addAuthenticatedPubkey ( pubkey )
658+
659+ const pubkeys = adapter . getAuthenticatedPubkeys ( )
660+ expect ( pubkeys . size ) . to . equal ( 1 )
661+ expect ( pubkeys . has ( pubkey ) ) . to . be . true
662+ } )
663+
664+ it ( 'addAuthenticatedPubkey supports multiple pubkeys' , ( ) => {
665+ const pk1 = 'a' . repeat ( 64 )
666+ const pk2 = 'b' . repeat ( 64 )
667+ adapter . addAuthenticatedPubkey ( pk1 )
668+ adapter . addAuthenticatedPubkey ( pk2 )
669+
670+ const pubkeys = adapter . getAuthenticatedPubkeys ( )
671+ expect ( pubkeys . size ) . to . equal ( 2 )
672+ expect ( pubkeys . has ( pk1 ) ) . to . be . true
673+ expect ( pubkeys . has ( pk2 ) ) . to . be . true
674+ } )
675+
676+ it ( 'addAuthenticatedPubkey deduplicates same pubkey' , ( ) => {
677+ const pubkey = 'a' . repeat ( 64 )
678+ adapter . addAuthenticatedPubkey ( pubkey )
679+ adapter . addAuthenticatedPubkey ( pubkey )
680+
681+ const pubkeys = adapter . getAuthenticatedPubkeys ( )
682+ expect ( pubkeys . size ) . to . equal ( 1 )
683+ } )
684+
685+ it ( 'generates different challenges for different adapters' , ( ) => {
686+ const adapter2 = new WebSocketAdapter (
687+ client ,
688+ request ,
689+ webSocketServer as any ,
690+ createMessageHandler ,
691+ slidingWindowRateLimiter ,
692+ settingsFactory ,
693+ )
694+
695+ expect ( adapter . getChallenge ( ) ) . not . to . equal ( adapter2 . getChallenge ( ) )
696+ adapter2 . removeAllListeners ( )
697+ } )
698+ } )
606699} )
0 commit comments