From 8cb44598c0cd475aeda1286732c79460a0352f4f Mon Sep 17 00:00:00 2001 From: Timo Reimann Date: Mon, 17 Jul 2017 13:42:48 +0200 Subject: [PATCH] [marathon] Use test builder. This change introduces the builder pattern to the Marathon unit tests in order to simplify and reduce the amount of testing boilerplate. Additional changes: - Add missing unit tests. - Make all tests look consistent. - Use dedicated type for task states for increased type safety. - Remove obsoleted getApplication function. --- provider/marathon/builder_test.go | 131 +++ provider/marathon/marathon.go | 20 +- provider/marathon/marathon_test.go | 1502 ++++++++++++---------------- testhelpers/helpers.go | 5 + 4 files changed, 800 insertions(+), 858 deletions(-) create mode 100644 provider/marathon/builder_test.go diff --git a/provider/marathon/builder_test.go b/provider/marathon/builder_test.go new file mode 100644 index 000000000..5f226a584 --- /dev/null +++ b/provider/marathon/builder_test.go @@ -0,0 +1,131 @@ +package marathon + +import "github.com/gambol99/go-marathon" + +// Functions related to building applications. + +func createApplication(ops ...func(*marathon.Application)) marathon.Application { + app := marathon.Application{} + app.EmptyLabels() + + for _, op := range ops { + op(&app) + } + + return app +} + +func appID(name string) func(*marathon.Application) { + return func(app *marathon.Application) { + app.Name(name) + } +} + +func appPorts(ports ...int) func(*marathon.Application) { + return func(app *marathon.Application) { + app.Ports = append(app.Ports, ports...) + } +} + +func label(key, value string) func(*marathon.Application) { + return func(app *marathon.Application) { + app.AddLabel(key, value) + } +} + +func healthChecks(checks ...*marathon.HealthCheck) func(*marathon.Application) { + return func(app *marathon.Application) { + for _, check := range checks { + app.AddHealthCheck(*check) + } + } +} + +func portDefinition(port int) func(*marathon.Application) { + return func(app *marathon.Application) { + app.AddPortDefinition(marathon.PortDefinition{ + Port: &port, + }) + } +} + +func ipAddrPerTask(port int) func(*marathon.Application) { + return func(app *marathon.Application) { + p := marathon.Port{ + Number: port, + Name: "port", + } + disc := marathon.Discovery{} + disc.AddPort(p) + ipAddr := marathon.IPAddressPerTask{} + ipAddr.SetDiscovery(disc) + app.SetIPAddressPerTask(ipAddr) + } +} + +// Functions related to building tasks. + +func createTask(ops ...func(*marathon.Task)) marathon.Task { + t := marathon.Task{ + // The vast majority of tests expect the task state to be TASK_RUNNING. + State: string(taskStateRunning), + } + + for _, op := range ops { + op(&t) + } + + return t +} + +func createLocalhostTask(ops ...func(*marathon.Task)) marathon.Task { + t := createTask( + host("localhost"), + ipAddresses("127.0.0.1"), + ) + + for _, op := range ops { + op(&t) + } + + return t +} + +func taskPorts(ports ...int) func(*marathon.Task) { + return func(t *marathon.Task) { + t.Ports = append(t.Ports, ports...) + } +} + +func host(h string) func(*marathon.Task) { + return func(t *marathon.Task) { + t.Host = h + } +} + +func ipAddresses(addresses ...string) func(*marathon.Task) { + return func(t *marathon.Task) { + for _, addr := range addresses { + t.IPAddresses = append(t.IPAddresses, &marathon.IPAddress{ + IPAddress: addr, + Protocol: "tcp", + }) + } + } +} + +func state(s TaskState) func(*marathon.Task) { + return func(t *marathon.Task) { + t.State = string(s) + } +} + +func healthCheckResultLiveness(alive ...bool) func(*marathon.Task) { + return func(t *marathon.Task) { + for _, a := range alive { + t.HealthCheckResults = append(t.HealthCheckResults, &marathon.HealthCheckResult{ + Alive: a, + }) + } + } +} diff --git a/provider/marathon/marathon.go b/provider/marathon/marathon.go index d4e7c155d..905c5a926 100644 --- a/provider/marathon/marathon.go +++ b/provider/marathon/marathon.go @@ -26,7 +26,14 @@ import ( const ( traceMaxScanTokenSize = 1024 * 1024 - taskStateRunning = "TASK_RUNNING" +) + +// TaskState denotes the Mesos state a task can have. +type TaskState string + +const ( + taskStateRunning TaskState = "TASK_RUNNING" + taskStateStaging TaskState = "TASK_STAGING" ) var _ provider.Provider = (*Provider)(nil) @@ -222,7 +229,7 @@ func (p *Provider) applicationFilter(app marathon.Application) bool { } func (p *Provider) taskFilter(task marathon.Task, application marathon.Application) bool { - if task.State != taskStateRunning { + if task.State != string(taskStateRunning) { return false } @@ -254,15 +261,6 @@ func (p *Provider) taskFilter(task marathon.Task, application marathon.Applicati return true } -func getApplication(task marathon.Task, apps []marathon.Application) (marathon.Application, error) { - for _, application := range apps { - if application.ID == task.AppID { - return application, nil - } - } - return marathon.Application{}, errors.New("Application not found: " + task.AppID) -} - func isApplicationEnabled(application marathon.Application, exposedByDefault bool) bool { return exposedByDefault && (*application.Labels)[types.LabelEnable] != "false" || (*application.Labels)[types.LabelEnable] == "true" } diff --git a/provider/marathon/marathon_test.go b/provider/marathon/marathon_test.go index 1e07d9660..0673036d7 100644 --- a/provider/marathon/marathon_test.go +++ b/provider/marathon/marathon_test.go @@ -3,6 +3,7 @@ package marathon import ( "errors" "fmt" + "math" "reflect" "testing" @@ -50,27 +51,12 @@ func TestMarathonLoadConfigNonAPIErrors(t *testing.T) { expectedBackends map[string]*types.Backend }{ { - desc: "simple application", - application: marathon.Application{ - Ports: []int{80}, - Labels: &map[string]string{}, - }, - task: marathon.Task{ - Host: "localhost", - Ports: []int{80}, - IPAddresses: []*marathon.IPAddress{ - { - IPAddress: "127.0.0.1", - Protocol: "tcp", - }, - }, - }, + desc: "simple application", + application: createApplication(appPorts(80)), + task: createLocalhostTask(taskPorts(80)), expectedFrontends: map[string]*types.Frontend{ "frontend-app": { - Backend: "backend-app", - PassHostHeader: true, - BasicAuth: []string{}, - EntryPoints: []string{}, + Backend: "backend-app", Routes: map[string]types.Route{ "route-host-app": { Rule: "Host:app.docker.localhost", @@ -91,21 +77,15 @@ func TestMarathonLoadConfigNonAPIErrors(t *testing.T) { }, }, { - desc: "filtered task", - application: marathon.Application{ - Ports: []int{80}, - Labels: &map[string]string{}, - }, - task: marathon.Task{ - Ports: []int{80}, - State: "TASK_STAGING", - }, + desc: "filtered task", + application: createApplication(appPorts(80)), + task: createLocalhostTask( + taskPorts(80), + state(taskStateStaging), + ), expectedFrontends: map[string]*types.Frontend{ "frontend-app": { - Backend: "backend-app", - PassHostHeader: true, - BasicAuth: []string{}, - EntryPoints: []string{}, + Backend: "backend-app", Routes: map[string]types.Route{ "route-host-app": { Rule: "Host:app.docker.localhost", @@ -117,30 +97,15 @@ func TestMarathonLoadConfigNonAPIErrors(t *testing.T) { }, { desc: "load balancer / circuit breaker labels", - application: marathon.Application{ - - Ports: []int{80}, - Labels: &map[string]string{ - types.LabelBackendLoadbalancerMethod: "drr", - types.LabelBackendCircuitbreakerExpression: "NetworkErrorRatio() > 0.5", - }, - }, - task: marathon.Task{ - Host: "localhost", - Ports: []int{80}, - IPAddresses: []*marathon.IPAddress{ - { - IPAddress: "127.0.0.1", - Protocol: "tcp", - }, - }, - }, + application: createApplication( + appPorts(80), + label(types.LabelBackendLoadbalancerMethod, "drr"), + label(types.LabelBackendCircuitbreakerExpression, "NetworkErrorRatio() > 0.5"), + ), + task: createLocalhostTask(taskPorts(80)), expectedFrontends: map[string]*types.Frontend{ "frontend-app": { - Backend: "backend-app", - PassHostHeader: true, - BasicAuth: []string{}, - EntryPoints: []string{}, + Backend: "backend-app", Routes: map[string]types.Route{ "route-host-app": { Rule: "Host:app.docker.localhost", @@ -167,29 +132,15 @@ func TestMarathonLoadConfigNonAPIErrors(t *testing.T) { }, { desc: "general max connection labels", - application: marathon.Application{ - Ports: []int{80}, - Labels: &map[string]string{ - types.LabelBackendMaxconnAmount: "1000", - types.LabelBackendMaxconnExtractorfunc: "client.ip", - }, - }, - task: marathon.Task{ - Host: "localhost", - Ports: []int{80}, - IPAddresses: []*marathon.IPAddress{ - { - IPAddress: "127.0.0.1", - Protocol: "tcp", - }, - }, - }, + application: createApplication( + appPorts(80), + label(types.LabelBackendMaxconnAmount, "1000"), + label(types.LabelBackendMaxconnExtractorfunc, "client.ip"), + ), + task: createLocalhostTask(taskPorts(80)), expectedFrontends: map[string]*types.Frontend{ "frontend-app": { - Backend: "backend-app", - PassHostHeader: true, - BasicAuth: []string{}, - EntryPoints: []string{}, + Backend: "backend-app", Routes: map[string]types.Route{ "route-host-app": { Rule: "Host:app.docker.localhost", @@ -213,29 +164,15 @@ func TestMarathonLoadConfigNonAPIErrors(t *testing.T) { }, }, { - desc: "max connection amount label", - application: marathon.Application{ - Ports: []int{80}, - Labels: &map[string]string{ - types.LabelBackendMaxconnAmount: "1000", - }, - }, - task: marathon.Task{ - Host: "localhost", - Ports: []int{80}, - IPAddresses: []*marathon.IPAddress{ - { - IPAddress: "127.0.0.1", - Protocol: "tcp", - }, - }, - }, + desc: "max connection amount label only", + application: createApplication( + appPorts(80), + label(types.LabelBackendMaxconnAmount, "1000"), + ), + task: createLocalhostTask(taskPorts(80)), expectedFrontends: map[string]*types.Frontend{ "frontend-app": { - Backend: "backend-app", - PassHostHeader: true, - BasicAuth: []string{}, - EntryPoints: []string{}, + Backend: "backend-app", Routes: map[string]types.Route{ "route-host-app": { Rule: "Host:app.docker.localhost", @@ -256,29 +193,15 @@ func TestMarathonLoadConfigNonAPIErrors(t *testing.T) { }, }, { - desc: "max connection extractor function label", - application: marathon.Application{ - Ports: []int{80}, - Labels: &map[string]string{ - types.LabelBackendMaxconnExtractorfunc: "client.ip", - }, - }, - task: marathon.Task{ - Host: "localhost", - Ports: []int{80}, - IPAddresses: []*marathon.IPAddress{ - { - IPAddress: "127.0.0.1", - Protocol: "tcp", - }, - }, - }, + desc: "max connection extractor function label only", + application: createApplication( + appPorts(80), + label(types.LabelBackendMaxconnExtractorfunc, "client.ip"), + ), + task: createLocalhostTask(taskPorts(80)), expectedFrontends: map[string]*types.Frontend{ "frontend-app": { - Backend: "backend-app", - PassHostHeader: true, - BasicAuth: []string{}, - EntryPoints: []string{}, + Backend: "backend-app", Routes: map[string]types.Route{ "route-host-app": { Rule: "Host:app.docker.localhost", @@ -300,29 +223,18 @@ func TestMarathonLoadConfigNonAPIErrors(t *testing.T) { }, { desc: "health check labels", - application: marathon.Application{ - Ports: []int{80}, - Labels: &map[string]string{ - types.LabelBackendHealthcheckPath: "/path", - types.LabelBackendHealthcheckInterval: "5m", - }, - }, - task: marathon.Task{ - Host: "127.0.0.1", - Ports: []int{80}, - IPAddresses: []*marathon.IPAddress{ - { - IPAddress: "127.0.0.1", - Protocol: "tcp", - }, - }, - }, + application: createApplication( + appPorts(80), + label(types.LabelBackendHealthcheckPath, "/path"), + label(types.LabelBackendHealthcheckInterval, "5m"), + ), + task: createTask( + host("127.0.0.1"), + taskPorts(80), + ), expectedFrontends: map[string]*types.Frontend{ "frontend-app": { - Backend: "backend-app", - PassHostHeader: true, - BasicAuth: []string{}, - EntryPoints: []string{}, + Backend: "backend-app", Routes: map[string]types.Route{ "route-host-app": { Rule: "Host:app.docker.localhost", @@ -351,12 +263,20 @@ func TestMarathonLoadConfigNonAPIErrors(t *testing.T) { c := c t.Run(c.desc, func(t *testing.T) { t.Parallel() + c.application.ID = "/app" c.task.ID = "task" if c.task.State == "" { c.task.State = "TASK_RUNNING" } c.application.Tasks = []*marathon.Task{&c.task} + + for _, frontend := range c.expectedFrontends { + frontend.PassHostHeader = true + frontend.BasicAuth = []string{} + frontend.EntryPoints = []string{} + } + fakeClient := newFakeClient(false, marathon.Applications{Apps: []marathon.Application{c.application}}) provider := &Provider{ @@ -378,159 +298,99 @@ func TestMarathonLoadConfigNonAPIErrors(t *testing.T) { func TestMarathonTaskFilter(t *testing.T) { cases := []struct { - task marathon.Task - application marathon.Application - expected bool - exposedByDefault bool + desc string + task marathon.Task + application marathon.Application + expected bool }{ { - task: marathon.Task{ - AppID: "missing-port", - Ports: []int{}, - }, - application: marathon.Application{ - ID: "missing-port", - Labels: &map[string]string{}, - }, - expected: false, - exposedByDefault: true, + desc: "missing port", + task: createTask(), + application: createApplication(), + expected: false, }, { - task: marathon.Task{ - AppID: "task-not-running", - Ports: []int{80}, - State: "TASK_STAGING", - }, - application: marathon.Application{ - ID: "task-not-running", - Ports: []int{80}, - Labels: &map[string]string{}, - }, - expected: false, - exposedByDefault: true, + desc: "task not running", + task: createTask( + taskPorts(80), + state(taskStateStaging), + ), + application: createApplication(appPorts(80)), + expected: false, }, { - task: marathon.Task{ - AppID: "existing-port", - Ports: []int{80}, - }, - application: marathon.Application{ - ID: "existing-port", - Ports: []int{80}, - Labels: &map[string]string{}, - }, - expected: true, - exposedByDefault: true, + desc: "existing port", + task: createTask(taskPorts(80)), + application: createApplication(appPorts(80)), + expected: true, }, { - task: marathon.Task{ - AppID: "specify-both-port-index-and-number", - Ports: []int{80, 443}, - }, - application: marathon.Application{ - ID: "specify-both-port-index-and-number", - Ports: []int{80, 443}, - Labels: &map[string]string{ - types.LabelPort: "443", - types.LabelPortIndex: "1", - }, - }, - expected: false, - exposedByDefault: true, + desc: "ambiguous port specification", + task: createTask(taskPorts(80, 443)), + application: createApplication( + appPorts(80, 443), + label(types.LabelPort, "443"), + label(types.LabelPortIndex, "1"), + ), + expected: false, }, { - task: marathon.Task{ - AppID: "healthcheck-available", - Ports: []int{80}, - }, - application: marathon.Application{ - ID: "healthcheck-available", - Ports: []int{80}, - Labels: &map[string]string{}, - HealthChecks: &[]marathon.HealthCheck{ - *marathon.NewDefaultHealthCheck(), - }, - }, - expected: true, - exposedByDefault: true, + desc: "healthcheck available", + task: createTask(taskPorts(80)), + application: createApplication( + appPorts(80), + healthChecks(marathon.NewDefaultHealthCheck()), + ), + expected: true, }, { - task: marathon.Task{ - AppID: "healthcheck-false", - Ports: []int{80}, - HealthCheckResults: []*marathon.HealthCheckResult{ - { - Alive: false, - }, - }, - }, - application: marathon.Application{ - ID: "healthcheck-false", - Ports: []int{80}, - Labels: &map[string]string{}, - HealthChecks: &[]marathon.HealthCheck{ - *marathon.NewDefaultHealthCheck(), - }, - }, - expected: false, - exposedByDefault: true, + desc: "healthcheck result false", + task: createTask( + taskPorts(80), + healthCheckResultLiveness(false), + ), + application: createApplication( + appPorts(80), + healthChecks(marathon.NewDefaultHealthCheck()), + ), + expected: false, }, { - task: marathon.Task{ - AppID: "healthcheck-mixed-results", - Ports: []int{80}, - HealthCheckResults: []*marathon.HealthCheckResult{ - { - Alive: true, - }, - { - Alive: false, - }, - }, - }, - application: marathon.Application{ - ID: "healthcheck-mixed-results", - Ports: []int{80}, - Labels: &map[string]string{}, - HealthChecks: &[]marathon.HealthCheck{ - *marathon.NewDefaultHealthCheck(), - }, - }, - expected: false, - exposedByDefault: true, + desc: "healthcheck results mixed", + task: createTask( + taskPorts(80), + healthCheckResultLiveness(true, false), + ), + application: createApplication( + appPorts(80), + healthChecks(marathon.NewDefaultHealthCheck()), + ), + expected: false, }, { - task: marathon.Task{ - AppID: "healthcheck-alive", - Ports: []int{80}, - HealthCheckResults: []*marathon.HealthCheckResult{ - { - Alive: true, - }, - }, - }, - application: marathon.Application{ - ID: "healthcheck-alive", - Ports: []int{80}, - Labels: &map[string]string{}, - HealthChecks: &[]marathon.HealthCheck{ - *marathon.NewDefaultHealthCheck(), - }, - }, - expected: true, - exposedByDefault: true, + desc: "healthcheck result true", + task: createTask( + taskPorts(80), + healthCheckResultLiveness(true), + ), + application: createApplication( + appPorts(80), + healthChecks(marathon.NewDefaultHealthCheck()), + ), + expected: true, }, } - provider := &Provider{} - for i, c := range cases { - if c.task.State == "" { - c.task.State = taskStateRunning - } - actual := provider.taskFilter(c.task, c.application) - if actual != c.expected { - t.Fatalf("App %s (#%d): got %v, expected %v", c.task.AppID, i, actual, c.expected) - } + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { + t.Parallel() + provider := &Provider{} + actual := provider.taskFilter(c.task, c.application) + if actual != c.expected { + t.Errorf("actual %v, expected %v", actual, c.expected) + } + }) } } @@ -542,34 +402,23 @@ func TestMarathonApplicationFilterConstraints(t *testing.T) { expected bool }{ { - desc: "tags missing", - application: marathon.Application{ - ID: "app", - Labels: &map[string]string{}, - }, + desc: "tags missing", + application: createApplication(), marathonLBCompatibility: false, expected: false, }, { - desc: "tag matching", - application: marathon.Application{ - ID: "app", - Labels: &map[string]string{ - types.LabelTags: "valid", - }, - }, + desc: "tag matching", + application: createApplication(label(types.LabelTags, "valid")), marathonLBCompatibility: false, expected: true, }, { desc: "LB compatibility tag matching", - application: marathon.Application{ - ID: "app", - Labels: &map[string]string{ - "HAPROXY_GROUP": "valid", - types.LabelTags: "notvalid", - }, - }, + application: createApplication( + label("HAPROXY_GROUP", "valid"), + label(types.LabelTags, "notvalid"), + ), marathonLBCompatibility: true, expected: true, }, @@ -590,7 +439,7 @@ func TestMarathonApplicationFilterConstraints(t *testing.T) { provider.Constraints = types.Constraints{constraint} actual := provider.applicationFilter(c.application) if actual != c.expected { - t.Fatalf("got %v, expected %v", actual, c.expected) + t.Errorf("got %v, expected %v", actual, c.expected) } }) } @@ -598,46 +447,46 @@ func TestMarathonApplicationFilterConstraints(t *testing.T) { func TestMarathonApplicationFilterEnabled(t *testing.T) { cases := []struct { - desc string - exposed bool - enabledLabel string - expected bool + desc string + exposedByDefault bool + enabledLabel string + expected bool }{ { - desc: "exposed", - exposed: true, - enabledLabel: "", - expected: true, + desc: "exposed", + exposedByDefault: true, + enabledLabel: "", + expected: true, }, { - desc: "exposed and tolerated by valid label value", - exposed: true, - enabledLabel: "true", - expected: true, + desc: "exposed and tolerated by valid label value", + exposedByDefault: true, + enabledLabel: "true", + expected: true, }, { - desc: "exposed and tolerated by invalid label value", - exposed: true, - enabledLabel: "invalid", - expected: true, + desc: "exposed and tolerated by invalid label value", + exposedByDefault: true, + enabledLabel: "invalid", + expected: true, }, { - desc: "exposed but overridden by label", - exposed: true, - enabledLabel: "false", - expected: false, + desc: "exposed but overridden by label", + exposedByDefault: true, + enabledLabel: "false", + expected: false, }, { - desc: "non-exposed", - exposed: false, - enabledLabel: "", - expected: false, + desc: "non-exposed", + exposedByDefault: false, + enabledLabel: "", + expected: false, }, { - desc: "non-exposed but overridden by label", - exposed: false, - enabledLabel: "true", - expected: true, + desc: "non-exposed but overridden by label", + exposedByDefault: false, + enabledLabel: "true", + expected: true, }, } @@ -645,13 +494,8 @@ func TestMarathonApplicationFilterEnabled(t *testing.T) { c := c t.Run(c.desc, func(t *testing.T) { t.Parallel() - provider := &Provider{ExposedByDefault: c.exposed} - app := marathon.Application{ - ID: "app", - Labels: &map[string]string{ - types.LabelEnable: c.enabledLabel, - }, - } + provider := &Provider{ExposedByDefault: c.exposedByDefault} + app := createApplication(label(types.LabelEnable, c.enabledLabel)) if provider.applicationFilter(app) != c.expected { t.Errorf("got unexpected filtering = %t", !c.expected) } @@ -669,184 +513,72 @@ func TestMarathonGetPort(t *testing.T) { expected string }{ { - desc: "port missing", - application: marathon.Application{ - ID: "app", - Labels: &map[string]string{}, - }, - task: marathon.Task{ - AppID: "app", - Ports: []int{}, - }, - expected: "", + desc: "port missing", + application: createApplication(), + task: createTask(), + expected: "", }, { - desc: "explicit port taken", - application: marathon.Application{ - ID: "app", - Labels: &map[string]string{ - types.LabelPort: "80", - }, - }, - task: marathon.Task{ - AppID: "app", - Ports: []int{}, - }, - expected: "80", + desc: "numeric port", + application: createApplication(label(types.LabelPort, "80")), + task: createTask(), + expected: "80", }, { - desc: "illegal explicit port specified", - application: marathon.Application{ - ID: "app", - Labels: &map[string]string{ - types.LabelPort: "foobar", - }, - }, - task: marathon.Task{ - AppID: "app", - Ports: []int{80}, - }, - expected: "", + desc: "string port", + application: createApplication(label(types.LabelPort, "foobar")), + task: createTask(taskPorts(80)), + expected: "", }, { - desc: "illegal explicit port integer specified", - application: marathon.Application{ - ID: "app", - Labels: &map[string]string{ - types.LabelPort: "-1", - }, - }, - task: marathon.Task{ - AppID: "app", - Ports: []int{80}, - }, - expected: "", + desc: "negative port", + application: createApplication(label(types.LabelPort, "-1")), + task: createTask(taskPorts(80)), + expected: "", }, { - desc: "task port available", - application: marathon.Application{ - ID: "app", - Labels: &map[string]string{}, - PortDefinitions: &[]marathon.PortDefinition{ - { - Port: testhelpers.Intp(443), - }, - }, - IPAddressPerTask: &marathon.IPAddressPerTask{ - Discovery: &marathon.Discovery{ - Ports: &[]marathon.Port{ - { - Number: 8000, - }, - }, - }, - }, - }, - task: marathon.Task{ - AppID: "app", - Ports: []int{80}, - }, - expected: "80", + desc: "task port available", + application: createApplication(), + task: createTask(taskPorts(80)), + expected: "80", }, { - desc: "port mapping port available", - application: marathon.Application{ - ID: "app", - Labels: &map[string]string{}, - PortDefinitions: &[]marathon.PortDefinition{ - { - Port: testhelpers.Intp(443), - }, - }, - IPAddressPerTask: &marathon.IPAddressPerTask{ - Discovery: &marathon.Discovery{ - Ports: &[]marathon.Port{ - { - Number: 8000, - }, - }, - }, - }, - }, - task: marathon.Task{ - AppID: "app", - Ports: []int{}, - }, + desc: "port definition available", + application: createApplication( + portDefinition(443), + ), + task: createTask(), expected: "443", }, { - desc: "IP-per-task port available", - application: marathon.Application{ - ID: "app", - Labels: &map[string]string{}, - IPAddressPerTask: &marathon.IPAddressPerTask{ - Discovery: &marathon.Discovery{ - Ports: &[]marathon.Port{ - { - Number: 8000, - }, - }, - }, - }, - }, - task: marathon.Task{ - AppID: "app", - Ports: []int{}, - }, - expected: "8000", + desc: "IP-per-task port available", + application: createApplication(ipAddrPerTask(8000)), + task: createTask(), + expected: "8000", }, { - desc: "first port taken from multiple ports", - application: marathon.Application{ - ID: "app", - Labels: &map[string]string{}, - }, - task: marathon.Task{ - AppID: "app", - Ports: []int{80, 443}, - }, - expected: "80", + desc: "multiple task ports available", + application: createApplication(), + task: createTask(taskPorts(80, 443)), + expected: "80", }, { - desc: "indexed port taken", - application: marathon.Application{ - ID: "app", - Labels: &map[string]string{ - types.LabelPortIndex: "1", - }, - }, - task: marathon.Task{ - AppID: "app", - Ports: []int{80, 443}, - }, - expected: "443", + desc: "numeric port index specified", + application: createApplication(label(types.LabelPortIndex, "1")), + task: createTask(taskPorts(80, 443)), + expected: "443", }, { - desc: "illegal port index specified", - application: marathon.Application{ - ID: "app", - Labels: &map[string]string{ - types.LabelPortIndex: "foobar", - }, - }, - task: marathon.Task{ - AppID: "app", - Ports: []int{80}, - }, - expected: "", + desc: "string port index specified", + application: createApplication(label(types.LabelPortIndex, "foobar")), + task: createTask(taskPorts(80)), + expected: "", }, { - desc: "task port preferred over application port", - application: marathon.Application{ - ID: "app", - Ports: []int{9999}, - Labels: &map[string]string{}, - }, - task: marathon.Task{ - AppID: "app", - Ports: []int{7777}, - }, - expected: "7777", + desc: "task and application ports specified", + application: createApplication(appPorts(9999)), + task: createTask(taskPorts(7777)), + expected: "7777", }, } @@ -856,35 +588,27 @@ func TestMarathonGetPort(t *testing.T) { t.Parallel() actual := provider.getPort(c.task, c.application) if actual != c.expected { - t.Errorf("got %q, want %q", c.expected, actual) + t.Errorf("actual %q, expected %q", c.expected, actual) } }) } } func TestMarathonGetWeight(t *testing.T) { - provider := &Provider{} - cases := []struct { desc string application marathon.Application expected string }{ { - desc: "weight label missing", - application: marathon.Application{ - Labels: &map[string]string{}, - }, - expected: "0", + desc: "label missing", + application: createApplication(), + expected: "0", }, { - desc: "weight label existing", - application: marathon.Application{ - Labels: &map[string]string{ - types.LabelWeight: "10", - }, - }, - expected: "10", + desc: "label existing", + application: createApplication(label(types.LabelWeight, "10")), + expected: "10", }, } @@ -892,69 +616,30 @@ func TestMarathonGetWeight(t *testing.T) { c := c t.Run(c.desc, func(t *testing.T) { t.Parallel() + provider := &Provider{} actual := provider.getWeight(c.application) if actual != c.expected { - t.Fatalf("actual %s, expected %s", actual, c.expected) + t.Errorf("actual %q, expected %q", actual, c.expected) } }) } } func TestMarathonGetDomain(t *testing.T) { - provider := &Provider{ - Domain: "docker.localhost", - } - - applications := []struct { - application marathon.Application - expected string - }{ - { - application: marathon.Application{ - Labels: &map[string]string{}}, - expected: "docker.localhost", - }, - { - application: marathon.Application{ - Labels: &map[string]string{ - types.LabelDomain: "foo.bar", - }, - }, - expected: "foo.bar", - }, - } - - for _, a := range applications { - actual := provider.getDomain(a.application) - if actual != a.expected { - t.Fatalf("expected %q, got %q", a.expected, actual) - } - } -} - -func TestMarathonGetProtocol(t *testing.T) { - provider := &Provider{} - cases := []struct { desc string application marathon.Application expected string }{ { - desc: "protocol label missing", - application: marathon.Application{ - Labels: &map[string]string{}, - }, - expected: "http", + desc: "label missing", + application: createApplication(), + expected: "docker.localhost", }, { - desc: "protocol label existing", - application: marathon.Application{ - Labels: &map[string]string{ - types.LabelProtocol: "https", - }, - }, - expected: "https", + desc: "label existing", + application: createApplication(label(types.LabelDomain, "foo.bar")), + expected: "foo.bar", }, } @@ -962,440 +647,570 @@ func TestMarathonGetProtocol(t *testing.T) { c := c t.Run(c.desc, func(t *testing.T) { t.Parallel() + provider := &Provider{ + Domain: "docker.localhost", + } + actual := provider.getDomain(c.application) + if actual != c.expected { + t.Errorf("actual %q, expected %q", actual, c.expected) + } + }) + } +} + +func TestMarathonGetProtocol(t *testing.T) { + cases := []struct { + desc string + application marathon.Application + expected string + }{ + { + desc: "label missing", + application: createApplication(), + expected: "http", + }, + { + desc: "label existing", + application: createApplication(label(types.LabelProtocol, "https")), + expected: "https", + }, + } + + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { + t.Parallel() + provider := &Provider{} actual := provider.getProtocol(c.application) if actual != c.expected { - t.Errorf("got protocol '%s', want '%s'", actual, c.expected) + t.Errorf("actual %q, expected %q", actual, c.expected) + } + }) + } +} + +func TestMarathonGetSticky(t *testing.T) { + cases := []struct { + desc string + application marathon.Application + expected string + }{ + { + desc: "label missing", + application: createApplication(), + expected: "false", + }, + { + desc: "label existing", + application: createApplication(label(types.LabelBackendLoadbalancerSticky, "true")), + expected: "true", + }, + } + + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { + t.Parallel() + provider := &Provider{} + actual := provider.getSticky(c.application) + if actual != c.expected { + t.Errorf("actual %q, expected %q", actual, c.expected) } }) } } func TestMarathonGetPassHostHeader(t *testing.T) { - provider := &Provider{} - - applications := []struct { + cases := []struct { + desc string application marathon.Application expected string }{ { - application: marathon.Application{ - Labels: &map[string]string{}}, - expected: "true", + desc: "label missing", + application: createApplication(), + expected: "true", }, { - application: marathon.Application{ - Labels: &map[string]string{ - types.LabelFrontendPassHostHeader: "false", - }, - }, - expected: "false", + desc: "label existing", + application: createApplication(label(types.LabelFrontendPassHostHeader, "false")), + expected: "false", }, } - for _, a := range applications { - actual := provider.getPassHostHeader(a.application) - if actual != a.expected { - t.Fatalf("expected %q, got %q", a.expected, actual) - } + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { + t.Parallel() + provider := &Provider{} + actual := provider.getPassHostHeader(c.application) + if actual != c.expected { + t.Errorf("actual %q, expected %q", actual, c.expected) + } + }) + } +} + +func TestMarathonMaxConnAmount(t *testing.T) { + cases := []struct { + desc string + application marathon.Application + expected int64 + }{ + { + desc: "label missing", + application: createApplication(), + expected: math.MaxInt64, + }, + { + desc: "non-integer value", + application: createApplication(label(types.LabelBackendMaxconnAmount, "foobar")), + expected: math.MaxInt64, + }, + { + desc: "label existing", + application: createApplication(label(types.LabelBackendMaxconnAmount, "32")), + expected: 32, + }, + } + + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { + t.Parallel() + provider := &Provider{} + actual := provider.getMaxConnAmount(c.application) + if actual != c.expected { + t.Errorf("actual %d, expected %d", actual, c.expected) + } + }) + } +} + +func TestMarathonGetMaxConnExtractorFunc(t *testing.T) { + cases := []struct { + desc string + application marathon.Application + expected string + }{ + { + desc: "label missing", + application: createApplication(), + expected: "request.host", + }, + { + desc: "label existing", + application: createApplication(label(types.LabelBackendMaxconnExtractorfunc, "client.ip")), + expected: "client.ip", + }, + } + + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { + t.Parallel() + provider := &Provider{} + actual := provider.getMaxConnExtractorFunc(c.application) + if actual != c.expected { + t.Errorf("actual %q, expected %q", actual, c.expected) + } + }) + } +} + +func TestMarathonGetLoadBalancerMethod(t *testing.T) { + cases := []struct { + desc string + application marathon.Application + expected string + }{ + { + desc: "label missing", + application: createApplication(), + expected: "wrr", + }, + { + desc: "label existing", + application: createApplication(label(types.LabelBackendLoadbalancerMethod, "drr")), + expected: "drr", + }, + } + + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { + t.Parallel() + provider := &Provider{} + actual := provider.getLoadBalancerMethod(c.application) + if actual != c.expected { + t.Errorf("actual %q, expected %q", actual, c.expected) + } + }) + } +} + +func TestMarathonGetCircuitBreakerExpression(t *testing.T) { + cases := []struct { + desc string + application marathon.Application + expected string + }{ + { + desc: "label missing", + application: createApplication(), + expected: "NetworkErrorRatio() > 1", + }, + { + desc: "label existing", + application: createApplication(label(types.LabelBackendCircuitbreakerExpression, "NetworkErrorRatio() > 0.5")), + expected: "NetworkErrorRatio() > 0.5", + }, + } + + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { + t.Parallel() + provider := &Provider{} + actual := provider.getCircuitBreakerExpression(c.application) + if actual != c.expected { + t.Errorf("actual %q, expected %q", actual, c.expected) + } + }) } } func TestMarathonGetEntryPoints(t *testing.T) { - provider := &Provider{} - - applications := []struct { + cases := []struct { + desc string application marathon.Application expected []string }{ { - application: marathon.Application{ - Labels: &map[string]string{}}, - expected: []string{}, + desc: "label missing", + application: createApplication(), + expected: []string{}, }, { - application: marathon.Application{ - Labels: &map[string]string{ - types.LabelFrontendEntryPoints: "http,https", - }, - }, - expected: []string{"http", "https"}, + desc: "label existing", + application: createApplication(label(types.LabelFrontendEntryPoints, "http,https")), + expected: []string{"http", "https"}, }, } - for _, a := range applications { - actual := provider.getEntryPoints(a.application) - - if !reflect.DeepEqual(a.expected, actual) { - t.Fatalf("expected %#v, got %#v", a.expected, actual) - } + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { + t.Parallel() + provider := &Provider{} + actual := provider.getEntryPoints(c.application) + if !reflect.DeepEqual(actual, c.expected) { + t.Errorf("actual %#v, expected %#v", actual, c.expected) + } + }) } } func TestMarathonGetFrontendRule(t *testing.T) { - applications := []struct { + cases := []struct { + desc string application marathon.Application expected string marathonLBCompatibility bool }{ { - application: marathon.Application{ - Labels: &map[string]string{}}, + desc: "label missing", + application: createApplication(appID("test")), marathonLBCompatibility: true, - expected: "Host:.docker.localhost", + expected: "Host:test.docker.localhost", }, { - application: marathon.Application{ - ID: "test", - Labels: &map[string]string{ - "HAPROXY_0_VHOST": "foo.bar", - }, - }, + desc: "HAProxy vhost available and LB compat disabled", + application: createApplication( + appID("test"), + label("HAPROXY_0_VHOST", "foo.bar"), + ), marathonLBCompatibility: false, expected: "Host:test.docker.localhost", }, { - application: marathon.Application{ - Labels: &map[string]string{ - types.LabelFrontendRule: "Host:foo.bar", - "HAPROXY_0_VHOST": "notvalid", - }, - }, + desc: "HAProxy vhost available and LB compat enabled", + application: createApplication(label("HAPROXY_0_VHOST", "foo.bar")), marathonLBCompatibility: true, expected: "Host:foo.bar", }, { - application: marathon.Application{ - Labels: &map[string]string{ - "HAPROXY_0_VHOST": "foo.bar", - }, - }, + desc: "frontend rule available", + + application: createApplication( + label(types.LabelFrontendRule, "Host:foo.bar"), + label("HAPROXY_0_VHOST", "unused"), + ), marathonLBCompatibility: true, expected: "Host:foo.bar", }, } - for _, a := range applications { - provider := &Provider{ - Domain: "docker.localhost", - MarathonLBCompatibility: a.marathonLBCompatibility, - } - actual := provider.getFrontendRule(a.application) - if actual != a.expected { - t.Fatalf("expected %q, got %q", a.expected, actual) - } + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { + t.Parallel() + provider := &Provider{ + Domain: "docker.localhost", + MarathonLBCompatibility: c.marathonLBCompatibility, + } + actual := provider.getFrontendRule(c.application) + if actual != c.expected { + t.Errorf("actual %q, expected %q", actual, c.expected) + } + }) } } func TestMarathonGetBackend(t *testing.T) { - provider := &Provider{} - - applications := []struct { + cases := []struct { + desc string application marathon.Application expected string }{ { - application: marathon.Application{ - ID: "foo", - Labels: &map[string]string{ - types.LabelBackend: "bar", - }, - }, - expected: "bar", + desc: "label missing", + application: createApplication(appID("/group/app")), + expected: "-group-app", + }, + { + desc: "label existing", + application: createApplication(label(types.LabelBackend, "bar")), + expected: "bar", }, } - for _, a := range applications { - actual := provider.getBackend(a.application) - if actual != a.expected { - t.Fatalf("expected %q, got %q", a.expected, actual) - } + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { + t.Parallel() + provider := &Provider{} + actual := provider.getBackend(c.application) + if actual != c.expected { + t.Errorf("actual %q, expected %q", actual, c.expected) + } + }) } } func TestMarathonGetSubDomain(t *testing.T) { - providerGroups := &Provider{GroupsAsSubDomains: true} - providerNoGroups := &Provider{GroupsAsSubDomains: false} - - apps := []struct { - path string - expected string - provider *Provider + cases := []struct { + path string + expected string + groupAsSubDomain bool }{ - {"/test", "test", providerNoGroups}, - {"/test", "test", providerGroups}, - {"/a/b/c/d", "d.c.b.a", providerGroups}, - {"/b/a/d/c", "c.d.a.b", providerGroups}, - {"/d/c/b/a", "a.b.c.d", providerGroups}, - {"/c/d/a/b", "b.a.d.c", providerGroups}, - {"/a/b/c/d", "a-b-c-d", providerNoGroups}, - {"/b/a/d/c", "b-a-d-c", providerNoGroups}, - {"/d/c/b/a", "d-c-b-a", providerNoGroups}, - {"/c/d/a/b", "c-d-a-b", providerNoGroups}, + {"/test", "test", false}, + {"/test", "test", true}, + {"/a/b/c/d", "d.c.b.a", true}, + {"/b/a/d/c", "c.d.a.b", true}, + {"/d/c/b/a", "a.b.c.d", true}, + {"/c/d/a/b", "b.a.d.c", true}, + {"/a/b/c/d", "a-b-c-d", false}, + {"/b/a/d/c", "b-a-d-c", false}, + {"/d/c/b/a", "d-c-b-a", false}, + {"/c/d/a/b", "c-d-a-b", false}, } - for _, a := range apps { - actual := a.provider.getSubDomain(a.path) - - if actual != a.expected { - t.Errorf("expected %q, got %q", a.expected, actual) - } + for _, c := range cases { + c := c + t.Run(fmt.Sprintf("path=%s,group=%t", c.path, c.groupAsSubDomain), func(t *testing.T) { + t.Parallel() + provider := &Provider{GroupsAsSubDomains: c.groupAsSubDomain} + actual := provider.getSubDomain(c.path) + if actual != c.expected { + t.Errorf("actual %q, expected %q", actual, c.expected) + } + }) } } func TestMarathonHasHealthCheckLabels(t *testing.T) { - tests := []struct { - desc string - value *string - want bool + cases := []struct { + desc string + value *string + expected bool }{ { - desc: "label missing", - value: nil, - want: false, + desc: "label missing", + value: nil, + expected: false, }, { - desc: "empty path", - value: stringp(""), - want: false, + desc: "empty path", + value: testhelpers.Stringp(""), + expected: false, }, { - desc: "non-empty path", - value: stringp("/path"), - want: true, + desc: "non-empty path", + value: testhelpers.Stringp("/path"), + expected: true, }, } - for _, test := range tests { - test := test - t.Run(test.desc, func(t *testing.T) { + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { t.Parallel() - app := marathon.Application{ - Labels: &map[string]string{}, + app := createApplication() + if c.value != nil { + app.AddLabel(types.LabelBackendHealthcheckPath, *c.value) } - if test.value != nil { - app.AddLabel(types.LabelBackendHealthcheckPath, *test.value) - } - prov := &Provider{} - got := prov.hasHealthCheckLabels(app) - if got != test.want { - t.Errorf("got %t, want %t", got, test.want) + provider := &Provider{} + actual := provider.hasHealthCheckLabels(app) + if actual != c.expected { + t.Errorf("actual %t, expected %t", actual, c.expected) } }) } } func TestMarathonGetHealthCheckPath(t *testing.T) { - tests := []struct { - desc string - value *string - want string + cases := []struct { + desc string + value string + expected string }{ { - desc: "label missing", - value: nil, - want: "", + desc: "label missing", + expected: "", }, { - desc: "path existing", - value: stringp("/path"), - want: "/path", + desc: "path existing", + value: "/path", + expected: "/path", }, } - for _, test := range tests { - test := test - t.Run(test.desc, func(t *testing.T) { + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { t.Parallel() - app := marathon.Application{} - app.EmptyLabels() - if test.value != nil { - app.AddLabel(types.LabelBackendHealthcheckPath, *test.value) + app := createApplication() + if c.value != "" { + app.AddLabel(types.LabelBackendHealthcheckPath, c.value) } - prov := &Provider{} - got := prov.getHealthCheckPath(app) - if got != test.want { - t.Errorf("got %s, want %s", got, test.want) + provider := &Provider{} + actual := provider.getHealthCheckPath(app) + if actual != c.expected { + t.Errorf("actual %q, expected %q", actual, c.expected) } }) } } func TestMarathonGetHealthCheckInterval(t *testing.T) { - tests := []struct { - desc string - value *string - want string + cases := []struct { + desc string + value string + expected string }{ { - desc: "label missing", - value: nil, - want: "", + desc: "label missing", + expected: "", }, { - desc: "interval existing", - value: stringp("5m"), - want: "5m", + desc: "interval existing", + value: "5m", + expected: "5m", }, } - for _, test := range tests { - test := test - t.Run(test.desc, func(t *testing.T) { + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { t.Parallel() - app := marathon.Application{ - Labels: &map[string]string{}, + app := createApplication() + if c.value != "" { + app.AddLabel(types.LabelBackendHealthcheckInterval, c.value) } - if test.value != nil { - app.AddLabel(types.LabelBackendHealthcheckInterval, *test.value) - } - prov := &Provider{} - got := prov.getHealthCheckInterval(app) - if got != test.want { - t.Errorf("got %s, want %s", got, test.want) + provider := &Provider{} + actual := provider.getHealthCheckInterval(app) + if actual != c.expected { + t.Errorf("actual %q, expected %q", actual, c.expected) } }) } } -func stringp(s string) *string { - return &s -} - func TestGetBackendServer(t *testing.T) { - appID := "appId" host := "host" - tests := []struct { + cases := []struct { desc string application marathon.Application - addIPAddrPerTask bool task marathon.Task forceTaskHostname bool - wantServer string + expectedServer string }{ { - desc: "application without IP-per-task", - wantServer: host, + desc: "application without IP-per-task", + application: createApplication(), + expectedServer: host, }, { desc: "task hostname override", - addIPAddrPerTask: true, + application: createApplication(ipAddrPerTask(8000)), forceTaskHostname: true, - wantServer: host, + expectedServer: host, }, { - desc: "task IP address missing", - task: marathon.Task{ - IPAddresses: []*marathon.IPAddress{}, - }, - addIPAddrPerTask: true, - wantServer: "", + desc: "task IP address missing", + application: createApplication(ipAddrPerTask(8000)), + task: createTask(), + expectedServer: "", }, { - desc: "single task IP address", - task: marathon.Task{ - IPAddresses: []*marathon.IPAddress{ - { - IPAddress: "1.1.1.1", - }, - }, - }, - addIPAddrPerTask: true, - wantServer: "1.1.1.1", + desc: "single task IP address", + application: createApplication(ipAddrPerTask(8000)), + task: createTask(ipAddresses("1.1.1.1")), + expectedServer: "1.1.1.1", }, { - desc: "multiple task IP addresses without index label", - task: marathon.Task{ - IPAddresses: []*marathon.IPAddress{ - { - IPAddress: "1.1.1.1", - }, - { - IPAddress: "2.2.2.2", - }, - }, - }, - addIPAddrPerTask: true, - wantServer: "", + desc: "multiple task IP addresses without index label", + application: createApplication(ipAddrPerTask(8000)), + task: createTask(ipAddresses("1.1.1.1", "2.2.2.2")), + expectedServer: "", }, { desc: "multiple task IP addresses with invalid index label", - application: marathon.Application{ - Labels: &map[string]string{"traefik.ipAddressIdx": "invalid"}, - }, - task: marathon.Task{ - IPAddresses: []*marathon.IPAddress{ - { - IPAddress: "1.1.1.1", - }, - { - IPAddress: "2.2.2.2", - }, - }, - }, - addIPAddrPerTask: true, - wantServer: "", + application: createApplication( + label("traefik.ipAddressIdx", "invalid"), + ipAddrPerTask(8000), + ), + task: createTask(ipAddresses("1.1.1.1", "2.2.2.2")), + expectedServer: "", }, { desc: "multiple task IP addresses with valid index label", - application: marathon.Application{ - Labels: &map[string]string{"traefik.ipAddressIdx": "1"}, - }, - task: marathon.Task{ - IPAddresses: []*marathon.IPAddress{ - { - IPAddress: "1.1.1.1", - }, - { - IPAddress: "2.2.2.2", - }, - }, - }, - addIPAddrPerTask: true, - wantServer: "2.2.2.2", + application: createApplication( + label("traefik.ipAddressIdx", "1"), + ipAddrPerTask(8000), + ), + task: createTask(ipAddresses("1.1.1.1", "2.2.2.2")), + expectedServer: "2.2.2.2", }, } - for _, test := range tests { - test := test - t.Run(test.desc, func(t *testing.T) { + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { t.Parallel() - provider := &Provider{ForceTaskHostname: test.forceTaskHostname} - - // Populate application. - if test.application.ID == "" { - test.application.ID = appID - } - if test.application.Labels == nil { - test.application.Labels = &map[string]string{} - } - if test.addIPAddrPerTask { - test.application.IPAddressPerTask = &marathon.IPAddressPerTask{ - Discovery: &marathon.Discovery{ - Ports: &[]marathon.Port{ - { - Number: 8000, - Name: "port", - }, - }, - }, - } - } - - // Populate task. - test.task.AppID = appID - test.task.Host = "host" - - gotServer := provider.getBackendServer(test.task, test.application) - - if gotServer != test.wantServer { - t.Errorf("got server '%s', want '%s'", gotServer, test.wantServer) + provider := &Provider{ForceTaskHostname: c.forceTaskHostname} + c.task.Host = host + actualServer := provider.getBackendServer(c.task, c.application) + if actualServer != c.expectedServer { + t.Errorf("actual %q, expected %q", actualServer, c.expectedServer) } }) } } func TestParseIndex(t *testing.T) { - tests := []struct { + cases := []struct { idxStr string length int shouldSucceed bool @@ -1435,45 +1250,38 @@ func TestParseIndex(t *testing.T) { }, } - for _, test := range tests { - test := test - t.Run(fmt.Sprintf("parseIndex(%s, %d)", test.idxStr, test.length), func(t *testing.T) { + for _, c := range cases { + c := c + t.Run(fmt.Sprintf("parseIndex(%s, %d)", c.idxStr, c.length), func(t *testing.T) { t.Parallel() - parsed, err := parseIndex(test.idxStr, test.length) + parsed, err := parseIndex(c.idxStr, c.length) - if test.shouldSucceed != (err == nil) { - t.Fatalf("got error '%s', want error: %t", err, !test.shouldSucceed) + if c.shouldSucceed != (err == nil) { + t.Fatalf("actual error %q, expected error: %t", err, !c.shouldSucceed) } - if test.shouldSucceed && parsed != test.parsed { - t.Errorf("got parsed index %d, want %d", parsed, test.parsed) + if c.shouldSucceed && parsed != c.parsed { + t.Errorf("actual parsed index %d, expected %d", parsed, c.parsed) } }) } } func TestMarathonGetBasicAuth(t *testing.T) { - provider := &Provider{} - cases := []struct { desc string application marathon.Application expected []string }{ { - desc: "basic auth label is empty", - application: marathon.Application{ - Labels: &map[string]string{}}, - expected: []string{}, + desc: "label missing", + application: createApplication(), + expected: []string{}, }, { - desc: "basic auth label is set with user:password", - application: marathon.Application{ - Labels: &map[string]string{ - types.LabelFrontendAuthBasic: "user:password", - }, - }, - expected: []string{"user:password"}, + desc: "label existing", + application: createApplication(label(types.LabelFrontendAuthBasic, "user:password")), + expected: []string{"user:password"}, }, } @@ -1481,10 +1289,10 @@ func TestMarathonGetBasicAuth(t *testing.T) { c := c t.Run(c.desc, func(t *testing.T) { t.Parallel() - + provider := &Provider{} actual := provider.getBasicAuth(c.application) - if !reflect.DeepEqual(c.expected, actual) { - t.Errorf("expected %q, got %q", c.expected, actual) + if !reflect.DeepEqual(actual, c.expected) { + t.Errorf("actual %q, expected %q", actual, c.expected) } }) } diff --git a/testhelpers/helpers.go b/testhelpers/helpers.go index 91e62eb5d..17172692b 100644 --- a/testhelpers/helpers.go +++ b/testhelpers/helpers.go @@ -12,6 +12,11 @@ func Intp(i int) *int { return &i } +// Stringp returns a pointer to the given string value. +func Stringp(s string) *string { + return &s +} + // MustNewRequest creates a new http get request or panics if it can't func MustNewRequest(method, urlStr string, body io.Reader) *http.Request { request, err := http.NewRequest(method, urlStr, body)