Cloudeploy/consul-template/dependency/health_service_test.go

557 lines
13 KiB
Go

package dependency
import (
"fmt"
"reflect"
"sort"
"strings"
"testing"
"github.com/hashicorp/consul/api"
)
func TestServiceDependencyFetch(t *testing.T) {
clients, consul := testConsulServer(t)
defer consul.Stop()
dep := &HealthServices{rawKey: "consul", Name: "consul"}
results, _, err := dep.Fetch(clients, nil)
if err != nil {
t.Fatal(err)
}
typed, ok := results.([]*HealthService)
if !ok {
t.Fatal("could not convert result to []*HealthService")
}
if typed[0].ID != "consul" {
t.Errorf("expected %q to be %q", typed[0].ID, "consul")
}
if len(typed[0].Checks) == 0 {
t.Fatalf("expected to have at least one associated health check")
}
if typed[0].Checks[0].CheckID != "serfHealth" {
t.Errorf("expected %q to be %q", typed[0].Checks[0].CheckID, "serfHealth")
}
}
func TestHealthServiceList_sorts(t *testing.T) {
a := HealthServiceList{
&HealthService{Node: "frontend01", ID: "1"},
&HealthService{Node: "frontend01", ID: "2"},
&HealthService{Node: "frontend02", ID: "1"},
}
b := HealthServiceList{
&HealthService{Node: "frontend02", ID: "1"},
&HealthService{Node: "frontend01", ID: "2"},
&HealthService{Node: "frontend01", ID: "1"},
}
c := HealthServiceList{
&HealthService{Node: "frontend01", ID: "2"},
&HealthService{Node: "frontend01", ID: "1"},
&HealthService{Node: "frontend02", ID: "1"},
}
sort.Stable(a)
sort.Stable(b)
sort.Stable(c)
expected := HealthServiceList{
&HealthService{Node: "frontend01", ID: "1"},
&HealthService{Node: "frontend01", ID: "2"},
&HealthService{Node: "frontend02", ID: "1"},
}
if !reflect.DeepEqual(a, expected) {
t.Fatal("invalid sort")
}
if !reflect.DeepEqual(b, expected) {
t.Fatal("invalid sort")
}
if !reflect.DeepEqual(c, expected) {
t.Fatal("invalid sort")
}
}
func TestServiceDependencyHashCode_isUnique(t *testing.T) {
dep1 := &HealthServices{rawKey: "redis@nyc1"}
dep2 := &HealthServices{rawKey: "redis@nyc2"}
if dep1.HashCode() == dep2.HashCode() {
t.Errorf("expected HashCode to be unique")
}
}
func TestParseHealthServices_emptyString(t *testing.T) {
_, err := ParseHealthServices("")
if err == nil {
t.Fatal("expected error, but nothing was returned")
}
expected := "cannot specify empty health service dependency"
if !strings.Contains(err.Error(), expected) {
t.Errorf("expected error %q to contain %q", err.Error(), expected)
}
}
func TestParseHealthServices_name(t *testing.T) {
sd, err := ParseHealthServices("webapp")
if err != nil {
t.Fatal(err)
}
expected := &HealthServices{
rawKey: "webapp",
Name: "webapp",
StatusFilter: nil,
}
if !reflect.DeepEqual(sd, expected) {
t.Errorf("expected %#v to equal %#v", sd, expected)
}
}
func TestParseHealthServices_nameAndAnyStatus(t *testing.T) {
sd, err := ParseHealthServices("webapp", "passing")
if err != nil {
t.Fatal(err)
}
expected := &HealthServices{
rawKey: "webapp [passing]",
Name: "webapp",
StatusFilter: ServiceStatusFilter{HealthPassing},
}
if !reflect.DeepEqual(sd, expected) {
t.Errorf("expected %#v to equal %#v", sd, expected)
}
}
func TestParseHealthServices_slashName(t *testing.T) {
sd, err := ParseHealthServices("web/app")
if err != nil {
t.Fatal(err)
}
expected := &HealthServices{
rawKey: "web/app",
Name: "web/app",
StatusFilter: nil,
}
if !reflect.DeepEqual(sd, expected) {
t.Errorf("expected %#v to equal %#v", sd, expected)
}
}
func TestParseHealthServices_underscoreName(t *testing.T) {
sd, err := ParseHealthServices("web_app")
if err != nil {
t.Fatal(err)
}
expected := &HealthServices{
rawKey: "web_app",
Name: "web_app",
StatusFilter: nil,
}
if !reflect.DeepEqual(sd, expected) {
t.Errorf("expected %#v to equal %#v", sd, expected)
}
}
func TestParseHealthServices_dotTag(t *testing.T) {
sd, err := ParseHealthServices("first.release.webapp")
if err != nil {
t.Fatal(err)
}
expected := &HealthServices{
rawKey: "first.release.webapp",
Name: "webapp",
Tag: "first.release",
StatusFilter: nil,
}
if !reflect.DeepEqual(sd, expected) {
t.Errorf("expected %#v to equal %#v", sd, expected)
}
}
func TestParseHealthServices_nameTag(t *testing.T) {
sd, err := ParseHealthServices("release.webapp")
if err != nil {
t.Fatal(err)
}
expected := &HealthServices{
rawKey: "release.webapp",
Name: "webapp",
Tag: "release",
StatusFilter: nil,
}
if !reflect.DeepEqual(sd, expected) {
t.Errorf("expected %#v to equal %#v", sd, expected)
}
}
func TestParseHealthServices_nameTagDataCenter(t *testing.T) {
sd, err := ParseHealthServices("release.webapp@nyc1")
if err != nil {
t.Fatal(err)
}
expected := &HealthServices{
rawKey: "release.webapp@nyc1",
Name: "webapp",
Tag: "release",
DataCenter: "nyc1",
StatusFilter: nil,
}
if !reflect.DeepEqual(sd, expected) {
t.Errorf("expected %#v to equal %#v", sd, expected)
}
}
func TestParseHealthServices_nameTagDataCenterPort(t *testing.T) {
sd, err := ParseHealthServices("release.webapp@nyc1:8500")
if err != nil {
t.Fatal(err)
}
expected := &HealthServices{
rawKey: "release.webapp@nyc1:8500",
Name: "webapp",
Tag: "release",
DataCenter: "nyc1",
StatusFilter: nil,
}
if !reflect.DeepEqual(sd, expected) {
t.Errorf("expected %#v to equal %#v", sd, expected)
}
}
func TestParseHealthServices_dataCenterOnly(t *testing.T) {
_, err := ParseHealthServices("@nyc1")
if err == nil {
t.Fatal("expected error, but nothing was returned")
}
expected := "invalid health service dependency format"
if !strings.Contains(err.Error(), expected) {
t.Errorf("expected error %q to contain %q", err.Error(), expected)
}
}
func TestParseHealthServices_nameAndPort(t *testing.T) {
sd, err := ParseHealthServices("webapp:8500")
if err != nil {
t.Fatal(err)
}
expected := &HealthServices{
rawKey: "webapp:8500",
Name: "webapp",
StatusFilter: nil,
}
if !reflect.DeepEqual(sd, expected) {
t.Errorf("expected %#v to equal %#v", sd, expected)
}
}
func TestParseHealthServices_nameAndDataCenter(t *testing.T) {
sd, err := ParseHealthServices("webapp@nyc1")
if err != nil {
t.Fatal(err)
}
expected := &HealthServices{
rawKey: "webapp@nyc1",
Name: "webapp",
DataCenter: "nyc1",
StatusFilter: nil,
}
if !reflect.DeepEqual(sd, expected) {
t.Errorf("expected %#v to equal %#v", sd, expected)
}
}
func TestServiceTagsContains(t *testing.T) {
s := &HealthService{
Node: "node",
Address: "127.0.0.1",
ID: "id",
Name: "name",
Tags: []string{"foo", "baz"},
Port: 1234,
}
if !s.Tags.Contains("foo") {
t.Error("expected Contains to return true for foo.")
}
if s.Tags.Contains("bar") {
t.Error("expected Contains to return false for bar.")
}
if !s.Tags.Contains("baz") {
t.Error("expected Contains to return true for baz.")
}
}
func TestStatusFromChecks_nil(t *testing.T) {
status, err := statusFromChecks(nil)
if err != nil {
t.Fatal(err)
}
expected := "passing"
if status != expected {
t.Fatalf("expected %q to be %q", status, expected)
}
}
func TestStatusFromChecks_badCbeck(t *testing.T) {
checks := []*api.HealthCheck{
&api.HealthCheck{Status: "bacon"},
}
_, err := statusFromChecks(checks)
if err == nil {
t.Fatal("expected error, but nothing was returned")
}
expected := fmt.Sprintf("unknown status: %q", "bacon")
if !strings.Contains(err.Error(), expected) {
t.Fatalf("expected %q to include %q", err.Error(), expected)
}
}
func TestStatusFromChecks_passing(t *testing.T) {
checks := []*api.HealthCheck{
&api.HealthCheck{Status: "passing"},
&api.HealthCheck{Status: "passing"},
&api.HealthCheck{Status: "passing"},
&api.HealthCheck{Status: "passing"},
}
status, err := statusFromChecks(checks)
if err != nil {
t.Fatal(err)
}
expected := "passing"
if status != expected {
t.Fatalf("expected %q to be %q", status, expected)
}
}
func TestStatusFromChecks_warning(t *testing.T) {
checks := []*api.HealthCheck{
&api.HealthCheck{Status: "passing"},
&api.HealthCheck{Status: "warning"},
&api.HealthCheck{Status: "passing"},
&api.HealthCheck{Status: "passing"},
}
status, err := statusFromChecks(checks)
if err != nil {
t.Fatal(err)
}
expected := "warning"
if status != expected {
t.Fatalf("expected %q to be %q", status, expected)
}
}
func TestStatusFromChecks_unknown(t *testing.T) {
checks := []*api.HealthCheck{
&api.HealthCheck{Status: "passing"},
&api.HealthCheck{Status: "warning"},
&api.HealthCheck{Status: "unknown"},
&api.HealthCheck{Status: "passing"},
}
status, err := statusFromChecks(checks)
if err != nil {
t.Fatal(err)
}
expected := "unknown"
if status != expected {
t.Fatalf("expected %q to be %q", status, expected)
}
}
func TestStatusFromChecks_critical(t *testing.T) {
checks := []*api.HealthCheck{
&api.HealthCheck{Status: "passing"},
&api.HealthCheck{Status: "warning"},
&api.HealthCheck{Status: "unknown"},
&api.HealthCheck{Status: "critical"},
}
status, err := statusFromChecks(checks)
if err != nil {
t.Fatal(err)
}
expected := "critical"
if status != expected {
t.Fatalf("expected %q to be %q", status, expected)
}
}
func TestStatusFromChecks_nodeMaintenance(t *testing.T) {
checks := []*api.HealthCheck{
&api.HealthCheck{Status: "passing"},
&api.HealthCheck{Status: "warning"},
&api.HealthCheck{Status: "unknown"},
&api.HealthCheck{Status: "critical"},
&api.HealthCheck{CheckID: "_node_maintenance"},
}
status, err := statusFromChecks(checks)
if err != nil {
t.Fatal(err)
}
expected := "maintenance"
if status != expected {
t.Fatalf("expected %q to be %q", status, expected)
}
}
func TestStatusFromChecks_serviceMaintenance(t *testing.T) {
checks := []*api.HealthCheck{
&api.HealthCheck{Status: "passing"},
&api.HealthCheck{Status: "warning"},
&api.HealthCheck{Status: "unknown"},
&api.HealthCheck{Status: "critical"},
&api.HealthCheck{CheckID: "_service_maintenance:1234"},
}
status, err := statusFromChecks(checks)
if err != nil {
t.Fatal(err)
}
expected := "maintenance"
if status != expected {
t.Fatalf("expected %q to be %q", status, expected)
}
}
// Tests specifically relating to health service filtering
// -------------------------
func TestNewServiceStatusFilter_emptyString(t *testing.T) {
filter, err := NewServiceStatusFilter("")
if err != nil {
t.Fatal(err)
}
var expected ServiceStatusFilter
if !reflect.DeepEqual(filter, expected) {
t.Errorf("expected %#v to be %#v", filter, expected)
}
}
func TestNewServiceStatusFilter_statuses(t *testing.T) {
statuses := []string{
HealthAny,
HealthUnknown,
HealthPassing,
HealthWarning,
HealthCritical,
}
for _, status := range statuses {
filter, err := NewServiceStatusFilter(status)
if err != nil {
t.Fatal(err)
}
expected := ServiceStatusFilter{status}
if !reflect.DeepEqual(filter, expected) {
t.Errorf("expected %#v to be %#v", filter, expected)
}
}
}
func TestNewServiceStatusFilter_any(t *testing.T) {
filter, err := NewServiceStatusFilter("any")
if err != nil {
t.Fatal(err)
}
expected := ServiceStatusFilter{HealthAny}
if !reflect.DeepEqual(filter, expected) {
t.Errorf("expected %#v to be %#v", filter, expected)
}
}
func TestNewServiceStatusFilter_anyWithMore(t *testing.T) {
_, err := NewServiceStatusFilter("any, passing")
if err == nil {
t.Fatal("expected error, but nothing was returned")
}
expected := fmt.Sprintf("cannot specify extra keys when using %q", "any")
if !strings.Contains(err.Error(), expected) {
t.Errorf("expected %q to include %q", err.Error(), expected)
}
}
func TestNewServiceStatusFilter_statusWithSpaces(t *testing.T) {
filter, err := NewServiceStatusFilter("passing, critical, , unknown")
if err != nil {
t.Fatal(err)
}
expected := ServiceStatusFilter{HealthPassing, HealthCritical, HealthUnknown}
if !reflect.DeepEqual(filter, expected) {
t.Errorf("expected %#v to be %#v", filter, expected)
}
}
func TestNewServiceStatusFilter_invalidStatus(t *testing.T) {
_, err := NewServiceStatusFilter("not_a_valid_status")
if err == nil {
t.Fatal("expected error, but nothing was returned")
}
expected := fmt.Sprintf("invalid filter %q", "not_a_valid_status")
if !strings.Contains(err.Error(), expected) {
t.Errorf("expected %q to be %q", err.Error(), expected)
}
}
func TestAccept_any(t *testing.T) {
filter, err := NewServiceStatusFilter("any")
if err != nil {
t.Fatal(err)
}
statuses := []string{"passing", "warning", "critical"}
for _, status := range statuses {
if !filter.Accept(status) {
t.Errorf("expected filter to accept %q", status)
}
}
}
func TestAccept_multiple(t *testing.T) {
filter, err := NewServiceStatusFilter("passing, critical")
if err != nil {
t.Fatal(err)
}
statuses := []string{"passing", "critical"}
for _, status := range statuses {
if !filter.Accept(status) {
t.Errorf("expected filter to accept %q", status)
}
}
if filter.Accept("warning") {
t.Fatalf("expected filter to not accept %q", "warning")
}
}