github.com/letsencrypt/boulder@v0.20251208.0/email/pardot_test.go (about) 1 package email 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "net/http" 8 "net/http/httptest" 9 "testing" 10 "time" 11 12 "github.com/jmhodges/clock" 13 "github.com/letsencrypt/boulder/test" 14 ) 15 16 func defaultTokenHandler(w http.ResponseWriter, r *http.Request) { 17 err := json.NewEncoder(w).Encode(oauthTokenResp{ 18 AccessToken: "dummy", 19 ExpiresIn: 3600, 20 }) 21 if err != nil { 22 // This should never happen. 23 w.WriteHeader(http.StatusInternalServerError) 24 w.Write([]byte("failed to encode token")) 25 return 26 } 27 } 28 29 func TestSendContactSuccess(t *testing.T) { 30 t.Parallel() 31 32 contactHandler := func(w http.ResponseWriter, r *http.Request) { 33 if r.Header.Get("Authorization") != "Bearer dummy" { 34 w.WriteHeader(http.StatusUnauthorized) 35 return 36 } 37 w.WriteHeader(http.StatusOK) 38 } 39 40 tokenSrv := httptest.NewServer(http.HandlerFunc(defaultTokenHandler)) 41 defer tokenSrv.Close() 42 43 contactSrv := httptest.NewServer(http.HandlerFunc(contactHandler)) 44 defer contactSrv.Close() 45 46 clk := clock.NewFake() 47 client, err := NewSalesforceClientImpl(clk, "biz-unit", "cid", "csec", tokenSrv.URL, contactSrv.URL) 48 test.AssertNotError(t, err, "failed to create client") 49 50 err = client.SendContact("test@example.com") 51 test.AssertNotError(t, err, "SendContact should succeed") 52 } 53 54 func TestSendContactUpdateTokenFails(t *testing.T) { 55 t.Parallel() 56 57 tokenHandlerThatAlwaysErrors := func(w http.ResponseWriter, r *http.Request) { 58 w.WriteHeader(http.StatusInternalServerError) 59 fmt.Fprintln(w, "token error") 60 } 61 62 contactHandler := func(w http.ResponseWriter, r *http.Request) { 63 w.WriteHeader(http.StatusOK) 64 } 65 66 tokenSrv := httptest.NewServer(http.HandlerFunc(tokenHandlerThatAlwaysErrors)) 67 defer tokenSrv.Close() 68 69 contactSrv := httptest.NewServer(http.HandlerFunc(contactHandler)) 70 defer contactSrv.Close() 71 72 clk := clock.NewFake() 73 client, err := NewSalesforceClientImpl(clk, "biz-unit", "cid", "csec", tokenSrv.URL, contactSrv.URL) 74 test.AssertNotError(t, err, "Failed to create client") 75 76 err = client.SendContact("test@example.com") 77 test.AssertError(t, err, "Expected token update to fail") 78 test.AssertContains(t, err.Error(), "failed to update token") 79 } 80 81 func TestSendContact4xx(t *testing.T) { 82 t.Parallel() 83 84 contactHandler := func(w http.ResponseWriter, r *http.Request) { 85 w.WriteHeader(http.StatusBadRequest) 86 _, err := io.WriteString(w, "bad request") 87 test.AssertNotError(t, err, "failed to write response") 88 } 89 90 tokenSrv := httptest.NewServer(http.HandlerFunc(defaultTokenHandler)) 91 defer tokenSrv.Close() 92 93 contactSrv := httptest.NewServer(http.HandlerFunc(contactHandler)) 94 defer contactSrv.Close() 95 96 clk := clock.NewFake() 97 client, err := NewSalesforceClientImpl(clk, "biz-unit", "cid", "csec", tokenSrv.URL, contactSrv.URL) 98 test.AssertNotError(t, err, "Failed to create client") 99 100 err = client.SendContact("test@example.com") 101 test.AssertError(t, err, "Should fail on 400") 102 test.AssertContains(t, err.Error(), "create contact request returned status 400") 103 } 104 105 func TestSendContactTokenExpiry(t *testing.T) { 106 t.Parallel() 107 108 // tokenHandler returns "old_token" on the first call and "new_token" on subsequent calls. 109 tokenRetrieved := false 110 tokenHandler := func(w http.ResponseWriter, r *http.Request) { 111 token := "new_token" 112 if !tokenRetrieved { 113 token = "old_token" 114 tokenRetrieved = true 115 } 116 err := json.NewEncoder(w).Encode(oauthTokenResp{ 117 AccessToken: token, 118 ExpiresIn: 3600, 119 }) 120 test.AssertNotError(t, err, "failed to encode token") 121 } 122 123 // contactHandler expects "old_token" for the first request and "new_token" for the next. 124 firstRequest := true 125 contactHandler := func(w http.ResponseWriter, r *http.Request) { 126 expectedToken := "new_token" 127 if firstRequest { 128 expectedToken = "old_token" 129 firstRequest = false 130 } 131 if r.Header.Get("Authorization") != "Bearer "+expectedToken { 132 w.WriteHeader(http.StatusUnauthorized) 133 return 134 } 135 w.WriteHeader(http.StatusOK) 136 } 137 138 tokenSrv := httptest.NewServer(http.HandlerFunc(tokenHandler)) 139 defer tokenSrv.Close() 140 141 contactSrv := httptest.NewServer(http.HandlerFunc(contactHandler)) 142 defer contactSrv.Close() 143 144 clk := clock.NewFake() 145 client, err := NewSalesforceClientImpl(clk, "biz-unit", "cid", "csec", tokenSrv.URL, contactSrv.URL) 146 test.AssertNotError(t, err, "Failed to create client") 147 148 // First call uses the initial token ("old_token"). 149 err = client.SendContact("test@example.com") 150 test.AssertNotError(t, err, "SendContact should succeed with the initial token") 151 152 // Advance time to force token expiry. 153 clk.Add(3601 * time.Second) 154 155 // Second call should refresh the token to "new_token". 156 err = client.SendContact("test@example.com") 157 test.AssertNotError(t, err, "SendContact should succeed after refreshing the token") 158 } 159 160 func TestSendContactServerErrorsAfterMaxAttempts(t *testing.T) { 161 t.Parallel() 162 163 gotAttempts := 0 164 contactHandler := func(w http.ResponseWriter, r *http.Request) { 165 gotAttempts++ 166 w.WriteHeader(http.StatusServiceUnavailable) 167 } 168 169 tokenSrv := httptest.NewServer(http.HandlerFunc(defaultTokenHandler)) 170 defer tokenSrv.Close() 171 172 contactSrv := httptest.NewServer(http.HandlerFunc(contactHandler)) 173 defer contactSrv.Close() 174 175 client, err := NewSalesforceClientImpl(clock.NewFake(), "biz-unit", "cid", "csec", tokenSrv.URL, contactSrv.URL) 176 test.AssertNotError(t, err, "Failed to create Salesforce API client") 177 178 err = client.SendContact("test@example.com") 179 test.AssertError(t, err, "Should fail after retrying all attempts") 180 test.AssertEquals(t, maxAttempts, gotAttempts) 181 test.AssertContains(t, err.Error(), "create contact request returned status 503") 182 } 183 184 func TestSendContactRedactsEmail(t *testing.T) { 185 t.Parallel() 186 187 emailToTest := "test@example.com" 188 189 contactHandler := func(w http.ResponseWriter, r *http.Request) { 190 w.WriteHeader(http.StatusBadRequest) 191 // Intentionally include the request email in the response body. 192 resp := fmt.Sprintf("error: %s is invalid", emailToTest) 193 _, err := io.WriteString(w, resp) 194 test.AssertNotError(t, err, "failed to write response") 195 } 196 197 tokenSrv := httptest.NewServer(http.HandlerFunc(defaultTokenHandler)) 198 defer tokenSrv.Close() 199 200 contactSrv := httptest.NewServer(http.HandlerFunc(contactHandler)) 201 defer contactSrv.Close() 202 203 clk := clock.NewFake() 204 client, err := NewSalesforceClientImpl(clk, "biz-unit", "cid", "csec", tokenSrv.URL, contactSrv.URL) 205 test.AssertNotError(t, err, "failed to create client") 206 207 err = client.SendContact(emailToTest) 208 test.AssertError(t, err, "SendContact should fail") 209 test.AssertNotContains(t, err.Error(), emailToTest) 210 test.AssertContains(t, err.Error(), "[REDACTED]") 211 } 212 213 func TestSendCaseSuccess(t *testing.T) { 214 t.Parallel() 215 216 handler := func(w http.ResponseWriter, r *http.Request) { 217 switch r.URL.Path { 218 case "/services/oauth2/token": 219 defaultTokenHandler(w, r) 220 case "/services/data/v64.0/sobjects/Case": 221 if r.Header.Get("Authorization") != "Bearer dummy" { 222 w.WriteHeader(http.StatusUnauthorized) 223 return 224 } 225 w.WriteHeader(http.StatusCreated) 226 w.Write([]byte(`{"id":"500xx000001ABCdAAH","success":true,"errors":[]}`)) 227 default: 228 t.Errorf("unexpected path: %s", r.URL.Path) 229 w.WriteHeader(http.StatusNotFound) 230 } 231 } 232 233 salesforceSrv := httptest.NewServer(http.HandlerFunc(handler)) 234 defer salesforceSrv.Close() 235 236 client, err := NewSalesforceClientImpl(clock.NewFake(), "biz-unit", "cid", "csec", salesforceSrv.URL, "") 237 test.AssertNotError(t, err, "failed to create client") 238 239 err = client.SendCase(Case{ 240 Subject: "Unit Test Case", 241 Origin: "Web", 242 }) 243 test.AssertNotError(t, err, "SendCase should succeed") 244 } 245 246 func TestSendCaseUpdateTokenFails(t *testing.T) { 247 t.Parallel() 248 249 handler := func(w http.ResponseWriter, r *http.Request) { 250 switch r.URL.Path { 251 case "/services/oauth2/token": 252 w.WriteHeader(http.StatusInternalServerError) 253 fmt.Fprintln(w, "token error") 254 case "/services/data/v64.0/sobjects/Case": 255 w.WriteHeader(http.StatusOK) // should never reach here 256 default: 257 t.Errorf("unexpected path: %s", r.URL.Path) 258 w.WriteHeader(http.StatusNotFound) 259 } 260 } 261 262 salesforceSrv := httptest.NewServer(http.HandlerFunc(handler)) 263 defer salesforceSrv.Close() 264 265 client, err := NewSalesforceClientImpl(clock.NewFake(), "biz-unit", "cid", "csec", salesforceSrv.URL, "") 266 test.AssertNotError(t, err, "Failed to create client") 267 268 err = client.SendCase(Case{Subject: "fail", Origin: "Web"}) 269 test.AssertError(t, err, "Expected token update to fail") 270 test.AssertContains(t, err.Error(), "failed to update token") 271 } 272 273 func TestSendCase4xx(t *testing.T) { 274 t.Parallel() 275 276 handler := func(w http.ResponseWriter, r *http.Request) { 277 switch r.URL.Path { 278 case "/services/oauth2/token": 279 defaultTokenHandler(w, r) 280 case "/services/data/v64.0/sobjects/Case": 281 w.WriteHeader(http.StatusBadRequest) 282 _, err := io.WriteString(w, "bad request") 283 test.AssertNotError(t, err, "failed to write response") 284 default: 285 t.Errorf("unexpected path: %s", r.URL.Path) 286 w.WriteHeader(http.StatusNotFound) 287 } 288 } 289 290 salesforceSrv := httptest.NewServer(http.HandlerFunc(handler)) 291 defer salesforceSrv.Close() 292 293 client, err := NewSalesforceClientImpl(clock.NewFake(), "biz-unit", "cid", "csec", salesforceSrv.URL, "") 294 test.AssertNotError(t, err, "Failed to create client") 295 296 err = client.SendCase(Case{Subject: "bad", Origin: "Web"}) 297 test.AssertError(t, err, "Should fail on 400") 298 test.AssertContains(t, err.Error(), "create case request returned status 400") 299 } 300 301 func TestSendCaseServerErrorsAfterMaxAttempts(t *testing.T) { 302 t.Parallel() 303 304 gotAttempts := 0 305 handler := func(w http.ResponseWriter, r *http.Request) { 306 switch r.URL.Path { 307 case "/services/oauth2/token": 308 defaultTokenHandler(w, r) 309 case "/services/data/v64.0/sobjects/Case": 310 gotAttempts++ 311 w.WriteHeader(http.StatusServiceUnavailable) 312 default: 313 t.Errorf("unexpected path: %s", r.URL.Path) 314 w.WriteHeader(http.StatusNotFound) 315 } 316 } 317 318 salesforceSrv := httptest.NewServer(http.HandlerFunc(handler)) 319 defer salesforceSrv.Close() 320 321 client, err := NewSalesforceClientImpl(clock.NewFake(), "biz-unit", "cid", "csec", salesforceSrv.URL, "") 322 test.AssertNotError(t, err, "Failed to create client") 323 324 err = client.SendCase(Case{Subject: "retry", Origin: "Web"}) 325 test.AssertError(t, err, "Should fail after retrying all attempts") 326 test.AssertEquals(t, maxAttempts, gotAttempts) 327 test.AssertContains(t, err.Error(), "create case request returned status 503") 328 }