// Licensed to Elasticsearch B.V. under one or more agreements. // Elasticsearch B.V. licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. // +build !integration package elasticsearch import ( "encoding/base64" "errors" "net/http" "net/url" "os" "regexp" "testing" "github.com/elastic/go-elasticsearch/v7/estransport" ) func TestClientConfiguration(t *testing.T) { t.Parallel() t.Run("With empty", func(t *testing.T) { c, err := NewDefaultClient() if err != nil { t.Errorf("Unexpected error: %s", err) } u := c.Transport.(*estransport.Client).URLs()[0].String() if u != defaultURL { t.Errorf("Unexpected URL, want=%s, got=%s", defaultURL, u) } }) t.Run("With URL from Addresses", func(t *testing.T) { c, err := NewClient(Config{Addresses: []string{"http://localhost:8080//"}}) if err != nil { t.Fatalf("Unexpected error: %s", err) } u := c.Transport.(*estransport.Client).URLs()[0].String() if u != "http://localhost:8080" { t.Errorf("Unexpected URL, want=http://localhost:8080, got=%s", u) } }) t.Run("With URL from environment", func(t *testing.T) { os.Setenv("ELASTICSEARCH_URL", "http://example.com") defer func() { os.Setenv("ELASTICSEARCH_URL", "") }() c, err := NewDefaultClient() if err != nil { t.Errorf("Unexpected error: %s", err) } u := c.Transport.(*estransport.Client).URLs()[0].String() if u != "http://example.com" { t.Errorf("Unexpected URL, want=http://example.com, got=%s", u) } }) t.Run("With URL from environment and cfg.Addresses", func(t *testing.T) { os.Setenv("ELASTICSEARCH_URL", "http://example.com") defer func() { os.Setenv("ELASTICSEARCH_URL", "") }() c, err := NewClient(Config{Addresses: []string{"http://localhost:8080//"}}) if err != nil { t.Fatalf("Unexpected error: %s", err) } u := c.Transport.(*estransport.Client).URLs()[0].String() if u != "http://localhost:8080" { t.Errorf("Unexpected URL, want=http://localhost:8080, got=%s", u) } }) t.Run("With URL from environment and cfg.CloudID", func(t *testing.T) { os.Setenv("ELASTICSEARCH_URL", "http://example.com") defer func() { os.Setenv("ELASTICSEARCH_URL", "") }() c, err := NewClient(Config{CloudID: "foo:YmFyLmNsb3VkLmVzLmlvJGFiYzEyMyRkZWY0NTY="}) if err != nil { t.Fatalf("Unexpected error: %s", err) } u := c.Transport.(*estransport.Client).URLs()[0].String() if u != "https://abc123.bar.cloud.es.io" { t.Errorf("Unexpected URL, want=https://abc123.bar.cloud.es.io, got=%s", u) } }) t.Run("With cfg.Addresses and cfg.CloudID", func(t *testing.T) { _, err := NewClient(Config{Addresses: []string{"http://localhost:8080//"}, CloudID: "foo:ABC="}) if err == nil { t.Fatalf("Expected error, got: %v", err) } match, _ := regexp.MatchString("both .* are set", err.Error()) if !match { t.Errorf("Expected error when addresses from environment and configuration are used together, got: %v", err) } }) t.Run("With CloudID", func(t *testing.T) { // bar.cloud.es.io$abc123$def456 c, err := NewClient(Config{CloudID: "foo:YmFyLmNsb3VkLmVzLmlvJGFiYzEyMyRkZWY0NTY="}) if err != nil { t.Fatalf("Unexpected error: %s", err) } u := c.Transport.(*estransport.Client).URLs()[0].String() if u != "https://abc123.bar.cloud.es.io" { t.Errorf("Unexpected URL, want=https://abc123.bar.cloud.es.io, got=%s", u) } }) t.Run("With invalid CloudID", func(t *testing.T) { var err error _, err = NewClient(Config{CloudID: "foo:ZZZ==="}) if err == nil { t.Errorf("Expected error for CloudID, got: %v", err) } _, err = NewClient(Config{CloudID: "foo:Zm9v"}) if err == nil { t.Errorf("Expected error for CloudID, got: %v", err) } _, err = NewClient(Config{CloudID: "foo:"}) if err == nil { t.Errorf("Expected error for CloudID, got: %v", err) } }) t.Run("With invalid URL", func(t *testing.T) { u := ":foo" _, err := NewClient(Config{Addresses: []string{u}}) if err == nil { t.Errorf("Expected error for URL %q, got %v", u, err) } }) t.Run("With invalid URL from environment", func(t *testing.T) { os.Setenv("ELASTICSEARCH_URL", ":foobar") defer func() { os.Setenv("ELASTICSEARCH_URL", "") }() c, err := NewDefaultClient() if err == nil { t.Errorf("Expected error, got: %+v", c) } }) } var called bool type mockTransp struct{} func (t *mockTransp) RoundTrip(req *http.Request) (*http.Response, error) { called = true return &http.Response{}, nil } func TestClientInterface(t *testing.T) { t.Run("Transport", func(t *testing.T) { c, err := NewClient(Config{Transport: &mockTransp{}}) if err != nil { t.Fatalf("Unexpected error: %s", err) } if called != false { // megacheck ignore t.Errorf("Unexpected call to transport by client") } c.Perform(&http.Request{URL: &url.URL{}, Header: make(http.Header)}) // errcheck ignore if called != true { // megacheck ignore t.Errorf("Expected client to call transport") } }) } func TestAddrsToURLs(t *testing.T) { tt := []struct { name string addrs []string urls []*url.URL err error }{ { name: "valid", addrs: []string{ "http://example.com", "https://example.com", "http://192.168.255.255", "http://example.com:8080", }, urls: []*url.URL{ {Scheme: "http", Host: "example.com"}, {Scheme: "https", Host: "example.com"}, {Scheme: "http", Host: "192.168.255.255"}, {Scheme: "http", Host: "example.com:8080"}, }, err: nil, }, { name: "trim trailing slash", addrs: []string{"http://example.com/", "http://example.com//"}, urls: []*url.URL{ {Scheme: "http", Host: "example.com", Path: ""}, {Scheme: "http", Host: "example.com", Path: ""}, }, }, { name: "keep suffix", addrs: []string{"http://example.com/foo"}, urls: []*url.URL{{Scheme: "http", Host: "example.com", Path: "/foo"}}, }, { name: "invalid url", addrs: []string{"://invalid.com"}, urls: nil, err: errors.New("missing protocol scheme"), }, } for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { res, err := addrsToURLs(tc.addrs) if tc.err != nil { if err == nil { t.Errorf("Expected error, got: %v", err) } match, _ := regexp.MatchString(tc.err.Error(), err.Error()) if !match { t.Errorf("Expected err [%s] to match: %s", err.Error(), tc.err.Error()) } } for i := range tc.urls { if res[i].Scheme != tc.urls[i].Scheme { t.Errorf("%s: Unexpected scheme, want=%s, got=%s", tc.name, tc.urls[i].Scheme, res[i].Scheme) } } for i := range tc.urls { if res[i].Host != tc.urls[i].Host { t.Errorf("%s: Unexpected host, want=%s, got=%s", tc.name, tc.urls[i].Host, res[i].Host) } } for i := range tc.urls { if res[i].Path != tc.urls[i].Path { t.Errorf("%s: Unexpected path, want=%s, got=%s", tc.name, tc.urls[i].Path, res[i].Path) } } }) } } func TestCloudID(t *testing.T) { t.Run("Parse", func(t *testing.T) { var testdata = []struct { in string out string }{ { in: "name:" + base64.StdEncoding.EncodeToString([]byte("host$es_uuid$kibana_uuid")), out: "https://es_uuid.host", }, { in: "name:" + base64.StdEncoding.EncodeToString([]byte("host:9243$es_uuid$kibana_uuid")), out: "https://es_uuid.host:9243", }, { in: "name:" + base64.StdEncoding.EncodeToString([]byte("host$es_uuid$")), out: "https://es_uuid.host", }, { in: "name:" + base64.StdEncoding.EncodeToString([]byte("host$es_uuid")), out: "https://es_uuid.host", }, } for _, tt := range testdata { actual, err := addrFromCloudID(tt.in) if err != nil { t.Errorf("Unexpected error: %s", err) } if actual != tt.out { t.Errorf("Unexpected output, want=%q, got=%q", tt.out, actual) } } }) t.Run("Invalid format", func(t *testing.T) { input := "foobar" _, err := addrFromCloudID(input) if err == nil { t.Errorf("Expected error for input %q, got %v", input, err) } match, _ := regexp.MatchString("unexpected format", err.Error()) if !match { t.Errorf("Unexpected error string: %s", err) } }) t.Run("Invalid base64 value", func(t *testing.T) { input := "foobar:xxxxx" _, err := addrFromCloudID(input) if err == nil { t.Errorf("Expected error for input %q, got %v", input, err) } match, _ := regexp.MatchString("illegal base64 data", err.Error()) if !match { t.Errorf("Unexpected error string: %s", err) } }) } func TestVersion(t *testing.T) { if Version == "" { t.Error("Version is empty") } } func TestClientMetrics(t *testing.T) { c, _ := NewClient(Config{EnableMetrics: true}) m, err := c.Metrics() if err != nil { t.Fatalf("Unexpected error: %v", err) } if m.Requests != 0 { t.Errorf("Unexpected output: %s", m) } }