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  }