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 }