github.com/alwitt/goutils@v0.6.4/oauth_test.go (about) 1 package goutils_test 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net/http" 9 "testing" 10 "time" 11 12 "github.com/alwitt/goutils" 13 "github.com/apex/log" 14 "github.com/go-resty/resty/v2" 15 "github.com/google/uuid" 16 "github.com/jarcoal/httpmock" 17 "github.com/stretchr/testify/assert" 18 ) 19 20 func TestClientCredOAuthTokenManager(t *testing.T) { 21 assert := assert.New(t) 22 log.SetLevel(log.DebugLevel) 23 24 utCtxt := context.Background() 25 26 httpmock.Activate() 27 defer httpmock.DeactivateAndReset() 28 29 testClient := resty.New() 30 // Install with mock 31 httpmock.ActivateNonDefault(testClient.GetClient()) 32 33 // ------------------------------------------------------------------------------------ 34 // Prepare mock 35 36 idpBaseURL := "http://idp.testing.dev" 37 configURL := fmt.Sprintf("%s/.well-known/openid-configuration", idpBaseURL) 38 tokenURL := fmt.Sprintf("%s/auth/token", idpBaseURL) 39 40 testCfg := goutils.ClientCredOAuthTokenManagerParam{ 41 IDPIssuerURL: idpBaseURL, 42 ClientID: uuid.NewString(), 43 ClientSecret: uuid.NewString(), 44 TargetAudience: "http://application.testing.dev", 45 LogTags: log.Fields{"module": "goutils", "component": "client-cred-oauth"}, 46 CustomLogModifiers: []goutils.LogMetadataModifier{}, 47 } 48 49 // Prepare to receive IDP config call 50 httpmock.RegisterResponder( 51 "GET", 52 configURL, 53 func(r *http.Request) (*http.Response, error) { 54 return httpmock.NewJsonResponse(200, map[string]string{ 55 "token_endpoint": tokenURL, 56 }) 57 }, 58 ) 59 60 uut, err := goutils.GetNewClientCredOAuthTokenManager(utCtxt, testClient, testCfg) 61 assert.Nil(err) 62 63 currentTime := time.Now().UTC() 64 65 // Case 0: get token 66 testToken0 := map[string]interface{}{ 67 "access_token": uuid.NewString(), 68 "expires_in": 300, 69 } 70 { 71 // Prepare mock 72 httpmock.RegisterResponder( 73 "POST", 74 tokenURL, 75 func(r *http.Request) (*http.Response, error) { 76 req, err := io.ReadAll(r.Body) 77 assert.Nil(err) 78 79 reqBody := map[string]string{} 80 assert.Nil(json.Unmarshal(req, &reqBody)) 81 82 clientID, ok := reqBody["client_id"] 83 assert.True(ok) 84 assert.Equal(testCfg.ClientID, clientID) 85 86 clientSecret, ok := reqBody["client_secret"] 87 assert.True(ok) 88 assert.Equal(testCfg.ClientSecret, clientSecret) 89 90 audience, ok := reqBody["audience"] 91 assert.True(ok) 92 assert.Equal(testCfg.TargetAudience, audience) 93 94 grantType, ok := reqBody["grant_type"] 95 assert.True(ok) 96 assert.Equal("client_credentials", grantType) 97 98 // Return the token 99 return httpmock.NewJsonResponse(200, testToken0) 100 }, 101 ) 102 103 waitChan := make(chan bool, 1) 104 105 lclCtxt, lclCancel := context.WithTimeout(utCtxt, time.Second) 106 go func() { 107 workingToken, err := uut.GetToken(lclCtxt, currentTime) 108 assert.Nil(err) 109 assert.Equal(testToken0["access_token"], workingToken) 110 waitChan <- true 111 }() 112 113 select { 114 case <-lclCtxt.Done(): 115 assert.False(true, "request timed out") 116 case <-waitChan: 117 break 118 } 119 lclCancel() 120 } 121 122 // Clear mocks 123 httpmock.Reset() 124 125 // Case 1: get token again 126 { 127 lclCtxt, lclCancel := context.WithTimeout(utCtxt, time.Second) 128 workingToken, err := uut.GetToken(lclCtxt, currentTime) 129 assert.Nil(err) 130 assert.Equal(testToken0["access_token"], workingToken) 131 lclCancel() 132 } 133 134 // Case 2: token timeout, get new token 135 testToken1 := map[string]interface{}{ 136 "access_token": uuid.NewString(), 137 "expires_in": 300, 138 } 139 currentTime = currentTime.Add(time.Second * 400) 140 { 141 // Prepare mock 142 httpmock.RegisterResponder( 143 "POST", 144 tokenURL, 145 func(r *http.Request) (*http.Response, error) { 146 return httpmock.NewJsonResponse(200, testToken1) 147 }, 148 ) 149 150 waitChan := make(chan bool, 1) 151 152 lclCtxt, lclCancel := context.WithTimeout(utCtxt, time.Second) 153 go func() { 154 workingToken, err := uut.GetToken(lclCtxt, currentTime) 155 assert.Nil(err) 156 assert.Equal(testToken1["access_token"], workingToken) 157 waitChan <- true 158 }() 159 160 select { 161 case <-lclCtxt.Done(): 162 assert.False(true, "request timed out") 163 case <-waitChan: 164 break 165 } 166 lclCancel() 167 } 168 169 // Clean up 170 assert.Nil(uut.Stop(utCtxt)) 171 }