@@ -95,6 +95,43 @@ describe('joinDevice', () => {
9595 expect ( devices [ 0 ] ! . entropyBits ) . toBe ( 11 )
9696 } )
9797
98+ it ( 'reconoce la revisita por libFpHash aunque el hash propio cambie (incógnito)' , async ( ) => {
99+ const { id } = await createRoom ( )
100+ const first = await joinDevice ( { roomId : id , deviceHash : 'hashNormal' , ownFp : null , libFpHash : 'visitor-abc' , entropyBits : 30 } )
101+ expect ( first ) . toEqual ( { label : 1 , revisits : 0 , isReturning : false } )
102+
103+ // Segunda entrada en incógnito: canvas/audio bailan → hash propio distinto,
104+ // pero FingerprintJS devuelve el mismo visitorId. Debe verse como revisita.
105+ const second = await joinDevice ( { roomId : id , deviceHash : 'hashIncognito' , ownFp : null , libFpHash : 'visitor-abc' , entropyBits : 28 } )
106+ expect ( second ) . toEqual ( { label : 1 , revisits : 1 , isReturning : true } )
107+
108+ const devices = await listDevices ( id )
109+ expect ( devices ) . toHaveLength ( 1 )
110+ } )
111+
112+ it ( 'sin libFpHash cae al match por deviceHash (FingerprintJS no cargó)' , async ( ) => {
113+ const { id } = await createRoom ( )
114+ await joinDevice ( { roomId : id , deviceHash : 'hashX' , ownFp : null , libFpHash : null , entropyBits : 15 } )
115+ const again = await joinDevice ( { roomId : id , deviceHash : 'hashX' , ownFp : null , libFpHash : null , entropyBits : 15 } )
116+ expect ( again . isReturning ) . toBe ( true )
117+ expect ( again . revisits ) . toBe ( 1 )
118+
119+ // Distinto hash y sin libFpHash → no hay forma de reconocerlo: dispositivo nuevo.
120+ const nuevo = await joinDevice ( { roomId : id , deviceHash : 'hashY' , ownFp : null , libFpHash : null , entropyBits : 15 } )
121+ expect ( nuevo ) . toEqual ( { label : 2 , revisits : 0 , isReturning : false } )
122+ } )
123+
124+ it ( 'un libFpHash null en la 2a visita no impide reconocer por deviceHash' , async ( ) => {
125+ const { id } = await createRoom ( )
126+ await joinDevice ( { roomId : id , deviceHash : 'hashSame' , ownFp : null , libFpHash : 'visitor-z' , entropyBits : 20 } )
127+ // Mismo hash propio pero esta vez FingerprintJS falló (null): igual es revisita.
128+ const again = await joinDevice ( { roomId : id , deviceHash : 'hashSame' , ownFp : null , libFpHash : null , entropyBits : 20 } )
129+ expect ( again . isReturning ) . toBe ( true )
130+ // El libFpHash previo no se pisa con null.
131+ const [ d ] = await listDevices ( id )
132+ expect ( d ! . libFpHash ) . toBe ( 'visitor-z' )
133+ } )
134+
98135 it ( 'el mismo hash en salas distintas es independiente' , async ( ) => {
99136 const r1 = await createRoom ( )
100137 const r2 = await createRoom ( )
0 commit comments