1331 lines
35 KiB
Go
1331 lines
35 KiB
Go
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
|
// Use of this source code is governed by a MIT-license.
|
|
// See http://olivere.mit-license.org/license.txt for details.
|
|
|
|
package elastic
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"reflect"
|
|
"regexp"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/fortytw2/leaktest"
|
|
)
|
|
|
|
func findConn(s string, slice ...*conn) (int, bool) {
|
|
for i, t := range slice {
|
|
if s == t.URL() {
|
|
return i, true
|
|
}
|
|
}
|
|
return -1, false
|
|
}
|
|
|
|
// -- NewClient --
|
|
|
|
func TestClientDefaults(t *testing.T) {
|
|
client, err := NewClient()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if client.healthcheckEnabled != true {
|
|
t.Errorf("expected health checks to be enabled, got: %v", client.healthcheckEnabled)
|
|
}
|
|
if client.healthcheckTimeoutStartup != DefaultHealthcheckTimeoutStartup {
|
|
t.Errorf("expected health checks timeout on startup = %v, got: %v", DefaultHealthcheckTimeoutStartup, client.healthcheckTimeoutStartup)
|
|
}
|
|
if client.healthcheckTimeout != DefaultHealthcheckTimeout {
|
|
t.Errorf("expected health checks timeout = %v, got: %v", DefaultHealthcheckTimeout, client.healthcheckTimeout)
|
|
}
|
|
if client.healthcheckInterval != DefaultHealthcheckInterval {
|
|
t.Errorf("expected health checks interval = %v, got: %v", DefaultHealthcheckInterval, client.healthcheckInterval)
|
|
}
|
|
if client.snifferEnabled != true {
|
|
t.Errorf("expected sniffing to be enabled, got: %v", client.snifferEnabled)
|
|
}
|
|
if client.snifferTimeoutStartup != DefaultSnifferTimeoutStartup {
|
|
t.Errorf("expected sniffer timeout on startup = %v, got: %v", DefaultSnifferTimeoutStartup, client.snifferTimeoutStartup)
|
|
}
|
|
if client.snifferTimeout != DefaultSnifferTimeout {
|
|
t.Errorf("expected sniffer timeout = %v, got: %v", DefaultSnifferTimeout, client.snifferTimeout)
|
|
}
|
|
if client.snifferInterval != DefaultSnifferInterval {
|
|
t.Errorf("expected sniffer interval = %v, got: %v", DefaultSnifferInterval, client.snifferInterval)
|
|
}
|
|
if client.basicAuth != false {
|
|
t.Errorf("expected no basic auth; got: %v", client.basicAuth)
|
|
}
|
|
if client.basicAuthUsername != "" {
|
|
t.Errorf("expected no basic auth username; got: %q", client.basicAuthUsername)
|
|
}
|
|
if client.basicAuthPassword != "" {
|
|
t.Errorf("expected no basic auth password; got: %q", client.basicAuthUsername)
|
|
}
|
|
if client.sendGetBodyAs != "GET" {
|
|
t.Errorf("expected sendGetBodyAs to be GET; got: %q", client.sendGetBodyAs)
|
|
}
|
|
}
|
|
|
|
func TestClientWithoutURL(t *testing.T) {
|
|
client, err := NewClient()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Two things should happen here:
|
|
// 1. The client starts sniffing the cluster on DefaultURL
|
|
// 2. The sniffing process should find (at least) one node in the cluster, i.e. the DefaultURL
|
|
if len(client.conns) == 0 {
|
|
t.Fatalf("expected at least 1 node in the cluster, got: %d (%v)", len(client.conns), client.conns)
|
|
}
|
|
if !isTravis() {
|
|
if _, found := findConn(DefaultURL, client.conns...); !found {
|
|
t.Errorf("expected to find node with default URL of %s in %v", DefaultURL, client.conns)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestClientWithSingleURL(t *testing.T) {
|
|
client, err := NewClient(SetURL("http://127.0.0.1:9200"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Two things should happen here:
|
|
// 1. The client starts sniffing the cluster on DefaultURL
|
|
// 2. The sniffing process should find (at least) one node in the cluster, i.e. the DefaultURL
|
|
if len(client.conns) == 0 {
|
|
t.Fatalf("expected at least 1 node in the cluster, got: %d (%v)", len(client.conns), client.conns)
|
|
}
|
|
if !isTravis() {
|
|
if _, found := findConn(DefaultURL, client.conns...); !found {
|
|
t.Errorf("expected to find node with default URL of %s in %v", DefaultURL, client.conns)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestClientWithMultipleURLs(t *testing.T) {
|
|
client, err := NewClient(SetURL("http://127.0.0.1:9200", "http://127.0.0.1:9201"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// The client should sniff both URLs, but only 127.0.0.1:9200 should return nodes.
|
|
if len(client.conns) != 1 {
|
|
t.Fatalf("expected exactly 1 node in the local cluster, got: %d (%v)", len(client.conns), client.conns)
|
|
}
|
|
if !isTravis() {
|
|
if client.conns[0].URL() != DefaultURL {
|
|
t.Errorf("expected to find node with default URL of %s in %v", DefaultURL, client.conns)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestClientWithBasicAuth(t *testing.T) {
|
|
client, err := NewClient(SetBasicAuth("user", "secret"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if client.basicAuth != true {
|
|
t.Errorf("expected basic auth; got: %v", client.basicAuth)
|
|
}
|
|
if got, want := client.basicAuthUsername, "user"; got != want {
|
|
t.Errorf("expected basic auth username %q; got: %q", want, got)
|
|
}
|
|
if got, want := client.basicAuthPassword, "secret"; got != want {
|
|
t.Errorf("expected basic auth password %q; got: %q", want, got)
|
|
}
|
|
}
|
|
|
|
func TestClientWithBasicAuthInUserInfo(t *testing.T) {
|
|
client, err := NewClient(SetURL("http://user1:secret1@localhost:9200", "http://user2:secret2@localhost:9200"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if client.basicAuth != true {
|
|
t.Errorf("expected basic auth; got: %v", client.basicAuth)
|
|
}
|
|
if got, want := client.basicAuthUsername, "user1"; got != want {
|
|
t.Errorf("expected basic auth username %q; got: %q", want, got)
|
|
}
|
|
if got, want := client.basicAuthPassword, "secret1"; got != want {
|
|
t.Errorf("expected basic auth password %q; got: %q", want, got)
|
|
}
|
|
}
|
|
|
|
func TestClientSniffSuccess(t *testing.T) {
|
|
client, err := NewClient(SetURL("http://127.0.0.1:19200", "http://127.0.0.1:9200"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// The client should sniff both URLs, but only 127.0.0.1:9200 should return nodes.
|
|
if len(client.conns) != 1 {
|
|
t.Fatalf("expected exactly 1 node in the local cluster, got: %d (%v)", len(client.conns), client.conns)
|
|
}
|
|
}
|
|
|
|
func TestClientSniffFailure(t *testing.T) {
|
|
_, err := NewClient(SetURL("http://127.0.0.1:19200", "http://127.0.0.1:19201"))
|
|
if err == nil {
|
|
t.Fatalf("expected cluster to fail with no nodes found")
|
|
}
|
|
}
|
|
|
|
func TestClientSnifferCallback(t *testing.T) {
|
|
var calls int
|
|
cb := func(node *NodesInfoNode) bool {
|
|
calls++
|
|
return false
|
|
}
|
|
_, err := NewClient(
|
|
SetURL("http://127.0.0.1:19200", "http://127.0.0.1:9200"),
|
|
SetSnifferCallback(cb))
|
|
if err == nil {
|
|
t.Fatalf("expected cluster to fail with no nodes found")
|
|
}
|
|
if calls != 1 {
|
|
t.Fatalf("expected 1 call to the sniffer callback, got %d", calls)
|
|
}
|
|
}
|
|
|
|
func TestClientSniffDisabled(t *testing.T) {
|
|
client, err := NewClient(SetSniff(false), SetURL("http://127.0.0.1:9200", "http://127.0.0.1:9201"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// The client should not sniff, so it should have two connections.
|
|
if len(client.conns) != 2 {
|
|
t.Fatalf("expected 2 nodes, got: %d (%v)", len(client.conns), client.conns)
|
|
}
|
|
// Make two requests, so that both connections are being used
|
|
for i := 0; i < len(client.conns); i++ {
|
|
client.Flush().Do(context.TODO())
|
|
}
|
|
// The first connection (127.0.0.1:9200) should now be okay.
|
|
if i, found := findConn("http://127.0.0.1:9200", client.conns...); !found {
|
|
t.Fatalf("expected connection to %q to be found", "http://127.0.0.1:9200")
|
|
} else {
|
|
if conn := client.conns[i]; conn.IsDead() {
|
|
t.Fatal("expected connection to be alive, but it is dead")
|
|
}
|
|
}
|
|
// The second connection (127.0.0.1:9201) should now be marked as dead.
|
|
if i, found := findConn("http://127.0.0.1:9201", client.conns...); !found {
|
|
t.Fatalf("expected connection to %q to be found", "http://127.0.0.1:9201")
|
|
} else {
|
|
if conn := client.conns[i]; !conn.IsDead() {
|
|
t.Fatal("expected connection to be dead, but it is alive")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestClientWillMarkConnectionsAsAliveWhenAllAreDead(t *testing.T) {
|
|
client, err := NewClient(SetURL("http://127.0.0.1:9201"),
|
|
SetSniff(false), SetHealthcheck(false), SetMaxRetries(0))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// We should have a connection.
|
|
if len(client.conns) != 1 {
|
|
t.Fatalf("expected 1 node, got: %d (%v)", len(client.conns), client.conns)
|
|
}
|
|
|
|
// Make a request, so that the connections is marked as dead.
|
|
client.Flush().Do(context.TODO())
|
|
|
|
// The connection should now be marked as dead.
|
|
if i, found := findConn("http://127.0.0.1:9201", client.conns...); !found {
|
|
t.Fatalf("expected connection to %q to be found", "http://127.0.0.1:9201")
|
|
} else {
|
|
if conn := client.conns[i]; !conn.IsDead() {
|
|
t.Fatalf("expected connection to be dead, got: %v", conn)
|
|
}
|
|
}
|
|
|
|
// Now send another request and the connection should be marked as alive again.
|
|
client.Flush().Do(context.TODO())
|
|
|
|
if i, found := findConn("http://127.0.0.1:9201", client.conns...); !found {
|
|
t.Fatalf("expected connection to %q to be found", "http://127.0.0.1:9201")
|
|
} else {
|
|
if conn := client.conns[i]; conn.IsDead() {
|
|
t.Fatalf("expected connection to be alive, got: %v", conn)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestClientWithRequiredPlugins(t *testing.T) {
|
|
_, err := NewClient(SetRequiredPlugins("no-such-plugin"))
|
|
if err == nil {
|
|
t.Fatal("expected error when creating client")
|
|
}
|
|
if got, want := err.Error(), "elastic: plugin no-such-plugin not found"; got != want {
|
|
t.Fatalf("expected error %q; got: %q", want, got)
|
|
}
|
|
}
|
|
|
|
func TestClientHealthcheckStartupTimeout(t *testing.T) {
|
|
start := time.Now()
|
|
_, err := NewClient(SetURL("http://localhost:9299"), SetHealthcheckTimeoutStartup(5*time.Second))
|
|
duration := time.Since(start)
|
|
if !IsConnErr(err) {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(err.Error(), "connection refused") {
|
|
t.Fatalf("expected error to contain %q, have %q", "connection refused", err.Error())
|
|
}
|
|
if duration < 5*time.Second {
|
|
t.Fatalf("expected a timeout in more than 5 seconds; got: %v", duration)
|
|
}
|
|
}
|
|
|
|
func TestClientHealthcheckTimeoutLeak(t *testing.T) {
|
|
// This test test checks if healthcheck requests are canceled
|
|
// after timeout.
|
|
// It contains couple of hacks which won't be needed once we
|
|
// stop supporting Go1.7.
|
|
// On Go1.7 it uses server side effects to monitor if connection
|
|
// was closed,
|
|
// and on Go 1.8+ we're additionally honestly monitoring routine
|
|
// leaks via leaktest.
|
|
mux := http.NewServeMux()
|
|
|
|
var reqDoneMu sync.Mutex
|
|
var reqDone bool
|
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
cn, ok := w.(http.CloseNotifier)
|
|
if !ok {
|
|
t.Fatalf("Writer is not CloseNotifier, but %v", reflect.TypeOf(w).Name())
|
|
}
|
|
<-cn.CloseNotify()
|
|
reqDoneMu.Lock()
|
|
reqDone = true
|
|
reqDoneMu.Unlock()
|
|
})
|
|
|
|
lis, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatalf("Couldn't setup listener: %v", err)
|
|
}
|
|
addr := lis.Addr().String()
|
|
|
|
srv := &http.Server{
|
|
Handler: mux,
|
|
}
|
|
go srv.Serve(lis)
|
|
|
|
cli := &Client{
|
|
c: &http.Client{},
|
|
conns: []*conn{
|
|
&conn{
|
|
url: "http://" + addr + "/",
|
|
},
|
|
},
|
|
}
|
|
|
|
type closer interface {
|
|
Shutdown(context.Context) error
|
|
}
|
|
|
|
// pre-Go1.8 Server can't Shutdown
|
|
cl, isServerCloseable := (interface{}(srv)).(closer)
|
|
|
|
// Since Go1.7 can't Shutdown() - there will be leak from server
|
|
// Monitor leaks on Go 1.8+
|
|
if isServerCloseable {
|
|
defer leaktest.CheckTimeout(t, time.Second*10)()
|
|
}
|
|
|
|
cli.healthcheck(time.Millisecond*500, true)
|
|
|
|
if isServerCloseable {
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
cl.Shutdown(ctx)
|
|
}
|
|
|
|
<-time.After(time.Second)
|
|
reqDoneMu.Lock()
|
|
if !reqDone {
|
|
reqDoneMu.Unlock()
|
|
t.Fatal("Request wasn't canceled or stopped")
|
|
}
|
|
reqDoneMu.Unlock()
|
|
}
|
|
|
|
// -- NewSimpleClient --
|
|
|
|
func TestSimpleClientDefaults(t *testing.T) {
|
|
client, err := NewSimpleClient()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if client.healthcheckEnabled != false {
|
|
t.Errorf("expected health checks to be disabled, got: %v", client.healthcheckEnabled)
|
|
}
|
|
if client.healthcheckTimeoutStartup != off {
|
|
t.Errorf("expected health checks timeout on startup = %v, got: %v", off, client.healthcheckTimeoutStartup)
|
|
}
|
|
if client.healthcheckTimeout != off {
|
|
t.Errorf("expected health checks timeout = %v, got: %v", off, client.healthcheckTimeout)
|
|
}
|
|
if client.healthcheckInterval != off {
|
|
t.Errorf("expected health checks interval = %v, got: %v", off, client.healthcheckInterval)
|
|
}
|
|
if client.snifferEnabled != false {
|
|
t.Errorf("expected sniffing to be disabled, got: %v", client.snifferEnabled)
|
|
}
|
|
if client.snifferTimeoutStartup != off {
|
|
t.Errorf("expected sniffer timeout on startup = %v, got: %v", off, client.snifferTimeoutStartup)
|
|
}
|
|
if client.snifferTimeout != off {
|
|
t.Errorf("expected sniffer timeout = %v, got: %v", off, client.snifferTimeout)
|
|
}
|
|
if client.snifferInterval != off {
|
|
t.Errorf("expected sniffer interval = %v, got: %v", off, client.snifferInterval)
|
|
}
|
|
if client.basicAuth != false {
|
|
t.Errorf("expected no basic auth; got: %v", client.basicAuth)
|
|
}
|
|
if client.basicAuthUsername != "" {
|
|
t.Errorf("expected no basic auth username; got: %q", client.basicAuthUsername)
|
|
}
|
|
if client.basicAuthPassword != "" {
|
|
t.Errorf("expected no basic auth password; got: %q", client.basicAuthUsername)
|
|
}
|
|
if client.sendGetBodyAs != "GET" {
|
|
t.Errorf("expected sendGetBodyAs to be GET; got: %q", client.sendGetBodyAs)
|
|
}
|
|
}
|
|
|
|
// -- Start and stop --
|
|
|
|
func TestClientStartAndStop(t *testing.T) {
|
|
client, err := NewClient()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
running := client.IsRunning()
|
|
if !running {
|
|
t.Fatalf("expected background processes to run; got: %v", running)
|
|
}
|
|
|
|
// Stop
|
|
client.Stop()
|
|
running = client.IsRunning()
|
|
if running {
|
|
t.Fatalf("expected background processes to be stopped; got: %v", running)
|
|
}
|
|
|
|
// Stop again => no-op
|
|
client.Stop()
|
|
running = client.IsRunning()
|
|
if running {
|
|
t.Fatalf("expected background processes to be stopped; got: %v", running)
|
|
}
|
|
|
|
// Start
|
|
client.Start()
|
|
running = client.IsRunning()
|
|
if !running {
|
|
t.Fatalf("expected background processes to run; got: %v", running)
|
|
}
|
|
|
|
// Start again => no-op
|
|
client.Start()
|
|
running = client.IsRunning()
|
|
if !running {
|
|
t.Fatalf("expected background processes to run; got: %v", running)
|
|
}
|
|
}
|
|
|
|
func TestClientStartAndStopWithSnifferAndHealthchecksDisabled(t *testing.T) {
|
|
client, err := NewClient(SetSniff(false), SetHealthcheck(false))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
running := client.IsRunning()
|
|
if !running {
|
|
t.Fatalf("expected background processes to run; got: %v", running)
|
|
}
|
|
|
|
// Stop
|
|
client.Stop()
|
|
running = client.IsRunning()
|
|
if running {
|
|
t.Fatalf("expected background processes to be stopped; got: %v", running)
|
|
}
|
|
|
|
// Stop again => no-op
|
|
client.Stop()
|
|
running = client.IsRunning()
|
|
if running {
|
|
t.Fatalf("expected background processes to be stopped; got: %v", running)
|
|
}
|
|
|
|
// Start
|
|
client.Start()
|
|
running = client.IsRunning()
|
|
if !running {
|
|
t.Fatalf("expected background processes to run; got: %v", running)
|
|
}
|
|
|
|
// Start again => no-op
|
|
client.Start()
|
|
running = client.IsRunning()
|
|
if !running {
|
|
t.Fatalf("expected background processes to run; got: %v", running)
|
|
}
|
|
}
|
|
|
|
// -- Sniffing --
|
|
|
|
func TestClientSniffNode(t *testing.T) {
|
|
client, err := NewClient()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ch := make(chan []*conn)
|
|
go func() { ch <- client.sniffNode(context.Background(), DefaultURL) }()
|
|
|
|
select {
|
|
case nodes := <-ch:
|
|
if len(nodes) != 1 {
|
|
t.Fatalf("expected %d nodes; got: %d", 1, len(nodes))
|
|
}
|
|
pattern := `http:\/\/[\d\.]+:9200`
|
|
matched, err := regexp.MatchString(pattern, nodes[0].URL())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !matched {
|
|
t.Fatalf("expected node URL pattern %q; got: %q", pattern, nodes[0].URL())
|
|
}
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("expected no timeout in sniff node")
|
|
break
|
|
}
|
|
}
|
|
|
|
func TestClientSniffOnDefaultURL(t *testing.T) {
|
|
client, _ := NewClient()
|
|
if client == nil {
|
|
t.Fatal("no client returned")
|
|
}
|
|
|
|
ch := make(chan error, 1)
|
|
go func() {
|
|
ch <- client.sniff(DefaultSnifferTimeoutStartup)
|
|
}()
|
|
|
|
select {
|
|
case err := <-ch:
|
|
if err != nil {
|
|
t.Fatalf("expected sniff to succeed; got: %v", err)
|
|
}
|
|
if len(client.conns) != 1 {
|
|
t.Fatalf("expected %d nodes; got: %d", 1, len(client.conns))
|
|
}
|
|
pattern := `http:\/\/[\d\.]+:9200`
|
|
matched, err := regexp.MatchString(pattern, client.conns[0].URL())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !matched {
|
|
t.Fatalf("expected node URL pattern %q; got: %q", pattern, client.conns[0].URL())
|
|
}
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("expected no timeout in sniff")
|
|
break
|
|
}
|
|
}
|
|
|
|
func TestClientSniffTimeoutLeak(t *testing.T) {
|
|
// This test test checks if sniff requests are canceled
|
|
// after timeout.
|
|
// It contains couple of hacks which won't be needed once we
|
|
// stop supporting Go1.7.
|
|
// On Go1.7 it uses server side effects to monitor if connection
|
|
// was closed,
|
|
// and on Go 1.8+ we're additionally honestly monitoring routine
|
|
// leaks via leaktest.
|
|
mux := http.NewServeMux()
|
|
|
|
var reqDoneMu sync.Mutex
|
|
var reqDone bool
|
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
cn, ok := w.(http.CloseNotifier)
|
|
if !ok {
|
|
t.Fatalf("Writer is not CloseNotifier, but %v", reflect.TypeOf(w).Name())
|
|
}
|
|
<-cn.CloseNotify()
|
|
reqDoneMu.Lock()
|
|
reqDone = true
|
|
reqDoneMu.Unlock()
|
|
})
|
|
|
|
lis, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatalf("Couldn't setup listener: %v", err)
|
|
}
|
|
addr := lis.Addr().String()
|
|
|
|
srv := &http.Server{
|
|
Handler: mux,
|
|
}
|
|
go srv.Serve(lis)
|
|
|
|
cli := &Client{
|
|
c: &http.Client{},
|
|
conns: []*conn{
|
|
&conn{
|
|
url: "http://" + addr + "/",
|
|
},
|
|
},
|
|
snifferEnabled: true,
|
|
}
|
|
|
|
type closer interface {
|
|
Shutdown(context.Context) error
|
|
}
|
|
|
|
// pre-Go1.8 Server can't Shutdown
|
|
cl, isServerCloseable := (interface{}(srv)).(closer)
|
|
|
|
// Since Go1.7 can't Shutdown() - there will be leak from server
|
|
// Monitor leaks on Go 1.8+
|
|
if isServerCloseable {
|
|
defer leaktest.CheckTimeout(t, time.Second*10)()
|
|
}
|
|
|
|
cli.sniff(time.Millisecond * 500)
|
|
|
|
if isServerCloseable {
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
cl.Shutdown(ctx)
|
|
}
|
|
|
|
<-time.After(time.Second)
|
|
reqDoneMu.Lock()
|
|
if !reqDone {
|
|
reqDoneMu.Unlock()
|
|
t.Fatal("Request wasn't canceled or stopped")
|
|
}
|
|
reqDoneMu.Unlock()
|
|
}
|
|
|
|
func TestClientExtractHostname(t *testing.T) {
|
|
tests := []struct {
|
|
Scheme string
|
|
Address string
|
|
Output string
|
|
}{
|
|
{
|
|
Scheme: "http",
|
|
Address: "",
|
|
Output: "",
|
|
},
|
|
{
|
|
Scheme: "https",
|
|
Address: "abc",
|
|
Output: "",
|
|
},
|
|
{
|
|
Scheme: "http",
|
|
Address: "127.0.0.1:19200",
|
|
Output: "http://127.0.0.1:19200",
|
|
},
|
|
{
|
|
Scheme: "https",
|
|
Address: "127.0.0.1:9200",
|
|
Output: "https://127.0.0.1:9200",
|
|
},
|
|
{
|
|
Scheme: "http",
|
|
Address: "myelk.local/10.1.0.24:9200",
|
|
Output: "http://10.1.0.24:9200",
|
|
},
|
|
}
|
|
|
|
client, err := NewClient(SetSniff(false), SetHealthcheck(false))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, test := range tests {
|
|
got := client.extractHostname(test.Scheme, test.Address)
|
|
if want := test.Output; want != got {
|
|
t.Errorf("expected %q; got: %q", want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
// -- Selector --
|
|
|
|
func TestClientSelectConnHealthy(t *testing.T) {
|
|
client, err := NewClient(
|
|
SetSniff(false),
|
|
SetHealthcheck(false),
|
|
SetURL("http://127.0.0.1:9200/node1", "http://127.0.0.1:9201/node2/"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Both are healthy, so we should get both URLs in round-robin
|
|
client.conns[0].MarkAsHealthy()
|
|
client.conns[1].MarkAsHealthy()
|
|
|
|
// #1: Return 1st
|
|
c, err := client.next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.URL() != client.conns[0].URL() {
|
|
t.Fatalf("expected %s; got: %s", c.URL(), client.conns[0].URL())
|
|
}
|
|
// #2: Return 2nd
|
|
c, err = client.next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.URL() != client.conns[1].URL() {
|
|
t.Fatalf("expected %s; got: %s", c.URL(), client.conns[1].URL())
|
|
}
|
|
// #3: Return 1st
|
|
c, err = client.next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.URL() != client.conns[0].URL() {
|
|
t.Fatalf("expected %s; got: %s", c.URL(), client.conns[0].URL())
|
|
}
|
|
}
|
|
|
|
func TestClientSelectConnHealthyWithURLPrefix(t *testing.T) {
|
|
client, err := NewClient(
|
|
SetSniff(false),
|
|
SetHealthcheck(false),
|
|
SetURL("http://127.0.0.1:9200/node1", "http://127.0.0.1:9201/node2/prefix/"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Both are healthy, so we should get both URLs in round-robin
|
|
client.conns[0].MarkAsHealthy()
|
|
client.conns[1].MarkAsHealthy()
|
|
|
|
// Check that the connection used the URLs, including its prefix
|
|
if want, have := "http://127.0.0.1:9200/node1", client.conns[0].URL(); want != have {
|
|
t.Fatalf("want Node[0] = %q, have %q", want, have)
|
|
}
|
|
// Note that it stripped the / off the suffix
|
|
if want, have := "http://127.0.0.1:9201/node2/prefix", client.conns[1].URL(); want != have {
|
|
t.Fatalf("want Node[1] = %q, have %q", want, have)
|
|
}
|
|
|
|
// #1: Return 1st
|
|
c, err := client.next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.URL() != client.conns[0].URL() {
|
|
t.Fatalf("expected %s; got: %s", c.URL(), client.conns[0].URL())
|
|
}
|
|
// #2: Return 2nd
|
|
c, err = client.next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.URL() != client.conns[1].URL() {
|
|
t.Fatalf("expected %s; got: %s", c.URL(), client.conns[1].URL())
|
|
}
|
|
// #3: Return 1st
|
|
c, err = client.next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.URL() != client.conns[0].URL() {
|
|
t.Fatalf("expected %s; got: %s", c.URL(), client.conns[0].URL())
|
|
}
|
|
}
|
|
|
|
func TestClientSelectConnHealthyAndDead(t *testing.T) {
|
|
client, err := NewClient(
|
|
SetSniff(false),
|
|
SetHealthcheck(false),
|
|
SetURL("http://127.0.0.1:9200", "http://127.0.0.1:9201"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// 1st is healthy, second is dead
|
|
client.conns[0].MarkAsHealthy()
|
|
client.conns[1].MarkAsDead()
|
|
|
|
// #1: Return 1st
|
|
c, err := client.next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.URL() != client.conns[0].URL() {
|
|
t.Fatalf("expected %s; got: %s", c.URL(), client.conns[0].URL())
|
|
}
|
|
// #2: Return 1st again
|
|
c, err = client.next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.URL() != client.conns[0].URL() {
|
|
t.Fatalf("expected %s; got: %s", c.URL(), client.conns[0].URL())
|
|
}
|
|
// #3: Return 1st again and again
|
|
c, err = client.next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.URL() != client.conns[0].URL() {
|
|
t.Fatalf("expected %s; got: %s", c.URL(), client.conns[0].URL())
|
|
}
|
|
}
|
|
|
|
func TestClientSelectConnDeadAndHealthy(t *testing.T) {
|
|
client, err := NewClient(
|
|
SetSniff(false),
|
|
SetHealthcheck(false),
|
|
SetURL("http://127.0.0.1:9200", "http://127.0.0.1:9201"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// 1st is dead, 2nd is healthy
|
|
client.conns[0].MarkAsDead()
|
|
client.conns[1].MarkAsHealthy()
|
|
|
|
// #1: Return 2nd
|
|
c, err := client.next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.URL() != client.conns[1].URL() {
|
|
t.Fatalf("expected %s; got: %s", c.URL(), client.conns[1].URL())
|
|
}
|
|
// #2: Return 2nd again
|
|
c, err = client.next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.URL() != client.conns[1].URL() {
|
|
t.Fatalf("expected %s; got: %s", c.URL(), client.conns[1].URL())
|
|
}
|
|
// #3: Return 2nd again and again
|
|
c, err = client.next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.URL() != client.conns[1].URL() {
|
|
t.Fatalf("expected %s; got: %s", c.URL(), client.conns[1].URL())
|
|
}
|
|
}
|
|
|
|
func TestClientSelectConnAllDead(t *testing.T) {
|
|
client, err := NewClient(
|
|
SetSniff(false),
|
|
SetHealthcheck(false),
|
|
SetURL("http://127.0.0.1:9200", "http://127.0.0.1:9201"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Both are dead
|
|
client.conns[0].MarkAsDead()
|
|
client.conns[1].MarkAsDead()
|
|
|
|
// If all connections are dead, next should make them alive again, but
|
|
// still return an error when it first finds out.
|
|
c, err := client.next()
|
|
if !IsConnErr(err) {
|
|
t.Fatal(err)
|
|
}
|
|
if c != nil {
|
|
t.Fatalf("expected no connection; got: %v", c)
|
|
}
|
|
// Return a connection
|
|
c, err = client.next()
|
|
if err != nil {
|
|
t.Fatalf("expected no error; got: %v", err)
|
|
}
|
|
if c == nil {
|
|
t.Fatalf("expected connection; got: %v", c)
|
|
}
|
|
// Return a connection
|
|
c, err = client.next()
|
|
if err != nil {
|
|
t.Fatalf("expected no error; got: %v", err)
|
|
}
|
|
if c == nil {
|
|
t.Fatalf("expected connection; got: %v", c)
|
|
}
|
|
}
|
|
|
|
// -- ElasticsearchVersion --
|
|
|
|
func TestElasticsearchVersion(t *testing.T) {
|
|
client, err := NewClient()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
version, err := client.ElasticsearchVersion(DefaultURL)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if version == "" {
|
|
t.Errorf("expected a version number, got: %q", version)
|
|
}
|
|
}
|
|
|
|
// -- IndexNames --
|
|
|
|
func TestIndexNames(t *testing.T) {
|
|
client := setupTestClientAndCreateIndex(t)
|
|
names, err := client.IndexNames()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(names) == 0 {
|
|
t.Fatalf("expected some index names, got: %d", len(names))
|
|
}
|
|
var found bool
|
|
for _, name := range names {
|
|
if name == testIndexName {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("expected to find index %q; got: %v", testIndexName, found)
|
|
}
|
|
}
|
|
|
|
// -- PerformRequest --
|
|
|
|
func TestPerformRequest(t *testing.T) {
|
|
client, err := NewClient()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
res, err := client.PerformRequest(context.TODO(), "GET", "/", nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if res == nil {
|
|
t.Fatal("expected response to be != nil")
|
|
}
|
|
|
|
ret := new(PingResult)
|
|
if err := json.Unmarshal(res.Body, ret); err != nil {
|
|
t.Fatalf("expected no error on decode; got: %v", err)
|
|
}
|
|
if ret.ClusterName == "" {
|
|
t.Errorf("expected cluster name; got: %q", ret.ClusterName)
|
|
}
|
|
}
|
|
|
|
func TestPerformRequestWithSimpleClient(t *testing.T) {
|
|
client, err := NewSimpleClient()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
res, err := client.PerformRequest(context.TODO(), "GET", "/", nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if res == nil {
|
|
t.Fatal("expected response to be != nil")
|
|
}
|
|
|
|
ret := new(PingResult)
|
|
if err := json.Unmarshal(res.Body, ret); err != nil {
|
|
t.Fatalf("expected no error on decode; got: %v", err)
|
|
}
|
|
if ret.ClusterName == "" {
|
|
t.Errorf("expected cluster name; got: %q", ret.ClusterName)
|
|
}
|
|
}
|
|
|
|
func TestPerformRequestWithLogger(t *testing.T) {
|
|
var w bytes.Buffer
|
|
out := log.New(&w, "LOGGER ", log.LstdFlags)
|
|
|
|
client, err := NewClient(SetInfoLog(out), SetSniff(false))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
res, err := client.PerformRequest(context.TODO(), "GET", "/", nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if res == nil {
|
|
t.Fatal("expected response to be != nil")
|
|
}
|
|
|
|
ret := new(PingResult)
|
|
if err := json.Unmarshal(res.Body, ret); err != nil {
|
|
t.Fatalf("expected no error on decode; got: %v", err)
|
|
}
|
|
if ret.ClusterName == "" {
|
|
t.Errorf("expected cluster name; got: %q", ret.ClusterName)
|
|
}
|
|
|
|
got := w.String()
|
|
pattern := `^LOGGER \d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} GET http://.*/ \[status:200, request:\d+\.\d{3}s\]\n`
|
|
matched, err := regexp.MatchString(pattern, got)
|
|
if err != nil {
|
|
t.Fatalf("expected log line to match %q; got: %v", pattern, err)
|
|
}
|
|
if !matched {
|
|
t.Errorf("expected log line to match %q; got: %v", pattern, got)
|
|
}
|
|
}
|
|
|
|
func TestPerformRequestWithLoggerAndTracer(t *testing.T) {
|
|
var lw bytes.Buffer
|
|
lout := log.New(&lw, "LOGGER ", log.LstdFlags)
|
|
|
|
var tw bytes.Buffer
|
|
tout := log.New(&tw, "TRACER ", log.LstdFlags)
|
|
|
|
client, err := NewClient(SetInfoLog(lout), SetTraceLog(tout), SetSniff(false))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
res, err := client.PerformRequest(context.TODO(), "GET", "/", nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if res == nil {
|
|
t.Fatal("expected response to be != nil")
|
|
}
|
|
|
|
ret := new(PingResult)
|
|
if err := json.Unmarshal(res.Body, ret); err != nil {
|
|
t.Fatalf("expected no error on decode; got: %v", err)
|
|
}
|
|
if ret.ClusterName == "" {
|
|
t.Errorf("expected cluster name; got: %q", ret.ClusterName)
|
|
}
|
|
|
|
lgot := lw.String()
|
|
if lgot == "" {
|
|
t.Errorf("expected logger output; got: %q", lgot)
|
|
}
|
|
|
|
tgot := tw.String()
|
|
if tgot == "" {
|
|
t.Errorf("expected tracer output; got: %q", tgot)
|
|
}
|
|
}
|
|
func TestPerformRequestWithTracerOnError(t *testing.T) {
|
|
var tw bytes.Buffer
|
|
tout := log.New(&tw, "TRACER ", log.LstdFlags)
|
|
|
|
client, err := NewClient(SetTraceLog(tout), SetSniff(false))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
client.PerformRequest(context.TODO(), "GET", "/no-such-index", nil, nil)
|
|
|
|
tgot := tw.String()
|
|
if tgot == "" {
|
|
t.Errorf("expected tracer output; got: %q", tgot)
|
|
}
|
|
}
|
|
|
|
type customLogger struct {
|
|
out bytes.Buffer
|
|
}
|
|
|
|
func (l *customLogger) Printf(format string, v ...interface{}) {
|
|
l.out.WriteString(fmt.Sprintf(format, v...) + "\n")
|
|
}
|
|
|
|
func TestPerformRequestWithCustomLogger(t *testing.T) {
|
|
logger := &customLogger{}
|
|
|
|
client, err := NewClient(SetInfoLog(logger), SetSniff(false))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
res, err := client.PerformRequest(context.TODO(), "GET", "/", nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if res == nil {
|
|
t.Fatal("expected response to be != nil")
|
|
}
|
|
|
|
ret := new(PingResult)
|
|
if err := json.Unmarshal(res.Body, ret); err != nil {
|
|
t.Fatalf("expected no error on decode; got: %v", err)
|
|
}
|
|
if ret.ClusterName == "" {
|
|
t.Errorf("expected cluster name; got: %q", ret.ClusterName)
|
|
}
|
|
|
|
got := logger.out.String()
|
|
pattern := `^GET http://.*/ \[status:200, request:\d+\.\d{3}s\]\n`
|
|
matched, err := regexp.MatchString(pattern, got)
|
|
if err != nil {
|
|
t.Fatalf("expected log line to match %q; got: %v", pattern, err)
|
|
}
|
|
if !matched {
|
|
t.Errorf("expected log line to match %q; got: %v", pattern, got)
|
|
}
|
|
}
|
|
|
|
// failingTransport will run a fail callback if it sees a given URL path prefix.
|
|
type failingTransport struct {
|
|
path string // path prefix to look for
|
|
fail func(*http.Request) (*http.Response, error) // call when path prefix is found
|
|
next http.RoundTripper // next round-tripper (use http.DefaultTransport if nil)
|
|
}
|
|
|
|
// RoundTrip implements a failing transport.
|
|
func (tr *failingTransport) RoundTrip(r *http.Request) (*http.Response, error) {
|
|
if strings.HasPrefix(r.URL.Path, tr.path) && tr.fail != nil {
|
|
return tr.fail(r)
|
|
}
|
|
if tr.next != nil {
|
|
return tr.next.RoundTrip(r)
|
|
}
|
|
return http.DefaultTransport.RoundTrip(r)
|
|
}
|
|
|
|
func TestPerformRequestRetryOnHttpError(t *testing.T) {
|
|
var numFailedReqs int
|
|
fail := func(r *http.Request) (*http.Response, error) {
|
|
numFailedReqs += 1
|
|
//return &http.Response{Request: r, StatusCode: 400}, nil
|
|
return nil, errors.New("request failed")
|
|
}
|
|
|
|
// Run against a failing endpoint and see if PerformRequest
|
|
// retries correctly.
|
|
tr := &failingTransport{path: "/fail", fail: fail}
|
|
httpClient := &http.Client{Transport: tr}
|
|
|
|
client, err := NewClient(SetHttpClient(httpClient), SetMaxRetries(5), SetHealthcheck(false))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
res, err := client.PerformRequest(context.TODO(), "GET", "/fail", nil, nil)
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
if res != nil {
|
|
t.Fatal("expected no response")
|
|
}
|
|
// Connection should be marked as dead after it failed
|
|
if numFailedReqs != 5 {
|
|
t.Errorf("expected %d failed requests; got: %d", 5, numFailedReqs)
|
|
}
|
|
}
|
|
|
|
func TestPerformRequestNoRetryOnValidButUnsuccessfulHttpStatus(t *testing.T) {
|
|
var numFailedReqs int
|
|
fail := func(r *http.Request) (*http.Response, error) {
|
|
numFailedReqs += 1
|
|
return &http.Response{Request: r, StatusCode: 500}, nil
|
|
}
|
|
|
|
// Run against a failing endpoint and see if PerformRequest
|
|
// retries correctly.
|
|
tr := &failingTransport{path: "/fail", fail: fail}
|
|
httpClient := &http.Client{Transport: tr}
|
|
|
|
client, err := NewClient(SetHttpClient(httpClient), SetMaxRetries(5), SetHealthcheck(false))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
res, err := client.PerformRequest(context.TODO(), "GET", "/fail", nil, nil)
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
if res == nil {
|
|
t.Fatal("expected response, got nil")
|
|
}
|
|
if want, got := 500, res.StatusCode; want != got {
|
|
t.Fatalf("expected status code = %d, got %d", want, got)
|
|
}
|
|
// Retry should not have triggered additional requests because
|
|
if numFailedReqs != 1 {
|
|
t.Errorf("expected %d failed requests; got: %d", 1, numFailedReqs)
|
|
}
|
|
}
|
|
|
|
// failingBody will return an error when json.Marshal is called on it.
|
|
type failingBody struct{}
|
|
|
|
// MarshalJSON implements the json.Marshaler interface and always returns an error.
|
|
func (fb failingBody) MarshalJSON() ([]byte, error) {
|
|
return nil, errors.New("failing to marshal")
|
|
}
|
|
|
|
func TestPerformRequestWithSetBodyError(t *testing.T) {
|
|
client, err := NewClient()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
res, err := client.PerformRequest(context.TODO(), "GET", "/", nil, failingBody{})
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
if res != nil {
|
|
t.Fatal("expected no response")
|
|
}
|
|
}
|
|
|
|
// sleepingTransport will sleep before doing a request.
|
|
type sleepingTransport struct {
|
|
timeout time.Duration
|
|
}
|
|
|
|
// RoundTrip implements a "sleepy" transport.
|
|
func (tr *sleepingTransport) RoundTrip(r *http.Request) (*http.Response, error) {
|
|
time.Sleep(tr.timeout)
|
|
return http.DefaultTransport.RoundTrip(r)
|
|
}
|
|
|
|
func TestPerformRequestWithCancel(t *testing.T) {
|
|
tr := &sleepingTransport{timeout: 3 * time.Second}
|
|
httpClient := &http.Client{Transport: tr}
|
|
|
|
client, err := NewSimpleClient(SetHttpClient(httpClient), SetMaxRetries(0))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
type result struct {
|
|
res *Response
|
|
err error
|
|
}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
resc := make(chan result, 1)
|
|
go func() {
|
|
res, err := client.PerformRequest(ctx, "GET", "/", nil, nil)
|
|
resc <- result{res: res, err: err}
|
|
}()
|
|
select {
|
|
case <-time.After(1 * time.Second):
|
|
cancel()
|
|
case res := <-resc:
|
|
t.Fatalf("expected response before cancel, got %v", res)
|
|
case <-ctx.Done():
|
|
t.Fatalf("expected no early termination, got ctx.Done(): %v", ctx.Err())
|
|
}
|
|
err = ctx.Err()
|
|
if err != context.Canceled {
|
|
t.Fatalf("expected error context.Canceled, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPerformRequestWithTimeout(t *testing.T) {
|
|
tr := &sleepingTransport{timeout: 3 * time.Second}
|
|
httpClient := &http.Client{Transport: tr}
|
|
|
|
client, err := NewSimpleClient(SetHttpClient(httpClient), SetMaxRetries(0))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
type result struct {
|
|
res *Response
|
|
err error
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
defer cancel()
|
|
|
|
resc := make(chan result, 1)
|
|
go func() {
|
|
res, err := client.PerformRequest(ctx, "GET", "/", nil, nil)
|
|
resc <- result{res: res, err: err}
|
|
}()
|
|
select {
|
|
case res := <-resc:
|
|
t.Fatalf("expected timeout before response, got %v", res)
|
|
case <-ctx.Done():
|
|
err := ctx.Err()
|
|
if err != context.DeadlineExceeded {
|
|
t.Fatalf("expected error context.DeadlineExceeded, got: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// -- Compression --
|
|
|
|
// Notice that the trace log does always print "Accept-Encoding: gzip"
|
|
// regardless of whether compression is enabled or not. This is because
|
|
// of the underlying "httputil.DumpRequestOut".
|
|
//
|
|
// Use a real HTTP proxy/recorder to convince yourself that
|
|
// "Accept-Encoding: gzip" is NOT sent when DisableCompression
|
|
// is set to true.
|
|
//
|
|
// See also:
|
|
// https://groups.google.com/forum/#!topic/golang-nuts/ms8QNCzew8Q
|
|
|
|
func TestPerformRequestWithCompressionEnabled(t *testing.T) {
|
|
testPerformRequestWithCompression(t, &http.Client{
|
|
Transport: &http.Transport{
|
|
DisableCompression: true,
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestPerformRequestWithCompressionDisabled(t *testing.T) {
|
|
testPerformRequestWithCompression(t, &http.Client{
|
|
Transport: &http.Transport{
|
|
DisableCompression: false,
|
|
},
|
|
})
|
|
}
|
|
|
|
func testPerformRequestWithCompression(t *testing.T, hc *http.Client) {
|
|
client, err := NewClient(SetHttpClient(hc), SetSniff(false))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
res, err := client.PerformRequest(context.TODO(), "GET", "/", nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if res == nil {
|
|
t.Fatal("expected response to be != nil")
|
|
}
|
|
|
|
ret := new(PingResult)
|
|
if err := json.Unmarshal(res.Body, ret); err != nil {
|
|
t.Fatalf("expected no error on decode; got: %v", err)
|
|
}
|
|
if ret.ClusterName == "" {
|
|
t.Errorf("expected cluster name; got: %q", ret.ClusterName)
|
|
}
|
|
}
|