diff --git a/command/agent/catalog_endpoint_test.go b/command/agent/catalog_endpoint_test.go index 8ee5ff828a..03928aaf93 100644 --- a/command/agent/catalog_endpoint_test.go +++ b/command/agent/catalog_endpoint_test.go @@ -1,6 +1,7 @@ package agent import ( + "fmt" "github.com/hashicorp/consul/consul/structs" "net/http" "os" @@ -129,6 +130,66 @@ func TestCatalogNodes(t *testing.T) { } } +func TestCatalogNodes_Blocking(t *testing.T) { + dir, srv := makeHTTPServer(t) + defer os.RemoveAll(dir) + defer srv.Shutdown() + defer srv.agent.Shutdown() + + // Wait for a leader + time.Sleep(100 * time.Millisecond) + + // Register node + args := &structs.DCSpecificRequest{ + Datacenter: "dc1", + } + var out structs.IndexedNodes + if err := srv.agent.RPC("Catalog.ListNodes", *args, &out); err != nil { + t.Fatalf("err: %v", err) + } + + // Do an update in a little while + start := time.Now() + go func() { + time.Sleep(100 * time.Millisecond) + args := &structs.RegisterRequest{ + Datacenter: "dc1", + Node: "foo", + Address: "127.0.0.1", + } + var out struct{} + if err := srv.agent.RPC("Catalog.Register", args, &out); err != nil { + t.Fatalf("err: %v", err) + } + }() + + // Do a blocking read + req, err := http.NewRequest("GET", + fmt.Sprintf("/v1/catalog/nodes?wait=60s&index=%d", out.Index), nil) + if err != nil { + t.Fatalf("err: %v", err) + } + + idx, obj, err := srv.CatalogNodes(nil, req) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Should block for a while + if time.Now().Sub(start) < 100*time.Millisecond { + t.Fatalf("too fast") + } + + if idx <= out.Index { + t.Fatalf("bad: %v", idx) + } + + nodes := obj.(structs.Nodes) + if len(nodes) != 2 { + t.Fatalf("bad: %v", obj) + } +} + func TestCatalogServices(t *testing.T) { dir, srv := makeHTTPServer(t) defer os.RemoveAll(dir) diff --git a/command/agent/http_test.go b/command/agent/http_test.go index 97117003db..387e6fb6fa 100644 --- a/command/agent/http_test.go +++ b/command/agent/http_test.go @@ -3,9 +3,13 @@ package agent import ( "bytes" "encoding/json" + "github.com/hashicorp/consul/consul/structs" "io" "io/ioutil" + "net/http" + "net/http/httptest" "testing" + "time" ) func makeHTTPServer(t *testing.T) (string, *HTTPServer) { @@ -24,3 +28,72 @@ func encodeReq(obj interface{}) io.ReadCloser { enc.Encode(obj) return ioutil.NopCloser(buf) } + +func TestSetIndex(t *testing.T) { + resp := httptest.NewRecorder() + setIndex(resp, 1000) + header := resp.Header().Get("X-Consul-Index") + if header != "1000" { + t.Fatalf("Bad: %v", header) + } +} + +func TestParseWait(t *testing.T) { + resp := httptest.NewRecorder() + var b structs.BlockingQuery + + req, err := http.NewRequest("GET", + "/v1/catalog/nodes?wait=60s&index=1000", nil) + if err != nil { + t.Fatalf("err: %v", err) + } + + if d := parseWait(resp, req, &b); d { + t.Fatalf("unexpected done") + } + + if b.MinQueryIndex != 1000 { + t.Fatalf("Bad: %v", b) + } + if b.MaxQueryTime != 60*time.Second { + t.Fatalf("Bad: %v", b) + } +} + +func TestParseWait_InvalidTime(t *testing.T) { + resp := httptest.NewRecorder() + var b structs.BlockingQuery + + req, err := http.NewRequest("GET", + "/v1/catalog/nodes?wait=60foo&index=1000", nil) + if err != nil { + t.Fatalf("err: %v", err) + } + + if d := parseWait(resp, req, &b); !d { + t.Fatalf("expected done") + } + + if resp.Code != 400 { + t.Fatalf("bad code: %v", resp.Code) + } +} + +func TestParseWait_InvalidIndex(t *testing.T) { + resp := httptest.NewRecorder() + var b structs.BlockingQuery + + req, err := http.NewRequest("GET", + "/v1/catalog/nodes?wait=60s&index=foo", nil) + if err != nil { + t.Fatalf("err: %v", err) + } + + if d := parseWait(resp, req, &b); !d { + t.Fatalf("expected done") + } + + if resp.Code != 400 { + t.Fatalf("bad code: %v", resp.Code) + } +}