github.com/splucs/witchcraft-go-server@v1.7.0/status/routes/routes_test.go (about) 1 // Copyright (c) 2018 Palantir Technologies. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package routes 16 17 import ( 18 "context" 19 "encoding/json" 20 "fmt" 21 "io/ioutil" 22 "net/http" 23 "net/http/httptest" 24 "strings" 25 "testing" 26 27 "github.com/palantir/witchcraft-go-server/conjure/witchcraft/api/health" 28 "github.com/palantir/witchcraft-go-server/status" 29 "github.com/palantir/witchcraft-go-server/witchcraft/refreshable" 30 "github.com/palantir/witchcraft-go-server/witchcraft/wresource" 31 "github.com/palantir/witchcraft-go-server/wrouter" 32 "github.com/palantir/witchcraft-go-server/wrouter/whttprouter" 33 "github.com/stretchr/testify/assert" 34 "github.com/stretchr/testify/require" 35 ) 36 37 func TestAddStatusRoutes(t *testing.T) { 38 type testMetadata struct { 39 Message string 40 Value int 41 } 42 43 for i, tc := range []struct { 44 endpoint string 45 routeFunc func(resource wresource.Resource, source status.Source) error 46 status int 47 metadata testMetadata 48 }{ 49 { 50 endpoint: status.LivenessEndpoint, 51 routeFunc: AddLivenessRoutes, 52 status: http.StatusOK, 53 metadata: testMetadata{ 54 Message: "hello", 55 Value: 13, 56 }, 57 }, 58 { 59 endpoint: status.ReadinessEndpoint, 60 routeFunc: AddReadinessRoutes, 61 status: http.StatusServiceUnavailable, 62 metadata: testMetadata{ 63 Message: "goodbye", 64 Value: 1, 65 }, 66 }, 67 } { 68 func() { 69 r := wrouter.New(whttprouter.New(), nil) 70 resource := wresource.New("test", r) 71 err := tc.routeFunc(resource, statusFunc(func() (int, interface{}) { 72 return tc.status, tc.metadata 73 })) 74 require.NoError(t, err, "Case %d", i) 75 76 server := httptest.NewServer(r) 77 defer server.Close() 78 79 resp, err := http.Get(strings.Join([]string{server.URL, tc.endpoint}, "/")) 80 require.NoError(t, err, "Case %d", i) 81 assert.Equal(t, tc.status, resp.StatusCode, "Case %d", i) 82 83 bytes, err := ioutil.ReadAll(resp.Body) 84 require.NoError(t, err, "Case %d", i) 85 var gotObj testMetadata 86 err = json.Unmarshal(bytes, &gotObj) 87 require.NoError(t, err, "Case %d", i) 88 assert.Equal(t, tc.metadata, gotObj) 89 }() 90 } 91 } 92 93 func TestAddHealthRoute(t *testing.T) { 94 for _, test := range []struct { 95 name string 96 metadata health.HealthStatus 97 sharedSecret string 98 actualSecret string 99 expectedStatus int 100 }{ 101 { 102 "zero healthy check", 103 health.HealthStatus{ 104 Checks: make(map[health.CheckType]health.HealthCheckResult), 105 }, 106 "", 107 "", 108 200, 109 }, 110 { 111 "one healthy check", 112 health.HealthStatus{ 113 Checks: map[health.CheckType]health.HealthCheckResult{ 114 "SCHEMA_VERSION": { 115 Type: "SCHEMA_VERSION", 116 State: health.HealthStateHealthy, 117 Params: make(map[string]interface{}), 118 }, 119 }, 120 }, 121 "", 122 "secret shouldn't matter", 123 200, 124 }, 125 { 126 "one healthy and one error", 127 health.HealthStatus{ 128 Checks: map[health.CheckType]health.HealthCheckResult{ 129 "SCHEMA_VERSION": { 130 Type: "SCHEMA_VERSION", 131 State: health.HealthStateHealthy, 132 Params: make(map[string]interface{}), 133 }, 134 "SERVER_ERROR": { 135 Type: "SERVER_ERROR", 136 State: health.HealthStateError, 137 Params: make(map[string]interface{}), 138 }, 139 }, 140 }, 141 "", 142 "", 143 522, 144 }, 145 { 146 "health, terminal, error", 147 health.HealthStatus{ 148 Checks: map[health.CheckType]health.HealthCheckResult{ 149 "SCHEMA_VERSION": { 150 Type: "SCHEMA_VERSION", 151 State: health.HealthStateHealthy, 152 Params: make(map[string]interface{}), 153 }, 154 "SERVER_TERMINAL": { 155 Type: "SERVER_TERMINAL", 156 State: health.HealthStateTerminal, 157 Params: make(map[string]interface{}), 158 }, 159 "SERVER_ERROR": { 160 Type: "SERVER_ERROR", 161 State: health.HealthStateError, 162 Params: make(map[string]interface{}), 163 }, 164 }, 165 }, 166 "", 167 "", 168 523, 169 }, 170 { 171 "one healthy check with secret", 172 health.HealthStatus{ 173 Checks: map[health.CheckType]health.HealthCheckResult{ 174 "SCHEMA_VERSION": { 175 Type: "SCHEMA_VERSION", 176 State: health.HealthStateHealthy, 177 Params: make(map[string]interface{}), 178 }, 179 }, 180 }, 181 "top-secret", 182 "top-secret", 183 200, 184 }, 185 { 186 "one healthy check with incorrect secret", 187 health.HealthStatus{ 188 Checks: map[health.CheckType]health.HealthCheckResult{ 189 "SCHEMA_VERSION": { 190 Type: "SCHEMA_VERSION", 191 State: health.HealthStateHealthy, 192 Params: make(map[string]interface{}), 193 }, 194 }, 195 }, 196 "top-secret", 197 "bad-secret", 198 401, 199 }, 200 } { 201 t.Run(test.name, func(t *testing.T) { 202 r := wrouter.New(whttprouter.New()) 203 resource := wresource.New("test", r) 204 err := AddHealthRoutes(resource, healthCheck{value: test.metadata}, refreshable.NewString(refreshable.NewDefaultRefreshable(test.sharedSecret))) 205 require.NoError(t, err) 206 207 server := httptest.NewServer(r) 208 defer server.Close() 209 210 client := http.DefaultClient 211 request, err := http.NewRequest(http.MethodGet, strings.Join([]string{server.URL, status.HealthEndpoint}, "/"), nil) 212 require.NoError(t, err) 213 request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", test.actualSecret)) 214 resp, err := client.Do(request) 215 require.NoError(t, err) 216 assert.Equal(t, test.expectedStatus, resp.StatusCode) 217 bytes, err := ioutil.ReadAll(resp.Body) 218 require.NoError(t, err) 219 var gotObj health.HealthStatus 220 err = json.Unmarshal(bytes, &gotObj) 221 require.NoError(t, err) 222 223 expectedChecks := test.metadata 224 // 401 does not return any health check data 225 if test.expectedStatus == 401 { 226 expectedChecks.Checks = map[health.CheckType]health.HealthCheckResult{} 227 } 228 assert.Equal(t, expectedChecks, gotObj) 229 }) 230 } 231 } 232 233 type statusFunc func() (int, interface{}) 234 235 func (f statusFunc) Status() (int, interface{}) { 236 return f() 237 } 238 239 type healthCheck struct { 240 value health.HealthStatus 241 } 242 243 func (h healthCheck) HealthStatus(ctx context.Context) health.HealthStatus { 244 return h.value 245 }