@@ -18,6 +18,8 @@ import (
1818 "strings"
1919 "testing"
2020 "time"
21+
22+ "github.com/GoCodeAlone/rod/lib/proto"
2123)
2224
2325// --------------------------------------------------------------------------
@@ -513,6 +515,125 @@ func TestBrowserBackend_LoginSkipsWhenFresh(t *testing.T) {
513515 }
514516}
515517
518+ func TestBrowserBackend_LoginReusesWarmBrowserProfileSession (t * testing.T ) {
519+ opts := newBrowserTestOpts (t )
520+
521+ var signinHits , authHits , domainsHits int
522+ mux := http .NewServeMux ()
523+ mux .HandleFunc ("/api/domains" , func (w http.ResponseWriter , r * http.Request ) {
524+ domainsHits ++
525+ if _ , err := r .Cookie ("__uzma" ); err != nil {
526+ w .WriteHeader (http .StatusUnauthorized )
527+ _ , _ = w .Write ([]byte (`{"succeeded":false,"error_code":"login"}` ))
528+ return
529+ }
530+ w .Header ().Set ("Content-Type" , "application/json" )
531+ _ = json .NewEncoder (w ).Encode (map [string ]any {"succeeded" : true , "domains" : []map [string ]any {}})
532+ })
533+ mux .HandleFunc ("/signin" , func (w http.ResponseWriter , r * http.Request ) {
534+ signinHits ++
535+ http .SetCookie (w , & http.Cookie {Name : "__uzma" , Value : "fresh-from-signin" , Path : "/" })
536+ _ , _ = w .Write ([]byte (`<html><body>signin</body></html>` ))
537+ })
538+ mux .HandleFunc ("/signin/auth.json" , func (w http.ResponseWriter , r * http.Request ) {
539+ authHits ++
540+ w .Header ().Set ("Content-Type" , "application/json" )
541+ _ = json .NewEncoder (w ).Encode (map [string ]any {"succeeded" : true , "status" : "completed" })
542+ })
543+
544+ srv := httptest .NewServer (mux )
545+ t .Cleanup (srv .Close )
546+ seedBrowserProfileCookie (t , opts , srv .URL , "__uzma" , "warm-session" )
547+
548+ creds := Credentials {Username : "alice" , Password : "pw" }
549+ c := newBrowserClient (t , opts , srv .URL , creds )
550+ t .Cleanup (func () { _ = c .backend .(interface { Close () error }).Close () })
551+
552+ ctx , cancel := context .WithTimeout (context .Background (), 60 * time .Second )
553+ defer cancel ()
554+
555+ if err := c .Login (ctx ); err != nil {
556+ t .Fatalf ("Login with warm profile: %v" , err )
557+ }
558+ if signinHits != 0 || authHits != 0 {
559+ t .Fatalf ("warm profile should skip credential login; signinHits=%d authHits=%d" , signinHits , authHits )
560+ }
561+ if domainsHits == 0 {
562+ t .Fatal ("warm profile login did not probe /api/domains" )
563+ }
564+ c .mu .Lock ()
565+ loggedAt := c .loggedAt
566+ c .mu .Unlock ()
567+ if loggedAt .IsZero () {
568+ t .Fatal ("loggedAt not set after warm profile reuse" )
569+ }
570+ }
571+
572+ func TestBrowserBackend_LoginFallsBackWhenWarmProfileUnauthenticated (t * testing.T ) {
573+ opts := newBrowserTestOpts (t )
574+
575+ var signinHits , authHits , domainsHits int
576+ mux := http .NewServeMux ()
577+ mux .HandleFunc ("/api/domains" , func (w http.ResponseWriter , r * http.Request ) {
578+ domainsHits ++
579+ w .WriteHeader (http .StatusUnauthorized )
580+ _ , _ = w .Write ([]byte (`{"succeeded":false,"error_code":"login"}` ))
581+ })
582+ mux .HandleFunc ("/signin" , func (w http.ResponseWriter , r * http.Request ) {
583+ signinHits ++
584+ http .SetCookie (w , & http.Cookie {Name : "__uzma" , Value : "fresh-from-signin" , Path : "/" })
585+ _ , _ = w .Write ([]byte (`<html><body>signin</body></html>` ))
586+ })
587+ mux .HandleFunc ("/signin/auth.json" , func (w http.ResponseWriter , r * http.Request ) {
588+ authHits ++
589+ w .Header ().Set ("Content-Type" , "application/json" )
590+ _ = json .NewEncoder (w ).Encode (map [string ]any {"succeeded" : true , "status" : "completed" })
591+ })
592+
593+ srv := httptest .NewServer (mux )
594+ t .Cleanup (srv .Close )
595+
596+ creds := Credentials {Username : "alice" , Password : "pw" }
597+ c := newBrowserClient (t , opts , srv .URL , creds )
598+ t .Cleanup (func () { _ = c .backend .(interface { Close () error }).Close () })
599+
600+ ctx , cancel := context .WithTimeout (context .Background (), 60 * time .Second )
601+ defer cancel ()
602+
603+ if err := c .Login (ctx ); err != nil {
604+ t .Fatalf ("Login after stale warm profile probe: %v" , err )
605+ }
606+ if domainsHits == 0 {
607+ t .Fatal ("stale profile login did not probe /api/domains before fallback" )
608+ }
609+ if signinHits == 0 || authHits == 0 {
610+ t .Fatalf ("stale profile should fall back to credential login; signinHits=%d authHits=%d" , signinHits , authHits )
611+ }
612+ }
613+
614+ func seedBrowserProfileCookie (t * testing.T , opts BrowserOptions , baseURL , name , value string ) {
615+ t .Helper ()
616+ ctx , cancel := context .WithTimeout (context .Background (), 30 * time .Second )
617+ defer cancel ()
618+
619+ browser , launcher , err := launchBrowserWithHandles (ctx , opts )
620+ if err != nil {
621+ t .Fatalf ("launch browser to seed profile cookie: %v" , err )
622+ }
623+ defer launcher .Kill ()
624+ defer func () { _ = browser .Close () }()
625+
626+ if err := browser .Context (ctx ).SetCookies ([]* proto.NetworkCookieParam {{
627+ Name : name ,
628+ Value : value ,
629+ URL : baseURL ,
630+ Path : "/" ,
631+ Expires : proto .TimeSinceEpoch (time .Now ().Add (2 * time .Hour ).Unix ()),
632+ }}); err != nil {
633+ t .Fatalf ("seed browser profile cookie: %v" , err )
634+ }
635+ }
636+
516637// TestBrowserBackend_OverrideHostRespected is a compile-time check that
517638// overrideHost field exists on browserBackend (used by all local tests above).
518639func TestBrowserBackend_OverrideHostRespected (_ * testing.T ) {
0 commit comments