...

Source file src/github.com/docker/distribution/registry/handlers/app_test.go

Documentation: github.com/docker/distribution/registry/handlers

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

View as plain text