From 1a844d06238368c9d5c946a34c0c52485de1c435 Mon Sep 17 00:00:00 2001 From: Tim Pierce Date: Fri, 9 May 2014 02:49:51 -0400 Subject: [PATCH] 2328: handler_test.go refactoring Refactoring to make tests easier to read and understand. --- services/keep/src/keep/handler_test.go | 346 ++++++++++++++++--------- 1 file changed, 221 insertions(+), 125 deletions(-) diff --git a/services/keep/src/keep/handler_test.go b/services/keep/src/keep/handler_test.go index a45ab4338f..9455a8a80d 100644 --- a/services/keep/src/keep/handler_test.go +++ b/services/keep/src/keep/handler_test.go @@ -11,6 +11,7 @@ package main import ( "bytes" + "github.com/gorilla/mux" "net/http" "net/http/httptest" "regexp" @@ -18,6 +19,22 @@ import ( "time" ) +// A RequestTester represents the parameters for an HTTP request to +// be issued on behalf of a unit test. +type RequestTester struct { + uri string + api_token string + method string + request_body []byte +} + +// Test GetBlockHandler on the following situations: +// - permissions off, unauthenticated request, unsigned locator +// - permissions on, authenticated request, signed locator +// - permissions on, authenticated request, unsigned locator +// - permissions on, unauthenticated request, signed locator +// - permissions on, authenticated request, expired locator +// func TestGetHandler(t *testing.T) { defer teardown() @@ -33,59 +50,77 @@ func TestGetHandler(t *testing.T) { // Set up a REST router for testing the handlers. rest := NewRESTRouter() - // Test an unsigned GET request. - test_url := "http://localhost:25107/" + TEST_HASH - req, _ := http.NewRequest("GET", test_url, nil) - resp := httptest.NewRecorder() - rest.ServeHTTP(resp, req) + // ----------------- + // Permissions: off. - ExpectStatusCode(t, "unsigned GET", resp, http.StatusOK) - ExpectBody(t, "unsigned GET", resp, string(TEST_BLOCK)) + // Unauthenticated request, unsigned locator + // => OK + unsigned_locator := "http://localhost:25107/" + TEST_HASH + response := IssueRequest(rest, + &RequestTester{ + method: "GET", + uri: unsigned_locator, + }) + ExpectStatusCode(t, "unsigned GET (permissions off)", http.StatusOK, response) + ExpectBody(t, "unsigned GET (permissions off)", string(TEST_BLOCK), response) - // Enable permissions. + // ---------------- + // Permissions: on. + + // Create signed and expired locators for testing. enforce_permissions = true PermissionSecret = []byte(known_key) - permission_ttl = 300 - expiry := time.Now().Add(time.Duration(permission_ttl) * time.Second) - - // Test GET with a signed locator. - test_url = "http://localhost:25107/" + SignLocator(TEST_HASH, known_token, expiry) - resp = httptest.NewRecorder() - req, _ = http.NewRequest("GET", test_url, nil) - req.Header.Set("Authorization", "OAuth "+known_token) - rest.ServeHTTP(resp, req) - - ExpectStatusCode(t, "signed GET", resp, http.StatusOK) - ExpectBody(t, "signed GET", resp, string(TEST_BLOCK)) - - // Test GET with an unsigned locator. - test_url = "http://localhost:25107/" + TEST_HASH - resp = httptest.NewRecorder() - req, _ = http.NewRequest("GET", test_url, nil) - req.Header.Set("Authorization", "OAuth "+known_token) - rest.ServeHTTP(resp, req) - - ExpectStatusCode(t, "unsigned locator", resp, PermissionError.HTTPCode) - - // Test GET with a signed locator and an unauthenticated request. - test_url = "http://localhost:25107/" + SignLocator(TEST_HASH, known_token, expiry) - resp = httptest.NewRecorder() - req, _ = http.NewRequest("GET", test_url, nil) - rest.ServeHTTP(resp, req) - - ExpectStatusCode(t, "signed locator", resp, PermissionError.HTTPCode) - - // Test GET with an expired, signed locator. - expired_ts := time.Now().Add(-time.Hour) - test_url = "http://localhost:25107/" + SignLocator(TEST_HASH, known_token, expired_ts) - resp = httptest.NewRecorder() - req, _ = http.NewRequest("GET", test_url, nil) - req.Header.Set("Authorization", "OAuth "+known_token) - rest.ServeHTTP(resp, req) - - ExpectStatusCode(t, "expired signature", resp, ExpiredError.HTTPCode) + permission_ttl = time.Duration(300) * time.Second + + var ( + expiration = time.Now().Add(permission_ttl) + expired_timestamp = time.Now().Add(-time.Hour) + signed_locator = "http://localhost:25107/" + SignLocator(TEST_HASH, known_token, expiration) + expired_locator = "http://localhost:25107/" + SignLocator(TEST_HASH, known_token, expired_timestamp) + ) + + // Authenticated request, signed locator + // => OK + response = IssueRequest(rest, &RequestTester{ + method: "GET", + uri: signed_locator, + api_token: known_token, + }) + ExpectStatusCode(t, "signed GET (permissions on)", http.StatusOK, response) + ExpectBody(t, "signed GET (permissions on)", string(TEST_BLOCK), response) + + // Authenticated request, unsigned locator + // => PermissionError + response = IssueRequest(rest, &RequestTester{ + method: "GET", + uri: unsigned_locator, + api_token: known_token, + }) + ExpectStatusCode(t, "unsigned locator", PermissionError.HTTPCode, response) + + // Unauthenticated request, signed locator + // => PermissionError + response = IssueRequest(rest, &RequestTester{ + method: "GET", + uri: signed_locator, + }) + ExpectStatusCode(t, "signed locator", PermissionError.HTTPCode, response) + + // Authenticated request, expired locator + // => ExpiredError + response = IssueRequest(rest, &RequestTester{ + method: "GET", + uri: expired_locator, + api_token: known_token, + }) + ExpectStatusCode(t, "expired signature", ExpiredError.HTTPCode, response) } +// Test PutBlockHandler on the following situations: +// - no server key +// - with server key, authenticated request, unsigned locator +// - with server key, unauthenticated request, unsigned locator +// func TestPutHandler(t *testing.T) { defer teardown() @@ -96,48 +131,71 @@ func TestPutHandler(t *testing.T) { // Set up a REST router for testing the handlers. rest := NewRESTRouter() - // Execute a PUT request. - test_url := "http://localhost:25107/" + TEST_HASH - test_body := bytes.NewReader(TEST_BLOCK) - req, _ := http.NewRequest("PUT", test_url, test_body) - resp := httptest.NewRecorder() - rest.ServeHTTP(resp, req) + // -------------- + // No server key. - ExpectStatusCode(t, "permissions off", resp, http.StatusOK) - ExpectBody(t, "permissions off", resp, TEST_HASH) + // Unauthenticated request, no server key + // => OK (unsigned response) + unsigned_locator := "http://localhost:25107/" + TEST_HASH + response := IssueRequest(rest, + &RequestTester{ + method: "PUT", + uri: unsigned_locator, + request_body: TEST_BLOCK, + }) + + ExpectStatusCode(t, + "unauthenticated PUT (no server key)", http.StatusOK, response) + ExpectBody(t, "unauthenticated PUT (no server key)", TEST_HASH, response) + + // ------------------ + // With a server key. - // Add a permission key. - // When a permission key is available, the locator returned - // from a PUT request will be signed. PermissionSecret = []byte(known_key) permission_ttl = time.Duration(300) * time.Second - // An authenticated PUT request returns a signed locator. - test_url = "http://localhost:25107/" + TEST_HASH - test_body = bytes.NewReader(TEST_BLOCK) - req, _ = http.NewRequest("PUT", test_url, test_body) - req.Header.Set("Authorization", "OAuth "+known_token) - resp = httptest.NewRecorder() - rest.ServeHTTP(resp, req) - - ExpectStatusCode(t, "authenticated PUT", resp, http.StatusOK) - if !VerifySignature(resp.Body.String(), known_token) { - t.Errorf("authenticated PUT: response '%s' failed signature check", - resp.Body.String()) + // When a permission key is available, the locator returned + // from an authenticated PUT request will be signed. + + // Authenticated PUT, signed locator + // => OK (signed response) + response = IssueRequest(rest, + &RequestTester{ + method: "PUT", + uri: unsigned_locator, + request_body: TEST_BLOCK, + api_token: known_token, + }) + + ExpectStatusCode(t, + "authenticated PUT (with server key)", http.StatusOK, response) + if !VerifySignature(response.Body.String(), known_token) { + t.Errorf("authenticated PUT (with server key): response '%s' does not contain a valid signature", + response.Body.String()) } - // An unauthenticated PUT request returns an unsigned locator - // even when a permission key is available. - test_url = "http://localhost:25107/" + TEST_HASH - test_body = bytes.NewReader(TEST_BLOCK) - req, _ = http.NewRequest("PUT", test_url, test_body) - resp = httptest.NewRecorder() - rest.ServeHTTP(resp, req) - - ExpectStatusCode(t, "anon PUT with server key", resp, http.StatusOK) - ExpectBody(t, "anon PUT with server key", resp, TEST_HASH) + // Unauthenticated PUT, unsigned locator + // => OK + response = IssueRequest(rest, + &RequestTester{ + method: "PUT", + uri: unsigned_locator, + request_body: TEST_BLOCK, + }) + + ExpectStatusCode(t, + "unauthenticated PUT (with server key)", http.StatusOK, response) + ExpectBody(t, + "unauthenticated PUT (with server key)", TEST_HASH, response) } +// Test /index requests: +// - unauthenticated /index/{prefix} request +// - unauthenticated /index request +// - authenticated /index request, non-superuser +// - authenticated /index request by superuser, enforce_permissions = false +// - authenticated /index request by superuser, enforce_permissions = true +// func TestIndexHandler(t *testing.T) { defer teardown() @@ -154,64 +212,102 @@ func TestIndexHandler(t *testing.T) { // Set up a REST router for testing the handlers. rest := NewRESTRouter() - // Requests for /index with a prefix are okay even if unauthenticated. - test_url := "http://localhost:25107/index/" + TEST_HASH[0:5] - req, _ := http.NewRequest("GET", test_url, nil) - resp := httptest.NewRecorder() - rest.ServeHTTP(resp, req) + // Unauthenticated /index/{prefix} + // => OK + response := IssueRequest(rest, + &RequestTester{ + method: "GET", + uri: "http://localhost:25107/index/" + TEST_HASH[0:5], + }) expected := `^` + TEST_HASH + `\+\d+ \d+\n$` - match, _ := regexp.MatchString(expected, resp.Body.String()) + match, _ := regexp.MatchString(expected, response.Body.String()) if !match { t.Errorf("IndexHandler expected: %s, returned:\n%s", - expected, resp.Body.String()) + expected, response.Body.String()) } - // Unauthenticated /index requests: fail. - test_url = "http://localhost:25107/index" - req, _ = http.NewRequest("GET", test_url, nil) - resp = httptest.NewRecorder() - rest.ServeHTTP(resp, req) - - ExpectStatusCode(t, "unauthenticated /index", resp, PermissionError.HTTPCode) - - // Authenticated /index requests by a non-superuser: also fail. - test_url = "http://localhost:25107/index" - req, _ = http.NewRequest("GET", test_url, nil) - req.Header.Set("Authorization", "OAuth "+known_token) - resp = httptest.NewRecorder() - rest.ServeHTTP(resp, req) - - ExpectStatusCode(t, "authenticated /index", resp, PermissionError.HTTPCode) - - // Even superuser /index requests fail if enforce_permissions is off! + // Unauthenticated /index + // => PermissionError + response = IssueRequest(rest, + &RequestTester{ + method: "GET", + uri: "http://localhost:25107/index", + }) + + ExpectStatusCode(t, + "unauthenticated /index", PermissionError.HTTPCode, response) + + // Authenticated /index request by non-superuser + // => PermissionError + response = IssueRequest(rest, + &RequestTester{ + method: "GET", + uri: "http://localhost:25107/index", + api_token: known_token, + }) + + ExpectStatusCode(t, + "authenticated /index by non-superuser", + PermissionError.HTTPCode, + response) + + // Authenticated /index request by superuser, enforce_permissions = false + // => PermissionError enforce_permissions = false data_manager_token = "DATA MANAGER TOKEN" - test_url = "http://localhost:25107/index" - req, _ = http.NewRequest("GET", test_url, nil) - req.Header.Set("Authorization", "OAuth "+data_manager_token) - resp = httptest.NewRecorder() - rest.ServeHTTP(resp, req) - ExpectStatusCode(t, "superuser /index (permissions off)", resp, PermissionError.HTTPCode) + response = IssueRequest(rest, + &RequestTester{ + method: "GET", + uri: "http://localhost:25107/index", + api_token: data_manager_token, + }) - // Superuser /index requests with enforce_permissions set: succeed! + ExpectStatusCode(t, + "authenticated /index request by superuser (permissions off)", + PermissionError.HTTPCode, + response) + + // Authenticated /index request by superuser, enforce_permissions = true + // => OK enforce_permissions = true - data_manager_token = "DATA MANAGER TOKEN" - test_url = "http://localhost:25107/index" - req, _ = http.NewRequest("GET", test_url, nil) - req.Header.Set("Authorization", "OAuth "+data_manager_token) - resp = httptest.NewRecorder() - rest.ServeHTTP(resp, req) + response = IssueRequest(rest, + &RequestTester{ + method: "GET", + uri: "http://localhost:25107/index", + api_token: data_manager_token, + }) + + ExpectStatusCode(t, + "authenticated /index request by superuser (permissions on)", + http.StatusOK, + response) - ExpectStatusCode(t, "superuser /index (permissions on)", resp, http.StatusOK) expected = `^` + TEST_HASH + `\+\d+ \d+\n` + TEST_HASH_2 + `\+\d+ \d+\n$` - match, _ = regexp.MatchString(expected, resp.Body.String()) + match, _ = regexp.MatchString(expected, response.Body.String()) if !match { t.Errorf("superuser /index: expected %s, got:\n%s", - expected, resp.Body.String()) + expected, response.Body.String()) + } +} + +// ==================== +// Helper functions +// ==================== + +// IssueTestRequest executes an HTTP request described by rt, to a +// specified REST router. It returns the HTTP response to the request. +func IssueRequest(router *mux.Router, rt *RequestTester) *httptest.ResponseRecorder { + response := httptest.NewRecorder() + body := bytes.NewReader(rt.request_body) + req, _ := http.NewRequest(rt.method, rt.uri, body) + if rt.api_token != "" { + req.Header.Set("Authorization", "OAuth "+rt.api_token) } + router.ServeHTTP(response, req) + return response } // ExpectStatusCode checks whether a response has the specified status code, @@ -219,8 +315,8 @@ func TestIndexHandler(t *testing.T) { func ExpectStatusCode( t *testing.T, testname string, - response *httptest.ResponseRecorder, - expected_status int) { + expected_status int, + response *httptest.ResponseRecorder) { if response.Code != expected_status { t.Errorf("%s: expected status %s, got %+v", testname, expected_status, response) @@ -230,8 +326,8 @@ func ExpectStatusCode( func ExpectBody( t *testing.T, testname string, - response *httptest.ResponseRecorder, - expected_body string) { + expected_body string, + response *httptest.ResponseRecorder) { if response.Body.String() != expected_body { t.Errorf("%s: expected response body '%s', got %+v", testname, expected_body, response) -- 2.39.5