github.com/letsencrypt/boulder@v0.20251208.0/test/load-generator/acme/directory_test.go (about)

     1  package acme
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"net/url"
     9  	"testing"
    10  
    11  	"github.com/letsencrypt/boulder/test"
    12  )
    13  
    14  // Path constants for test cases and mockDirectoryServer handlers.
    15  const (
    16  	wrongStatusCodePath         = "/dir-wrong-status"
    17  	invalidJSONPath             = "/dir-bad-json"
    18  	missingEndpointPath         = "/dir-missing-endpoint"
    19  	invalidEndpointURLPath      = "/dir-invalid-endpoint"
    20  	validDirectoryPath          = "/dir-valid"
    21  	invalidMetaDirectoryPath    = "/dir-valid-meta-invalid"
    22  	invalidMetaDirectoryToSPath = "/dir-valid-meta-valid-tos-invalid"
    23  )
    24  
    25  // mockDirectoryServer is an httptest.Server that returns mock data for ACME
    26  // directory GET requests based on the requested path.
    27  type mockDirectoryServer struct {
    28  	*httptest.Server
    29  }
    30  
    31  // newMockDirectoryServer creates a mockDirectoryServer that returns mock data
    32  // based on the requested path. The returned server will not be started
    33  // automatically.
    34  func newMockDirectoryServer() *mockDirectoryServer {
    35  	m := http.NewServeMux()
    36  
    37  	m.HandleFunc(wrongStatusCodePath, func(w http.ResponseWriter, r *http.Request) {
    38  		w.WriteHeader(http.StatusUnavailableForLegalReasons)
    39  	})
    40  
    41  	m.HandleFunc(invalidJSONPath, func(w http.ResponseWriter, r *http.Request) {
    42  		fmt.Fprint(w, `{`)
    43  	})
    44  
    45  	m.HandleFunc(missingEndpointPath, func(w http.ResponseWriter, r *http.Request) {
    46  		fmt.Fprint(w, `{}`)
    47  	})
    48  
    49  	m.HandleFunc(invalidEndpointURLPath, func(w http.ResponseWriter, r *http.Request) {
    50  		fmt.Fprint(w, `{
    51  			 "newAccount": "",
    52  			 "newNonce": "ht\ntp://bad-scheme",
    53  			 "newOrder": "",
    54  			 "revokeCert": ""
    55  		}`)
    56  	})
    57  
    58  	m.HandleFunc(invalidMetaDirectoryPath, func(w http.ResponseWriter, r *http.Request) {
    59  		noMetaDir := `{
    60  			 "keyChange": "https://localhost:14000/rollover-account-key",
    61  			 "newAccount": "https://localhost:14000/sign-me-up",
    62  			 "newNonce": "https://localhost:14000/nonce-plz",
    63  			 "newOrder": "https://localhost:14000/order-plz",
    64  			 "revokeCert": "https://localhost:14000/revoke-cert"
    65  		}`
    66  		fmt.Fprint(w, noMetaDir)
    67  	})
    68  
    69  	m.HandleFunc(invalidMetaDirectoryToSPath, func(w http.ResponseWriter, r *http.Request) {
    70  		noToSDir := `{
    71  			 "keyChange": "https://localhost:14000/rollover-account-key",
    72  			 "meta": {
    73  				 "chaos": "reigns"
    74  			 },
    75  			 "newAccount": "https://localhost:14000/sign-me-up",
    76  			 "newNonce": "https://localhost:14000/nonce-plz",
    77  			 "newOrder": "https://localhost:14000/order-plz",
    78  			 "revokeCert": "https://localhost:14000/revoke-cert"
    79  		}`
    80  		fmt.Fprint(w, noToSDir)
    81  	})
    82  
    83  	m.HandleFunc(validDirectoryPath, func(w http.ResponseWriter, r *http.Request) {
    84  		validDir := `{
    85  			 "keyChange": "https://localhost:14000/rollover-account-key",
    86  			 "meta": {
    87  					"termsOfService": "data:text/plain,Do%20what%20thou%20wilt"
    88  			 },
    89  			 "newAccount": "https://localhost:14000/sign-me-up",
    90  			 "newNonce": "https://localhost:14000/nonce-plz",
    91  			 "newOrder": "https://localhost:14000/order-plz",
    92  			 "revokeCert": "https://localhost:14000/revoke-cert"
    93  		}`
    94  		fmt.Fprint(w, validDir)
    95  	})
    96  
    97  	srv := &mockDirectoryServer{
    98  		Server: httptest.NewUnstartedServer(m),
    99  	}
   100  
   101  	return srv
   102  }
   103  
   104  // TestNew tests that creating a new Client and populating the endpoint map
   105  // works correctly.
   106  func TestNew(t *testing.T) {
   107  	srv := newMockDirectoryServer()
   108  	srv.Start()
   109  	defer srv.Close()
   110  
   111  	srvUrl, _ := url.Parse(srv.URL)
   112  	_, port, _ := net.SplitHostPort(srvUrl.Host)
   113  
   114  	testURL := func(path string) string {
   115  		return fmt.Sprintf("http://localhost:%s%s", port, path)
   116  	}
   117  
   118  	testCases := []struct {
   119  		Name          string
   120  		DirectoryURL  string
   121  		ExpectedError string
   122  	}{
   123  		{
   124  			Name:          "empty directory URL",
   125  			ExpectedError: ErrEmptyDirectory.Error(),
   126  		},
   127  		{
   128  			Name:          "invalid directory URL",
   129  			DirectoryURL:  "http://" + string([]byte{0x1, 0x7F}),
   130  			ExpectedError: ErrInvalidDirectoryURL.Error(),
   131  		},
   132  		{
   133  			Name:          "unreachable directory URL",
   134  			DirectoryURL:  "http://localhost:1987",
   135  			ExpectedError: "connect: connection refused",
   136  		},
   137  		{
   138  			Name:          "wrong directory HTTP status code",
   139  			DirectoryURL:  testURL(wrongStatusCodePath),
   140  			ExpectedError: ErrInvalidDirectoryHTTPCode.Error(),
   141  		},
   142  		{
   143  			Name:          "invalid directory JSON",
   144  			DirectoryURL:  testURL(invalidJSONPath),
   145  			ExpectedError: ErrInvalidDirectoryJSON.Error(),
   146  		},
   147  		{
   148  			Name:          "directory JSON missing required endpoint",
   149  			DirectoryURL:  testURL(missingEndpointPath),
   150  			ExpectedError: ErrMissingEndpoint{endpoint: NewNonceEndpoint}.Error(),
   151  		},
   152  		{
   153  			Name:         "directory JSON with invalid endpoint URL",
   154  			DirectoryURL: testURL(invalidEndpointURLPath),
   155  			ExpectedError: ErrInvalidEndpointURL{
   156  				endpoint: NewNonceEndpoint,
   157  				value:    "ht\ntp://bad-scheme",
   158  			}.Error(),
   159  		},
   160  		{
   161  			Name:          "directory JSON missing meta key",
   162  			DirectoryURL:  testURL(invalidMetaDirectoryPath),
   163  			ExpectedError: ErrInvalidDirectoryMeta.Error(),
   164  		},
   165  		{
   166  			Name:          "directory JSON missing meta TermsOfService key",
   167  			DirectoryURL:  testURL(invalidMetaDirectoryToSPath),
   168  			ExpectedError: ErrInvalidTermsOfService.Error(),
   169  		},
   170  		{
   171  			Name:         "valid directory",
   172  			DirectoryURL: testURL(validDirectoryPath),
   173  		},
   174  	}
   175  
   176  	for _, tc := range testCases {
   177  		t.Run(tc.Name, func(t *testing.T) {
   178  			_, err := NewDirectory(tc.DirectoryURL)
   179  			if err == nil && tc.ExpectedError != "" {
   180  				t.Errorf("expected error %q got nil", tc.ExpectedError)
   181  			} else if err != nil {
   182  				test.AssertContains(t, err.Error(), tc.ExpectedError)
   183  			}
   184  		})
   185  	}
   186  }