From 4533cc592ff548d3c995eab244b75a0321bafe50 Mon Sep 17 00:00:00 2001 From: yusing Date: Thu, 28 Nov 2024 06:52:26 +0800 Subject: [PATCH] fixed and updated tests --- internal/route/provider/docker_test.go | 98 ++++++++++++++------------ internal/task/task.go | 8 ++- internal/task/task_test.go | 71 +++++++------------ internal/utils/testing/testing.go | 6 +- 4 files changed, 89 insertions(+), 94 deletions(-) diff --git a/internal/route/provider/docker_test.go b/internal/route/provider/docker_test.go index 0f6f496..d2d2945 100644 --- a/internal/route/provider/docker_test.go +++ b/internal/route/provider/docker_test.go @@ -8,8 +8,10 @@ import ( "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" "github.com/yusing/go-proxy/internal/common" + "github.com/yusing/go-proxy/internal/docker" D "github.com/yusing/go-proxy/internal/docker" E "github.com/yusing/go-proxy/internal/error" + "github.com/yusing/go-proxy/internal/route" "github.com/yusing/go-proxy/internal/route/entry" T "github.com/yusing/go-proxy/internal/route/types" . "github.com/yusing/go-proxy/internal/utils/testing" @@ -17,9 +19,23 @@ import ( var ( dummyNames = []string{"/a"} - p DockerProvider ) +func makeEntries(cont *types.Container, dockerHost ...string) route.RawEntries { + var p DockerProvider + var host string + if len(dockerHost) > 0 { + host = dockerHost[0] + } else { + host = client.DefaultDockerHost + } + entries := E.Must(p.entriesFromContainerLabels(docker.FromDocker(cont, host))) + entries.RangeAll(func(k string, v *route.RawEntry) { + v.Finalize() + }) + return entries +} + func TestApplyLabel(t *testing.T) { pathPatterns := ` - / @@ -41,8 +57,7 @@ func TestApplyLabel(t *testing.T) { "prop4": "value4", }, } - var p DockerProvider - entries, err := p.entriesFromContainerLabels(D.FromDocker(&types.Container{ + entries := makeEntries(&types.Container{ Names: dummyNames, Labels: map[string]string{ D.LabelAliases: "a,b", @@ -65,8 +80,7 @@ func TestApplyLabel(t *testing.T) { "proxy.a.healthcheck.path": "/ping", "proxy.a.healthcheck.interval": "10s", }, - }, client.DefaultDockerHost)) - ExpectNoError(t, err) + }) a, ok := entries.Load("a") ExpectTrue(t, ok) @@ -114,7 +128,7 @@ func TestApplyLabel(t *testing.T) { } func TestApplyLabelWithAlias(t *testing.T) { - entries, err := p.entriesFromContainerLabels(D.FromDocker(&types.Container{ + entries := makeEntries(&types.Container{ Names: dummyNames, State: "running", Labels: map[string]string{ @@ -124,7 +138,7 @@ func TestApplyLabelWithAlias(t *testing.T) { "proxy.b.port": "1234", "proxy.c.scheme": "https", }, - }, client.DefaultDockerHost)) + }) a, ok := entries.Load("a") ExpectTrue(t, ok) b, ok := entries.Load("b") @@ -132,7 +146,6 @@ func TestApplyLabelWithAlias(t *testing.T) { c, ok := entries.Load("c") ExpectTrue(t, ok) - ExpectNoError(t, err) ExpectEqual(t, a.Scheme, "http") ExpectEqual(t, a.Port, "3333") ExpectEqual(t, a.NoTLSVerify, true) @@ -142,7 +155,7 @@ func TestApplyLabelWithAlias(t *testing.T) { } func TestApplyLabelWithRef(t *testing.T) { - entries := E.Must(p.entriesFromContainerLabels(D.FromDocker(&types.Container{ + entries := makeEntries(&types.Container{ Names: dummyNames, State: "running", Labels: map[string]string{ @@ -153,7 +166,7 @@ func TestApplyLabelWithRef(t *testing.T) { "proxy.#3.port": "1111", "proxy.#3.scheme": "https", }, - }, client.DefaultDockerHost))) + }) a, ok := entries.Load("a") ExpectTrue(t, ok) b, ok := entries.Load("b") @@ -179,91 +192,92 @@ func TestApplyLabelWithRefIndexError(t *testing.T) { "proxy.#4.scheme": "https", }, }, "") + var p DockerProvider _, err := p.entriesFromContainerLabels(c) ExpectError(t, ErrAliasRefIndexOutOfRange, err) - _, err = p.entriesFromContainerLabels(D.FromDocker(&types.Container{ + c = D.FromDocker(&types.Container{ Names: dummyNames, State: "running", Labels: map[string]string{ D.LabelAliases: "a,b", "proxy.#0.host": "localhost", }, - }, "")) + }, "") + _, err = p.entriesFromContainerLabels(c) ExpectError(t, ErrAliasRefIndexOutOfRange, err) } func TestDynamicAliases(t *testing.T) { - c := D.FromDocker(&types.Container{ + c := &types.Container{ Names: []string{"app1"}, State: "running", Labels: map[string]string{ "proxy.app1.port": "1234", "proxy.app1_backend.port": "5678", }, - }, client.DefaultDockerHost) + } - raw, ok := E.Must(p.entriesFromContainerLabels(c)).Load("app1") + entries := makeEntries(c) + + raw, ok := entries.Load("app1") ExpectTrue(t, ok) ExpectEqual(t, raw.Scheme, "http") ExpectEqual(t, raw.Port, "1234") - raw, ok = E.Must(p.entriesFromContainerLabels(c)).Load("app1_backend") + raw, ok = entries.Load("app1_backend") ExpectTrue(t, ok) ExpectEqual(t, raw.Scheme, "http") ExpectEqual(t, raw.Port, "5678") } func TestDisableHealthCheck(t *testing.T) { - c := D.FromDocker(&types.Container{ + c := &types.Container{ Names: dummyNames, State: "running", Labels: map[string]string{ "proxy.a.healthcheck.disable": "true", "proxy.a.port": "1234", }, - }, client.DefaultDockerHost) - raw, ok := E.Must(p.entriesFromContainerLabels(c)).Load("a") + } + raw, ok := makeEntries(c).Load("a") ExpectTrue(t, ok) - ExpectEqual(t, raw.HealthCheck, nil) } func TestPublicIPLocalhost(t *testing.T) { - c := D.FromDocker(&types.Container{Names: dummyNames, State: "running"}, client.DefaultDockerHost) - raw, ok := E.Must(p.entriesFromContainerLabels(c)).Load("a") + c := &types.Container{Names: dummyNames, State: "running"} + raw, ok := makeEntries(c).Load("a") ExpectTrue(t, ok) ExpectEqual(t, raw.Container.PublicIP, "127.0.0.1") ExpectEqual(t, raw.Host, raw.Container.PublicIP) } func TestPublicIPRemote(t *testing.T) { - c := D.FromDocker(&types.Container{Names: dummyNames, State: "running"}, "tcp://1.2.3.4:2375") - raw, ok := E.Must(p.entriesFromContainerLabels(c)).Load("a") + c := &types.Container{Names: dummyNames, State: "running"} + raw, ok := makeEntries(c, "tcp://1.2.3.4:2375").Load("a") ExpectTrue(t, ok) ExpectEqual(t, raw.Container.PublicIP, "1.2.3.4") ExpectEqual(t, raw.Host, raw.Container.PublicIP) } func TestPrivateIPLocalhost(t *testing.T) { - c := D.FromDocker(&types.Container{ + c := &types.Container{ Names: dummyNames, NetworkSettings: &types.SummaryNetworkSettings{ Networks: map[string]*network.EndpointSettings{ "network": { IPAddress: "172.17.0.123", }, - }, - }, - }, client.DefaultDockerHost) - raw, ok := E.Must(p.entriesFromContainerLabels(c)).Load("a") + }}} + raw, ok := makeEntries(c).Load("a") ExpectTrue(t, ok) ExpectEqual(t, raw.Container.PrivateIP, "172.17.0.123") ExpectEqual(t, raw.Host, raw.Container.PrivateIP) } func TestPrivateIPRemote(t *testing.T) { - c := D.FromDocker(&types.Container{ + c := &types.Container{ Names: dummyNames, State: "running", NetworkSettings: &types.SummaryNetworkSettings{ @@ -272,9 +286,8 @@ func TestPrivateIPRemote(t *testing.T) { IPAddress: "172.17.0.123", }, }, - }, - }, "tcp://1.2.3.4:2375") - raw, ok := E.Must(p.entriesFromContainerLabels(c)).Load("a") + }} + raw, ok := makeEntries(c, "tcp://1.2.3.4:2375").Load("a") ExpectTrue(t, ok) ExpectEqual(t, raw.Container.PrivateIP, "") ExpectEqual(t, raw.Container.PublicIP, "1.2.3.4") @@ -301,9 +314,7 @@ func TestStreamDefaultValues(t *testing.T) { } t.Run("local", func(t *testing.T) { - c := D.FromDocker(cont, client.DefaultDockerHost) - - raw, ok := E.Must(p.entriesFromContainerLabels(c)).Load("a") + raw, ok := makeEntries(cont).Load("a") ExpectTrue(t, ok) en := E.Must(entry.ValidateEntry(raw)) a := ExpectType[*entry.StreamEntry](t, en) @@ -315,8 +326,7 @@ func TestStreamDefaultValues(t *testing.T) { }) t.Run("remote", func(t *testing.T) { - c := D.FromDocker(cont, "tcp://1.2.3.4:2375") - raw, ok := E.Must(p.entriesFromContainerLabels(c)).Load("a") + raw, ok := makeEntries(cont, "tcp://1.2.3.4:2375").Load("a") ExpectTrue(t, ok) en := E.Must(entry.ValidateEntry(raw)) a := ExpectType[*entry.StreamEntry](t, en) @@ -329,34 +339,34 @@ func TestStreamDefaultValues(t *testing.T) { } func TestExplicitExclude(t *testing.T) { - _, ok := E.Must(p.entriesFromContainerLabels(D.FromDocker(&types.Container{ + _, ok := makeEntries(&types.Container{ Names: dummyNames, Labels: map[string]string{ D.LabelAliases: "a", D.LabelExclude: "true", "proxy.a.no_tls_verify": "true", }, - }, ""))).Load("a") + }, "").Load("a") ExpectFalse(t, ok) } func TestImplicitExcludeDatabase(t *testing.T) { t.Run("mount path detection", func(t *testing.T) { - _, ok := E.Must(p.entriesFromContainerLabels(D.FromDocker(&types.Container{ + _, ok := makeEntries(&types.Container{ Names: dummyNames, Mounts: []types.MountPoint{ {Source: "/data", Destination: "/var/lib/postgresql/data"}, }, - }, ""))).Load("a") + }).Load("a") ExpectFalse(t, ok) }) t.Run("exposed port detection", func(t *testing.T) { - _, ok := E.Must(p.entriesFromContainerLabels(D.FromDocker(&types.Container{ + _, ok := makeEntries(&types.Container{ Names: dummyNames, Ports: []types.Port{ {Type: "tcp", PrivatePort: 5432, PublicPort: 5432}, }, - }, ""))).Load("a") + }).Load("a") ExpectFalse(t, ok) }) } diff --git a/internal/task/task.go b/internal/task/task.go index 3734515..6584a2f 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -24,6 +24,10 @@ func createGlobalTask() (t *task) { return } +func testResetGlobalTask() { + globalTask = createGlobalTask() +} + type ( // Task controls objects' lifetime. // @@ -146,7 +150,7 @@ func CancelGlobalContext() { // If the timeout is exceeded, it prints a list of all tasks that were // still running when the timeout was reached, and their current tree // of subtasks. -func GlobalContextWait(timeout time.Duration) { +func GlobalContextWait(timeout time.Duration) (err error) { done := make(chan struct{}) after := time.After(timeout) go func() { @@ -159,7 +163,7 @@ func GlobalContextWait(timeout time.Duration) { return case <-after: logger.Warn().Msg("Timeout waiting for these tasks to finish:\n" + globalTask.tree()) - return + return context.DeadlineExceeded } } } diff --git a/internal/task/task_test.go b/internal/task/task_test.go index 47f2143..c2076f1 100644 --- a/internal/task/task_test.go +++ b/internal/task/task_test.go @@ -1,4 +1,4 @@ -package task_test +package task import ( "context" @@ -6,13 +6,10 @@ import ( "testing" "time" - . "github.com/yusing/go-proxy/internal/task" . "github.com/yusing/go-proxy/internal/utils/testing" ) func TestTaskCreation(t *testing.T) { - defer CancelGlobalContext() - rootTask := GlobalTask("root-task") subTask := rootTask.Subtask("subtask") @@ -21,8 +18,6 @@ func TestTaskCreation(t *testing.T) { } func TestTaskCancellation(t *testing.T) { - defer CancelGlobalContext() - subTaskDone := make(chan struct{}) rootTask := GlobalTask("root-task") @@ -46,33 +41,10 @@ func TestTaskCancellation(t *testing.T) { } } -func TestGlobalContextCancellation(t *testing.T) { - taskDone := make(chan struct{}) - - rootTask := GlobalTask("root-task") - - go func() { - rootTask.Wait() - close(taskDone) - }() - - CancelGlobalContext() - - select { - case <-taskDone: - err := rootTask.Context().Err() - ExpectError(t, context.Canceled, err) - cause := context.Cause(rootTask.Context()) - ExpectError(t, ErrProgramExiting, cause) - case <-time.After(1 * time.Second): - t.Fatal("subTask context was not canceled as expected") - } -} - func TestOnComplete(t *testing.T) { - defer CancelGlobalContext() + rootTask := GlobalTask("root-task") + task := rootTask.Subtask("test") - task := GlobalTask("test") var value atomic.Int32 task.OnFinished("set value", func() { value.Store(1234) @@ -82,6 +54,7 @@ func TestOnComplete(t *testing.T) { } func TestGlobalContextWait(t *testing.T) { + testResetGlobalTask() defer CancelGlobalContext() rootTask := GlobalTask("root-task") @@ -122,26 +95,34 @@ func TestGlobalContextWait(t *testing.T) { } func TestTimeoutOnGlobalContextWait(t *testing.T) { - defer CancelGlobalContext() + testResetGlobalTask() rootTask := GlobalTask("root-task") - subTask := rootTask.Subtask("subtask") + rootTask.Subtask("subtask") + + ExpectError(t, context.DeadlineExceeded, GlobalContextWait(200*time.Millisecond)) +} + +func TestGlobalContextCancellation(t *testing.T) { + testResetGlobalTask() + + taskDone := make(chan struct{}) + rootTask := GlobalTask("root-task") - done := make(chan struct{}) go func() { - GlobalContextWait(500 * time.Millisecond) - close(done) + rootTask.Wait() + close(taskDone) }() + CancelGlobalContext() + select { - case <-done: - t.Fatal("GlobalContextWait should have timed out") - case <-time.After(200 * time.Millisecond): + case <-taskDone: + err := rootTask.Context().Err() + ExpectError(t, context.Canceled, err) + cause := context.Cause(rootTask.Context()) + ExpectError(t, ErrProgramExiting, cause) + case <-time.After(1 * time.Second): + t.Fatal("subTask context was not canceled as expected") } - - // Ensure clean exit - subTask.Finish("exit") -} - -func TestGlobalContextCancel(t *testing.T) { } diff --git a/internal/utils/testing/testing.go b/internal/utils/testing/testing.go index a812560..d8a9b92 100644 --- a/internal/utils/testing/testing.go +++ b/internal/utils/testing/testing.go @@ -23,7 +23,7 @@ func IgnoreError[Result any](r Result, _ error) Result { func ExpectNoError(t *testing.T, err error) { t.Helper() if err != nil && !reflect.ValueOf(err).IsNil() { - t.Errorf("expected err=nil, got %s", err) + t.Errorf("expected err=nil, got %v", err) t.FailNow() } } @@ -31,7 +31,7 @@ func ExpectNoError(t *testing.T, err error) { func ExpectError(t *testing.T, expected error, err error) { t.Helper() if !errors.Is(err, expected) { - t.Errorf("expected err %s, got %s", expected, err) + t.Errorf("expected err %s, got %v", expected, err) t.FailNow() } } @@ -39,7 +39,7 @@ func ExpectError(t *testing.T, expected error, err error) { func ExpectError2(t *testing.T, input any, expected error, err error) { t.Helper() if !errors.Is(err, expected) { - t.Errorf("%v: expected err %s, got %s", input, expected, err) + t.Errorf("%v: expected err %s, got %v", input, expected, err) t.FailNow() } }