github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+incompatible/registry/handlers/app_test.go (about) 1 package handlers 2 3 import ( 4 "encoding/json" 5 "net/http" 6 "net/http/httptest" 7 "net/url" 8 "reflect" 9 "testing" 10 11 "github.com/docker/distribution/configuration" 12 "github.com/docker/distribution/context" 13 "github.com/docker/distribution/registry/api/errcode" 14 "github.com/docker/distribution/registry/api/v2" 15 "github.com/docker/distribution/registry/auth" 16 _ "github.com/docker/distribution/registry/auth/silly" 17 "github.com/docker/distribution/registry/storage" 18 memorycache "github.com/docker/distribution/registry/storage/cache/memory" 19 "github.com/docker/distribution/registry/storage/driver/inmemory" 20 ) 21 22 // TestAppDispatcher builds an application with a test dispatcher and ensures 23 // that requests are properly dispatched and the handlers are constructed. 24 // This only tests the dispatch mechanism. The underlying dispatchers must be 25 // tested individually. 26 func TestAppDispatcher(t *testing.T) { 27 driver := inmemory.New() 28 ctx := context.Background() 29 registry, err := storage.NewRegistry(ctx, driver, storage.BlobDescriptorCacheProvider(memorycache.NewInMemoryBlobDescriptorCacheProvider()), storage.EnableDelete, storage.EnableRedirect) 30 if err != nil { 31 t.Fatalf("error creating registry: %v", err) 32 } 33 app := &App{ 34 Config: &configuration.Configuration{}, 35 Context: ctx, 36 router: v2.Router(), 37 driver: driver, 38 registry: registry, 39 } 40 server := httptest.NewServer(app) 41 router := v2.Router() 42 43 serverURL, err := url.Parse(server.URL) 44 if err != nil { 45 t.Fatalf("error parsing server url: %v", err) 46 } 47 48 varCheckingDispatcher := func(expectedVars map[string]string) dispatchFunc { 49 return func(ctx *Context, r *http.Request) http.Handler { 50 // Always checks the same name context 51 if ctx.Repository.Name() != getName(ctx) { 52 t.Fatalf("unexpected name: %q != %q", ctx.Repository.Name(), "foo/bar") 53 } 54 55 // Check that we have all that is expected 56 for expectedK, expectedV := range expectedVars { 57 if ctx.Value(expectedK) != expectedV { 58 t.Fatalf("unexpected %s in context vars: %q != %q", expectedK, ctx.Value(expectedK), expectedV) 59 } 60 } 61 62 // Check that we only have variables that are expected 63 for k, v := range ctx.Value("vars").(map[string]string) { 64 _, ok := expectedVars[k] 65 66 if !ok { // name is checked on context 67 // We have an unexpected key, fail 68 t.Fatalf("unexpected key %q in vars with value %q", k, v) 69 } 70 } 71 72 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 73 w.WriteHeader(http.StatusOK) 74 }) 75 } 76 } 77 78 // unflatten a list of variables, suitable for gorilla/mux, to a map[string]string 79 unflatten := func(vars []string) map[string]string { 80 m := make(map[string]string) 81 for i := 0; i < len(vars)-1; i = i + 2 { 82 m[vars[i]] = vars[i+1] 83 } 84 85 return m 86 } 87 88 for _, testcase := range []struct { 89 endpoint string 90 vars []string 91 }{ 92 { 93 endpoint: v2.RouteNameManifest, 94 vars: []string{ 95 "name", "foo/bar", 96 "reference", "sometag", 97 }, 98 }, 99 { 100 endpoint: v2.RouteNameTags, 101 vars: []string{ 102 "name", "foo/bar", 103 }, 104 }, 105 { 106 endpoint: v2.RouteNameBlobUpload, 107 vars: []string{ 108 "name", "foo/bar", 109 }, 110 }, 111 { 112 endpoint: v2.RouteNameBlobUploadChunk, 113 vars: []string{ 114 "name", "foo/bar", 115 "uuid", "theuuid", 116 }, 117 }, 118 } { 119 app.register(testcase.endpoint, varCheckingDispatcher(unflatten(testcase.vars))) 120 route := router.GetRoute(testcase.endpoint).Host(serverURL.Host) 121 u, err := route.URL(testcase.vars...) 122 123 if err != nil { 124 t.Fatal(err) 125 } 126 127 resp, err := http.Get(u.String()) 128 129 if err != nil { 130 t.Fatal(err) 131 } 132 133 if resp.StatusCode != http.StatusOK { 134 t.Fatalf("unexpected status code: %v != %v", resp.StatusCode, http.StatusOK) 135 } 136 } 137 } 138 139 // TestNewApp covers the creation of an application via NewApp with a 140 // configuration. 141 func TestNewApp(t *testing.T) { 142 ctx := context.Background() 143 config := configuration.Configuration{ 144 Storage: configuration.Storage{ 145 "inmemory": nil, 146 }, 147 Auth: configuration.Auth{ 148 // For now, we simply test that new auth results in a viable 149 // application. 150 "silly": { 151 "realm": "realm-test", 152 "service": "service-test", 153 }, 154 }, 155 } 156 157 // Mostly, with this test, given a sane configuration, we are simply 158 // ensuring that NewApp doesn't panic. We might want to tweak this 159 // behavior. 160 app := NewApp(ctx, &config) 161 162 server := httptest.NewServer(app) 163 builder, err := v2.NewURLBuilderFromString(server.URL) 164 if err != nil { 165 t.Fatalf("error creating urlbuilder: %v", err) 166 } 167 168 baseURL, err := builder.BuildBaseURL() 169 if err != nil { 170 t.Fatalf("error creating baseURL: %v", err) 171 } 172 173 // TODO(stevvooe): The rest of this test might belong in the API tests. 174 175 // Just hit the app and make sure we get a 401 Unauthorized error. 176 req, err := http.Get(baseURL) 177 if err != nil { 178 t.Fatalf("unexpected error during GET: %v", err) 179 } 180 defer req.Body.Close() 181 182 if req.StatusCode != http.StatusUnauthorized { 183 t.Fatalf("unexpected status code during request: %v", err) 184 } 185 186 if req.Header.Get("Content-Type") != "application/json; charset=utf-8" { 187 t.Fatalf("unexpected content-type: %v != %v", req.Header.Get("Content-Type"), "application/json; charset=utf-8") 188 } 189 190 expectedAuthHeader := "Bearer realm=\"realm-test\",service=\"service-test\"" 191 if e, a := expectedAuthHeader, req.Header.Get("WWW-Authenticate"); e != a { 192 t.Fatalf("unexpected WWW-Authenticate header: %q != %q", e, a) 193 } 194 195 var errs errcode.Errors 196 dec := json.NewDecoder(req.Body) 197 if err := dec.Decode(&errs); err != nil { 198 t.Fatalf("error decoding error response: %v", err) 199 } 200 201 err2, ok := errs[0].(errcode.ErrorCoder) 202 if !ok { 203 t.Fatalf("not an ErrorCoder: %#v", errs[0]) 204 } 205 if err2.ErrorCode() != errcode.ErrorCodeUnauthorized { 206 t.Fatalf("unexpected error code: %v != %v", err2.ErrorCode(), errcode.ErrorCodeUnauthorized) 207 } 208 } 209 210 // Test the access record accumulator 211 func TestAppendAccessRecords(t *testing.T) { 212 repo := "testRepo" 213 214 expectedResource := auth.Resource{ 215 Type: "repository", 216 Name: repo, 217 } 218 219 expectedPullRecord := auth.Access{ 220 Resource: expectedResource, 221 Action: "pull", 222 } 223 expectedPushRecord := auth.Access{ 224 Resource: expectedResource, 225 Action: "push", 226 } 227 expectedAllRecord := auth.Access{ 228 Resource: expectedResource, 229 Action: "*", 230 } 231 232 records := []auth.Access{} 233 result := appendAccessRecords(records, "GET", repo) 234 expectedResult := []auth.Access{expectedPullRecord} 235 if ok := reflect.DeepEqual(result, expectedResult); !ok { 236 t.Fatalf("Actual access record differs from expected") 237 } 238 239 records = []auth.Access{} 240 result = appendAccessRecords(records, "HEAD", repo) 241 expectedResult = []auth.Access{expectedPullRecord} 242 if ok := reflect.DeepEqual(result, expectedResult); !ok { 243 t.Fatalf("Actual access record differs from expected") 244 } 245 246 records = []auth.Access{} 247 result = appendAccessRecords(records, "POST", repo) 248 expectedResult = []auth.Access{expectedPullRecord, expectedPushRecord} 249 if ok := reflect.DeepEqual(result, expectedResult); !ok { 250 t.Fatalf("Actual access record differs from expected") 251 } 252 253 records = []auth.Access{} 254 result = appendAccessRecords(records, "PUT", repo) 255 expectedResult = []auth.Access{expectedPullRecord, expectedPushRecord} 256 if ok := reflect.DeepEqual(result, expectedResult); !ok { 257 t.Fatalf("Actual access record differs from expected") 258 } 259 260 records = []auth.Access{} 261 result = appendAccessRecords(records, "PATCH", repo) 262 expectedResult = []auth.Access{expectedPullRecord, expectedPushRecord} 263 if ok := reflect.DeepEqual(result, expectedResult); !ok { 264 t.Fatalf("Actual access record differs from expected") 265 } 266 267 records = []auth.Access{} 268 result = appendAccessRecords(records, "DELETE", repo) 269 expectedResult = []auth.Access{expectedAllRecord} 270 if ok := reflect.DeepEqual(result, expectedResult); !ok { 271 t.Fatalf("Actual access record differs from expected") 272 } 273 274 }