Test every endpoint for OPTIONS/MethodNotFound

pull/3885/head
Edd Steel 2018-02-17 17:33:38 -08:00
parent c5f0bb3711
commit f770f360e9
2 changed files with 102 additions and 110 deletions

View File

@ -3,94 +3,96 @@ package agent
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"net/http/httptest"
"strings" "strings"
"testing" "testing"
"github.com/hashicorp/consul/logger" "github.com/hashicorp/consul/logger"
) )
func TestHTTPAPI_MethodNotAllowed_OSS(t *testing.T) { var expectedEndpoints = []struct {
tests := []struct {
methods, uri string methods, uri string
}{ }{
{"PUT", "/v1/acl/bootstrap"}, {"OPTIONS,PUT", "/v1/acl/bootstrap"},
{"PUT", "/v1/acl/create"}, {"OPTIONS,PUT", "/v1/acl/create"},
{"PUT", "/v1/acl/update"}, {"OPTIONS,PUT", "/v1/acl/update"},
{"PUT", "/v1/acl/destroy/"}, {"OPTIONS,PUT", "/v1/acl/destroy/"},
{"GET", "/v1/acl/info/"}, {"OPTIONS,GET", "/v1/acl/info/"},
{"PUT", "/v1/acl/clone/"}, {"OPTIONS,PUT", "/v1/acl/clone/"},
{"GET", "/v1/acl/list"}, {"OPTIONS,GET", "/v1/acl/list"},
{"GET", "/v1/acl/replication"}, {"OPTIONS,GET", "/v1/acl/replication"},
{"PUT", "/v1/agent/token/"}, {"OPTIONS,PUT", "/v1/agent/token/"},
{"GET", "/v1/agent/self"}, {"OPTIONS,GET", "/v1/agent/self"},
{"GET", "/v1/agent/members"}, {"OPTIONS,GET", "/v1/agent/members"},
{"PUT", "/v1/agent/check/deregister/"}, {"OPTIONS,PUT", "/v1/agent/check/deregister/"},
{"PUT", "/v1/agent/check/fail/"}, {"OPTIONS,PUT", "/v1/agent/check/fail/"},
{"PUT", "/v1/agent/check/pass/"}, {"OPTIONS,PUT", "/v1/agent/check/pass/"},
{"PUT", "/v1/agent/check/register"}, {"OPTIONS,PUT", "/v1/agent/check/register"},
{"PUT", "/v1/agent/check/update/"}, {"OPTIONS,PUT", "/v1/agent/check/update/"},
{"PUT", "/v1/agent/check/warn/"}, {"OPTIONS,PUT", "/v1/agent/check/warn/"},
{"GET", "/v1/agent/checks"}, {"OPTIONS,GET", "/v1/agent/checks"},
{"PUT", "/v1/agent/force-leave/"}, {"OPTIONS,PUT", "/v1/agent/force-leave/"},
{"PUT", "/v1/agent/join/"}, {"OPTIONS,PUT", "/v1/agent/join/"},
{"PUT", "/v1/agent/leave"}, {"OPTIONS,PUT", "/v1/agent/leave"},
{"PUT", "/v1/agent/maintenance"}, {"OPTIONS,PUT", "/v1/agent/maintenance"},
{"GET", "/v1/agent/metrics"}, {"OPTIONS,GET", "/v1/agent/metrics"},
// {"GET", "/v1/agent/monitor"}, // requires LogWriter. Hangs if LogWriter is provided // {"GET", "/v1/agent/monitor"}, // requires LogWriter. Hangs if LogWriter is provided
{"PUT", "/v1/agent/reload"}, {"OPTIONS,PUT", "/v1/agent/reload"},
{"PUT", "/v1/agent/service/deregister/"}, {"OPTIONS,PUT", "/v1/agent/service/deregister/"},
{"PUT", "/v1/agent/service/maintenance/"}, {"OPTIONS,PUT", "/v1/agent/service/maintenance/"},
{"PUT", "/v1/agent/service/register"}, {"OPTIONS,PUT", "/v1/agent/service/register"},
{"GET", "/v1/agent/services"}, {"OPTIONS,GET", "/v1/agent/services"},
{"GET", "/v1/catalog/datacenters"}, {"OPTIONS,GET", "/v1/catalog/datacenters"},
{"PUT", "/v1/catalog/deregister"}, {"OPTIONS,PUT", "/v1/catalog/deregister"},
{"GET", "/v1/catalog/node/"}, {"OPTIONS,GET", "/v1/catalog/node/"},
{"GET", "/v1/catalog/nodes"}, {"OPTIONS,GET", "/v1/catalog/nodes"},
{"PUT", "/v1/catalog/register"}, {"OPTIONS,PUT", "/v1/catalog/register"},
{"GET", "/v1/catalog/service/"}, {"OPTIONS,GET", "/v1/catalog/service/"},
{"GET", "/v1/catalog/services"}, {"OPTIONS,GET", "/v1/catalog/services"},
{"GET", "/v1/coordinate/datacenters"}, {"OPTIONS,GET", "/v1/coordinate/datacenters"},
{"GET", "/v1/coordinate/nodes"}, {"OPTIONS,GET", "/v1/coordinate/nodes"},
{"GET", "/v1/coordinate/node/"}, {"OPTIONS,GET", "/v1/coordinate/node/"},
{"PUT", "/v1/event/fire/"}, {"OPTIONS,PUT", "/v1/event/fire/"},
{"GET", "/v1/event/list"}, {"OPTIONS,GET", "/v1/event/list"},
{"GET", "/v1/health/checks/"}, {"OPTIONS,GET", "/v1/health/checks/"},
{"GET", "/v1/health/node/"}, {"OPTIONS,GET", "/v1/health/node/"},
{"GET", "/v1/health/service/"}, {"OPTIONS,GET", "/v1/health/service/"},
{"GET", "/v1/health/state/"}, {"OPTIONS,GET", "/v1/health/state/"},
{"GET", "/v1/internal/ui/node/"}, {"OPTIONS,GET", "/v1/internal/ui/node/"},
{"GET", "/v1/internal/ui/nodes"}, {"OPTIONS,GET", "/v1/internal/ui/nodes"},
{"GET", "/v1/internal/ui/services"}, {"OPTIONS,GET", "/v1/internal/ui/services"},
{"GET PUT DELETE", "/v1/kv/"}, {"OPTIONS,GET,PUT,DELETE", "/v1/kv/"},
{"GET PUT", "/v1/operator/autopilot/configuration"}, {"OPTIONS,GET,PUT", "/v1/operator/autopilot/configuration"},
{"GET", "/v1/operator/autopilot/health"}, {"OPTIONS,GET", "/v1/operator/autopilot/health"},
{"GET POST PUT DELETE", "/v1/operator/keyring"}, {"OPTIONS,GET,POST,PUT,DELETE", "/v1/operator/keyring"},
{"GET", "/v1/operator/raft/configuration"}, {"OPTIONS,GET", "/v1/operator/raft/configuration"},
{"DELETE", "/v1/operator/raft/peer"}, {"OPTIONS,DELETE", "/v1/operator/raft/peer"},
{"GET POST", "/v1/query"}, {"OPTIONS,GET,POST", "/v1/query"},
{"GET PUT DELETE", "/v1/query/"}, {"OPTIONS,GET,PUT,DELETE", "/v1/query/"},
{"GET", "/v1/query/xxx/execute"}, {"OPTIONS,GET", "/v1/query/xxx/execute"},
{"GET", "/v1/query/xxx/explain"}, {"OPTIONS,GET", "/v1/query/xxx/explain"},
{"PUT", "/v1/session/create"}, {"OPTIONS,PUT", "/v1/session/create"},
{"PUT", "/v1/session/destroy/"}, {"OPTIONS,PUT", "/v1/session/destroy/"},
{"GET", "/v1/session/info/"}, {"OPTIONS,GET", "/v1/session/info/"},
{"GET", "/v1/session/list"}, {"OPTIONS,GET", "/v1/session/list"},
{"GET", "/v1/session/node/"}, {"OPTIONS,GET", "/v1/session/node/"},
{"PUT", "/v1/session/renew/"}, {"OPTIONS,PUT", "/v1/session/renew/"},
{"GET PUT", "/v1/snapshot"}, {"OPTIONS,GET,PUT", "/v1/snapshot"},
{"GET", "/v1/status/leader"}, {"OPTIONS,GET", "/v1/status/leader"},
// {"GET", "/v1/status/peers"},// hangs // {"GET", "/v1/status/peers"},// hangs
{"PUT", "/v1/txn"}, {"OPTIONS,PUT", "/v1/txn"},
} }
func TestHTTPAPI_MethodNotAllowed_OSS(t *testing.T) {
a := NewTestAgent(t.Name(), `acl_datacenter = "dc1"`) a := NewTestAgent(t.Name(), `acl_datacenter = "dc1"`)
a.Agent.LogWriter = logger.NewLogWriter(512) a.Agent.LogWriter = logger.NewLogWriter(512)
defer a.Shutdown() defer a.Shutdown()
all := []string{"GET", "PUT", "POST", "DELETE", "HEAD"} all := []string{"GET", "PUT", "POST", "DELETE", "HEAD", "OPTIONS"}
client := http.Client{} client := http.Client{}
for _, tt := range tests { for _, tt := range expectedEndpoints {
for _, m := range all { for _, m := range all {
t.Run(m+" "+tt.uri, func(t *testing.T) { t.Run(m+" "+tt.uri, func(t *testing.T) {
uri := fmt.Sprintf("http://%s%s", a.HTTPAddr(), tt.uri) uri := fmt.Sprintf("http://%s%s", a.HTTPAddr(), tt.uri)
@ -111,3 +113,29 @@ func TestHTTPAPI_MethodNotAllowed_OSS(t *testing.T) {
} }
} }
} }
func TestHTTPAPI_OptionMethod_OSS(t *testing.T) {
a := NewTestAgent(t.Name(), `acl_datacenter = "dc1"`)
a.Agent.LogWriter = logger.NewLogWriter(512)
defer a.Shutdown()
for _, tt := range expectedEndpoints {
t.Run("OPTIONS "+tt.uri, func(t *testing.T) {
uri := fmt.Sprintf("http://%s%s", a.HTTPAddr(), tt.uri)
req, _ := http.NewRequest("OPTIONS", uri, nil)
resp := httptest.NewRecorder()
a.srv.Handler.ServeHTTP(resp, req)
if resp.Code != http.StatusOK {
t.Fatalf("options request: got status code %d want %d", resp.Code, http.StatusOK)
}
optionsStr := resp.Header().Get("Allow")
if optionsStr == "" {
t.Fatalf("options request: got empty 'Allow' header")
} else if optionsStr != tt.methods {
t.Fatalf("options request: got 'Allow' header value of %s want %s", optionsStr, tt.methods)
}
})
}
}

View File

@ -714,42 +714,6 @@ func TestEnableWebUI(t *testing.T) {
} }
} }
func TestHttpOptions(t *testing.T) {
req, _ := http.NewRequest("OPTIONS", "/v1/query", nil)
resp := httptest.NewRecorder()
a := NewTestAgent(t.Name(), "")
a.srv.Handler.ServeHTTP(resp, req)
// Check the result
if resp.Code != 200 {
t.Fatalf("should handle options method")
}
optionsStr := resp.Header().Get("Allow")
if optionsStr == "" {
t.Fatalf("options method should set 'Allow' header")
} else if optionsStr != "OPTIONS,GET,POST" {
t.Fatalf("options method should set 'Allow' header correctly")
}
}
func TestMethodNotAllowed(t *testing.T) {
req, _ := http.NewRequest("PATH", "/v1/kv/", nil)
resp := httptest.NewRecorder()
a := NewTestAgent(t.Name(), "")
a.srv.Handler.ServeHTTP(resp, req)
if resp.Code != 405 {
t.Fatalf("should reject an unsupported method with 405")
}
optionsStr := resp.Header().Get("Allow")
if optionsStr == "" {
t.Fatalf("405 error should set 'Allow' header")
} else if optionsStr != "OPTIONS,GET,PUT,DELETE" {
t.Fatalf("options method should set 'Allow' header correctly")
}
}
// assertIndex tests that X-Consul-Index is set and non-zero // assertIndex tests that X-Consul-Index is set and non-zero
func assertIndex(t *testing.T, resp *httptest.ResponseRecorder) { func assertIndex(t *testing.T, resp *httptest.ResponseRecorder) {
header := resp.Header().Get("X-Consul-Index") header := resp.Header().Get("X-Consul-Index")