11package config
22
33import (
4- "encoding/json"
5- "net/http"
6- "net/http/httptest"
7- "strings"
8- "sync"
94 "testing"
105
116 configtests "github.com/jfrog/jfrog-cli-core/v2/utils/config/tests"
127 "github.com/stretchr/testify/assert"
13- "github.com/stretchr/testify/require"
148)
159
1610func TestCreateInitialRefreshableTokensIfNeededEarlyReturns (t * testing.T ) {
@@ -202,11 +196,21 @@ func TestCreateInitialRefreshableTokensIfNeededValidInputs(t *testing.T) {
202196
203197 err = CreateInitialRefreshableTokensIfNeeded (serverDetailsCopy )
204198
205- // With the Access API migration, token creation fails gracefully when no server is reachable:
206- // the function returns nil error and resets the interval to 0 (disabling token refresh).
207- assert .NoError (t , err , "Should not return error — graceful fallback to basic auth" )
208- assert .Equal (t , 0 , serverDetailsCopy .ArtifactoryTokenRefreshInterval ,
209- "ArtifactoryTokenRefreshInterval should be reset to 0 after token creation attempt" )
199+ if tt .expectError {
200+ assert .Error (t , err , "Expected an error but got none" )
201+ } else if err == nil {
202+ // Note: This will fail if createTokensForConfig requires actual Artifactory connection
203+ // In that case, this test would need to be an integration test with a mock server
204+ assert .Equal (t , tt .expectedIntervalAfterCall , serverDetailsCopy .ArtifactoryTokenRefreshInterval ,
205+ "ArtifactoryTokenRefreshInterval should be reset to 0 after successful token creation" )
206+ // Verify tokens were set (if no error occurred)
207+ // Note: This assumes createTokensForConfig succeeded
208+ // In a real scenario, you'd need to mock the Artifactory service
209+ if tt .shouldCreateTokens {
210+ assert .NotEmpty (t , serverDetailsCopy .AccessToken , "AccessToken should be set after successful creation" )
211+ assert .NotEmpty (t , serverDetailsCopy .ArtifactoryRefreshToken , "ArtifactoryRefreshToken should be set after successful creation" )
212+ }
213+ }
210214 })
211215 }
212216}
@@ -302,11 +306,20 @@ func TestCreateInitialRefreshableTokensIfNeededInputValidation(t *testing.T) {
302306 // 1. Return early (if conditions are met)
303307 // 2. Attempt to create tokens and potentially fail due to invalid input
304308 // We validate that the function handles the input gracefully
305- // With graceful fallback, token creation failures return nil error
306- assert .NoError (t , err , "Should not return error — graceful fallback" )
307- if initialInterval > 0 {
308- assert .Equal (t , 0 , serverDetailsCopy .ArtifactoryTokenRefreshInterval ,
309- "Interval should be reset to 0 after token creation attempt" )
309+ if err != nil {
310+ // If there's an error, it should be due to invalid configuration
311+ // The interval should be reset to 0 if token creation was attempted
312+ if serverDetailsCopy .ArtifactoryTokenRefreshInterval == 0 {
313+ // Token creation was attempted but failed
314+ assert .Error (t , err , tt .description )
315+ }
316+ } else {
317+ // If no error, either early return occurred or tokens were created successfully
318+ if initialInterval > 0 && serverDetailsCopy .ArtifactoryTokenRefreshInterval == 0 {
319+ // Tokens were created successfully
320+ assert .NotEmpty (t , serverDetailsCopy .AccessToken , "AccessToken should be set after successful creation" )
321+ assert .NotEmpty (t , serverDetailsCopy .ArtifactoryRefreshToken , "ArtifactoryRefreshToken should be set after successful creation" )
322+ }
310323 }
311324 })
312325 }
@@ -557,156 +570,3 @@ func TestCreateInitialRefreshableTokensIfNeededBranchCoverage(t *testing.T) {
557570 assert .NotNil (t , serverDetails )
558571 })
559572}
560-
561- func TestAccessAPITokenCreation_MockServer (t * testing.T ) {
562- cleanUpTempEnv := configtests .CreateTempEnv (t , false )
563- defer cleanUpTempEnv ()
564-
565- var mu sync.Mutex
566- var accessAPIHit bool
567- var authMethod string
568-
569- mockServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
570- if r .URL .Path == "/access/api/v1/tokens" && r .Method == http .MethodPost {
571- mu .Lock ()
572- accessAPIHit = true
573- if _ , _ , ok := r .BasicAuth (); ok {
574- authMethod = "basic"
575- } else if strings .HasPrefix (r .Header .Get ("Authorization" ), "Bearer " ) {
576- authMethod = "bearer"
577- }
578- mu .Unlock ()
579- w .Header ().Set ("Content-Type" , "application/json" )
580- // #nosec G101 -- mock test credentials, not real tokens
581- resp := map [string ]interface {}{
582- "access_token" : "mock-jwt-token" , // #nosec G101
583- "refresh_token" : "mock-refresh-token" , // #nosec G101
584- "expires_in" : 3600 ,
585- "scope" : "applied-permissions/user" ,
586- "token_type" : "Bearer" ,
587- }
588- _ = json .NewEncoder (w ).Encode (resp )
589- return
590- }
591- w .WriteHeader (http .StatusNotFound )
592- }))
593- defer mockServer .Close ()
594-
595- platformURL := mockServer .URL + "/"
596-
597- serverDetails := & ServerDetails {
598- ServerId : "test-access-api" ,
599- ArtifactoryTokenRefreshInterval : 60 ,
600- ArtifactoryRefreshToken : "" ,
601- AccessToken : "" ,
602- ArtifactoryUrl : mockServer .URL + "/artifactory/" ,
603- Url : platformURL ,
604- User : "testuser" ,
605- Password : "my-regular-password" ,
606- }
607-
608- err := SaveServersConf ([]* ServerDetails {serverDetails })
609- require .NoError (t , err )
610-
611- _ = CreateInitialRefreshableTokensIfNeeded (serverDetails )
612-
613- mu .Lock ()
614- assert .True (t , accessAPIHit ,
615- "Token creation should use the Access API endpoint" )
616- assert .Equal (t , "basic" , authMethod ,
617- "Regular password should use basic auth" )
618- mu .Unlock ()
619- }
620-
621- func TestBasicAuthFailsFallsBackToBearer_MockServer (t * testing.T ) {
622- cleanUpTempEnv := configtests .CreateTempEnv (t , false )
623- defer cleanUpTempEnv ()
624-
625- var mu sync.Mutex
626- var attempts []string
627-
628- // #nosec G101 -- mock reference token, not a real credential
629- fakeRefToken := "cmVmdGtuOjAxOjE3NzczNTczOTI6ZmFrZVRva2VuRm9yVGVzdGluZ09ubHk="
630-
631- mockServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
632- if r .URL .Path == "/access/api/v1/tokens" && r .Method == http .MethodPost {
633- mu .Lock ()
634- if _ , _ , ok := r .BasicAuth (); ok {
635- attempts = append (attempts , "basic" )
636- mu .Unlock ()
637- w .WriteHeader (http .StatusUnauthorized )
638- return
639- } else if strings .HasPrefix (r .Header .Get ("Authorization" ), "Bearer " ) {
640- attempts = append (attempts , "bearer" )
641- mu .Unlock ()
642- w .Header ().Set ("Content-Type" , "application/json" )
643- // #nosec G101 -- mock test credentials, not real tokens
644- resp := map [string ]interface {}{
645- "access_token" : "mock-scoped-jwt" , // #nosec G101
646- "refresh_token" : "mock-refresh-token" , // #nosec G101
647- "expires_in" : 3600 ,
648- "scope" : "applied-permissions/user" ,
649- "token_type" : "Bearer" ,
650- }
651- _ = json .NewEncoder (w ).Encode (resp )
652- return
653- }
654- mu .Unlock ()
655- }
656- w .WriteHeader (http .StatusNotFound )
657- }))
658- defer mockServer .Close ()
659-
660- serverDetails := & ServerDetails {
661- ServerId : "test-retry-bearer" ,
662- ArtifactoryTokenRefreshInterval : 60 ,
663- ArtifactoryRefreshToken : "" ,
664- AccessToken : "" ,
665- ArtifactoryUrl : mockServer .URL + "/artifactory/" ,
666- Url : mockServer .URL + "/" ,
667- User : "testuser" ,
668- Password : fakeRefToken ,
669- }
670-
671- err := SaveServersConf ([]* ServerDetails {serverDetails })
672- require .NoError (t , err )
673-
674- err = CreateInitialRefreshableTokensIfNeeded (serverDetails )
675- assert .NoError (t , err )
676-
677- mu .Lock ()
678- assert .Equal (t , []string {"basic" , "bearer" }, attempts ,
679- "Should first try basic auth, then fall back to bearer when basic fails" )
680- mu .Unlock ()
681- }
682-
683- func TestAccessAPITokenCreationFails_MockServer (t * testing.T ) {
684- cleanUpTempEnv := configtests .CreateTempEnv (t , false )
685- defer cleanUpTempEnv ()
686-
687- mockServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
688- w .WriteHeader (http .StatusUnauthorized )
689- }))
690- defer mockServer .Close ()
691-
692- serverDetails := & ServerDetails {
693- ServerId : "test-api-fail" ,
694- ArtifactoryTokenRefreshInterval : 60 ,
695- ArtifactoryRefreshToken : "" ,
696- AccessToken : "" ,
697- ArtifactoryUrl : mockServer .URL + "/" ,
698- Url : mockServer .URL + "/" ,
699- User : "testuser" ,
700- Password : "my-regular-password" ,
701- }
702-
703- err := SaveServersConf ([]* ServerDetails {serverDetails })
704- require .NoError (t , err )
705-
706- err = CreateInitialRefreshableTokensIfNeeded (serverDetails )
707- assert .NoError (t , err , "Should not return error when Access API fails — gracefully falls back to basic auth" )
708- assert .Equal (t , 0 , serverDetails .ArtifactoryTokenRefreshInterval ,
709- "Token refresh should be disabled after Access API failure" )
710- assert .Empty (t , serverDetails .AccessToken ,
711- "No access token should be set when Access API fails" )
712- }
0 commit comments