From abc9ec335a58b271de5cb5af087472a2909be12a Mon Sep 17 00:00:00 2001 From: David Gageot Date: Tue, 20 Jan 2026 10:30:31 +0100 Subject: [PATCH 1/4] test: replace context.Background()/context.TODO() with t.Context() Replace manual context creation with t.Context() which is automatically cancelled when the test completes. Go 1.24 modernization pattern. Assisted-By: cagent Signed-off-by: David Gageot --- cmd/compose/options_test.go | 12 +++--------- internal/desktop/client_test.go | 6 +----- pkg/compose/convergence_test.go | 21 ++++++++++----------- pkg/compose/create_test.go | 9 ++++----- pkg/compose/dependencies_test.go | 17 ++++------------- pkg/compose/down_test.go | 17 ++++++++--------- pkg/compose/images_test.go | 6 ++---- pkg/compose/kill_test.go | 10 ++++------ pkg/compose/logs_test.go | 11 ++++------- pkg/compose/ps_test.go | 6 ++---- pkg/compose/publish_test.go | 5 ++--- pkg/compose/stop_test.go | 4 +--- pkg/compose/viz_test.go | 7 ++----- pkg/compose/volumes_test.go | 11 ++++------- pkg/compose/watch_test.go | 2 +- pkg/e2e/cancel_test.go | 2 +- pkg/e2e/up_test.go | 2 +- pkg/watch/debounce_test.go | 2 +- pkg/watch/notify_test.go | 2 +- 19 files changed, 56 insertions(+), 96 deletions(-) diff --git a/cmd/compose/options_test.go b/cmd/compose/options_test.go index db6e8c0a870..1686dfaf77b 100644 --- a/cmd/compose/options_test.go +++ b/cmd/compose/options_test.go @@ -18,7 +18,6 @@ package compose import ( "bytes" - "context" "fmt" "io" "os" @@ -244,16 +243,11 @@ services: } // Set up the context with necessary environment variables - ctx := context.Background() - _ = os.Setenv("TEST_VAR", "test-value") - _ = os.Setenv("API_KEY", "123456") - defer func() { - _ = os.Unsetenv("TEST_VAR") - _ = os.Unsetenv("API_KEY") - }() + t.Setenv("TEST_VAR", "test-value") + t.Setenv("API_KEY", "123456") // Extract variables from the model - info, noVariables, err := extractInterpolationVariablesFromModel(ctx, cli, projectOptions, []string{}) + info, noVariables, err := extractInterpolationVariablesFromModel(t.Context(), cli, projectOptions, []string{}) require.NoError(t, err) require.False(t, noVariables) diff --git a/internal/desktop/client_test.go b/internal/desktop/client_test.go index 0355dd501a3..abc4f33122b 100644 --- a/internal/desktop/client_test.go +++ b/internal/desktop/client_test.go @@ -17,7 +17,6 @@ package desktop import ( - "context" "os" "testing" "time" @@ -34,9 +33,6 @@ func TestClientPing(t *testing.T) { t.Skip("Skipping - COMPOSE_TEST_DESKTOP_ENDPOINT not defined") } - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - client := NewClient(desktopEndpoint) t.Cleanup(func() { _ = client.Close() @@ -44,7 +40,7 @@ func TestClientPing(t *testing.T) { now := time.Now() - ret, err := client.Ping(ctx) + ret, err := client.Ping(t.Context()) require.NoError(t, err) serverTime := time.Unix(0, ret.ServerTime) diff --git a/pkg/compose/convergence_test.go b/pkg/compose/convergence_test.go index 920587cd383..eb4c1adfbde 100644 --- a/pkg/compose/convergence_test.go +++ b/pkg/compose/convergence_test.go @@ -17,7 +17,6 @@ package compose import ( - "context" "fmt" "strings" "testing" @@ -95,7 +94,7 @@ func TestServiceLinks(t *testing.T) { c := testContainer("db", dbContainerName, false) apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil) - links, err := tested.(*composeService).getLinks(context.Background(), testProject, s, 1) + links, err := tested.(*composeService).getLinks(t.Context(), testProject, s, 1) assert.NilError(t, err) assert.Equal(t, len(links), 3) @@ -118,7 +117,7 @@ func TestServiceLinks(t *testing.T) { c := testContainer("db", dbContainerName, false) apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil) - links, err := tested.(*composeService).getLinks(context.Background(), testProject, s, 1) + links, err := tested.(*composeService).getLinks(t.Context(), testProject, s, 1) assert.NilError(t, err) assert.Equal(t, len(links), 3) @@ -141,7 +140,7 @@ func TestServiceLinks(t *testing.T) { c := testContainer("db", dbContainerName, false) apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil) - links, err := tested.(*composeService).getLinks(context.Background(), testProject, s, 1) + links, err := tested.(*composeService).getLinks(t.Context(), testProject, s, 1) assert.NilError(t, err) assert.Equal(t, len(links), 3) @@ -165,7 +164,7 @@ func TestServiceLinks(t *testing.T) { c := testContainer("db", dbContainerName, false) apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil) - links, err := tested.(*composeService).getLinks(context.Background(), testProject, s, 1) + links, err := tested.(*composeService).getLinks(t.Context(), testProject, s, 1) assert.NilError(t, err) assert.Equal(t, len(links), 4) @@ -202,7 +201,7 @@ func TestServiceLinks(t *testing.T) { } apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptionsOneOff).Return([]container.Summary{c}, nil) - links, err := tested.(*composeService).getLinks(context.Background(), testProject, s, 1) + links, err := tested.(*composeService).getLinks(t.Context(), testProject, s, 1) assert.NilError(t, err) assert.Equal(t, len(links), 3) @@ -233,7 +232,7 @@ func TestWaitDependencies(t *testing.T) { "db": {Condition: ServiceConditionRunningOrHealthy}, "redis": {Condition: ServiceConditionRunningOrHealthy}, } - assert.NilError(t, tested.(*composeService).waitDependencies(context.Background(), &project, "", dependencies, nil, 0)) + assert.NilError(t, tested.(*composeService).waitDependencies(t.Context(), &project, "", dependencies, nil, 0)) }) t.Run("should skip dependencies with condition service_started", func(t *testing.T) { dbService := types.ServiceConfig{Name: "db", Scale: intPtr(1)} @@ -246,7 +245,7 @@ func TestWaitDependencies(t *testing.T) { "db": {Condition: types.ServiceConditionStarted, Required: true}, "redis": {Condition: types.ServiceConditionStarted, Required: true}, } - assert.NilError(t, tested.(*composeService).waitDependencies(context.Background(), &project, "", dependencies, nil, 0)) + assert.NilError(t, tested.(*composeService).waitDependencies(t.Context(), &project, "", dependencies, nil, 0)) }) } @@ -260,7 +259,7 @@ func TestIsServiceHealthy(t *testing.T) { assert.NilError(t, err) cli.EXPECT().Client().Return(apiClient).AnyTimes() - ctx := context.Background() + ctx := t.Context() t.Run("disabled healthcheck with fallback to running", func(t *testing.T) { containerID := "test-container-id" @@ -475,7 +474,7 @@ func TestCreateMobyContainer(t *testing.T) { Aliases: []string{"bork-test-0"}, })) - _, err = tested.(*composeService).createMobyContainer(context.Background(), &project, service, "test", 0, nil, createOptions{ + _, err = tested.(*composeService).createMobyContainer(t.Context(), &project, service, "test", 0, nil, createOptions{ Labels: make(types.Labels), }) assert.NilError(t, err) @@ -561,7 +560,7 @@ func TestCreateMobyContainer(t *testing.T) { NetworkSettings: &container.NetworkSettings{}, }, nil) - _, err = tested.(*composeService).createMobyContainer(context.Background(), &project, service, "test", 0, nil, createOptions{ + _, err = tested.(*composeService).createMobyContainer(t.Context(), &project, service, "test", 0, nil, createOptions{ Labels: make(types.Labels), }) assert.NilError(t, err) diff --git a/pkg/compose/create_test.go b/pkg/compose/create_test.go index a3cc5406929..41cd3bcb8ac 100644 --- a/pkg/compose/create_test.go +++ b/pkg/compose/create_test.go @@ -17,7 +17,6 @@ package compose import ( - "context" "os" "path/filepath" "sort" @@ -164,7 +163,7 @@ func TestBuildContainerMountOptions(t *testing.T) { } mock.EXPECT().ImageInspect(gomock.Any(), "myProject-myService").AnyTimes().Return(image.InspectResponse{}, nil) - mounts, err := s.buildContainerMountOptions(context.TODO(), project, project.Services["myService"], inherit) + mounts, err := s.buildContainerMountOptions(t.Context(), project, project.Services["myService"], inherit) sort.Slice(mounts, func(i, j int) bool { return mounts[i].Target < mounts[j].Target }) @@ -176,7 +175,7 @@ func TestBuildContainerMountOptions(t *testing.T) { assert.Equal(t, mounts[2].VolumeOptions.Subpath, "etc") assert.Equal(t, mounts[3].Target, "\\\\.\\pipe\\docker_engine") - mounts, err = s.buildContainerMountOptions(context.TODO(), project, project.Services["myService"], inherit) + mounts, err = s.buildContainerMountOptions(t.Context(), project, project.Services["myService"], inherit) sort.Slice(mounts, func(i, j int) bool { return mounts[i].Target < mounts[j].Target }) @@ -435,7 +434,7 @@ volumes: } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - p, err := composeloader.LoadWithContext(context.TODO(), composetypes.ConfigDetails{ + p, err := composeloader.LoadWithContext(t.Context(), composetypes.ConfigDetails{ ConfigFiles: []composetypes.ConfigFile{ { Filename: "test", @@ -448,7 +447,7 @@ volumes: }) assert.NilError(t, err) s := &composeService{} - binds, mounts, err := s.buildContainerVolumes(context.TODO(), *p, p.Services["test"], nil) + binds, mounts, err := s.buildContainerVolumes(t.Context(), *p, p.Services["test"], nil) assert.NilError(t, err) assert.DeepEqual(t, tt.binds, binds) assert.DeepEqual(t, tt.mounts, mounts) diff --git a/pkg/compose/dependencies_test.go b/pkg/compose/dependencies_test.go index 56e9298b500..b22b68b9908 100644 --- a/pkg/compose/dependencies_test.go +++ b/pkg/compose/dependencies_test.go @@ -71,9 +71,6 @@ func TestTraversalWithMultipleParents(t *testing.T) { project.Services[name] = svc } - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - svc := make(chan string, 10) seen := make(map[string]int) done := make(chan struct{}) @@ -84,7 +81,7 @@ func TestTraversalWithMultipleParents(t *testing.T) { done <- struct{}{} }() - err := InDependencyOrder(ctx, &project, func(ctx context.Context, service string) error { + err := InDependencyOrder(t.Context(), &project, func(ctx context.Context, service string) error { svc <- service return nil }) @@ -99,11 +96,8 @@ func TestTraversalWithMultipleParents(t *testing.T) { } func TestInDependencyUpCommandOrder(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - var order []string - err := InDependencyOrder(ctx, createTestProject(), func(ctx context.Context, service string) error { + err := InDependencyOrder(t.Context(), createTestProject(), func(ctx context.Context, service string) error { order = append(order, service) return nil }) @@ -112,11 +106,8 @@ func TestInDependencyUpCommandOrder(t *testing.T) { } func TestInDependencyReverseDownCommandOrder(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - var order []string - err := InReverseDependencyOrder(ctx, createTestProject(), func(ctx context.Context, service string) error { + err := InReverseDependencyOrder(t.Context(), createTestProject(), func(ctx context.Context, service string) error { order = append(order, service) return nil }) @@ -429,7 +420,7 @@ func TestWith_RootNodesAndUp(t *testing.T) { return nil }) WithRootNodesAndDown(tt.nodes)(gt) - err := gt.visit(context.TODO(), graph) + err := gt.visit(t.Context(), graph) assert.NilError(t, err) sort.Strings(visited) assert.DeepEqual(t, tt.want, visited) diff --git a/pkg/compose/down_test.go b/pkg/compose/down_test.go index 8966617504d..0b5852a2fa3 100644 --- a/pkg/compose/down_test.go +++ b/pkg/compose/down_test.go @@ -17,7 +17,6 @@ package compose import ( - "context" "fmt" "os" "strings" @@ -90,7 +89,7 @@ func TestDown(t *testing.T) { api.EXPECT().NetworkRemove(gomock.Any(), "abc123").Return(nil) api.EXPECT().NetworkRemove(gomock.Any(), "def456").Return(nil) - err = tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{}) + err = tested.Down(t.Context(), strings.ToLower(testProject), compose.DownOptions{}) assert.NilError(t, err) } @@ -139,7 +138,7 @@ func TestDownWithGivenServices(t *testing.T) { api.EXPECT().NetworkInspect(gomock.Any(), "abc123", gomock.Any()).Return(network.Inspect{ID: "abc123"}, nil) api.EXPECT().NetworkRemove(gomock.Any(), "abc123").Return(nil) - err = tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{ + err = tested.Down(t.Context(), strings.ToLower(testProject), compose.DownOptions{ Services: []string{"service1", "not-running-service"}, }) assert.NilError(t, err) @@ -175,7 +174,7 @@ func TestDownWithSpecifiedServiceButTheServicesAreNotRunning(t *testing.T) { {ID: "def456", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}}, }, nil) - err = tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{ + err = tested.Down(t.Context(), strings.ToLower(testProject), compose.DownOptions{ Services: []string{"not-running-service1", "not-running-service2"}, }) assert.NilError(t, err) @@ -227,7 +226,7 @@ func TestDownRemoveOrphans(t *testing.T) { api.EXPECT().NetworkInspect(gomock.Any(), "abc123", gomock.Any()).Return(network.Inspect{ID: "abc123"}, nil) api.EXPECT().NetworkRemove(gomock.Any(), "abc123").Return(nil) - err = tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{RemoveOrphans: true}) + err = tested.Down(t.Context(), strings.ToLower(testProject), compose.DownOptions{RemoveOrphans: true}) assert.NilError(t, err) } @@ -259,7 +258,7 @@ func TestDownRemoveVolumes(t *testing.T) { api.EXPECT().VolumeRemove(gomock.Any(), "myProject_volume", true).Return(nil) - err = tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{Volumes: true}) + err = tested.Down(t.Context(), strings.ToLower(testProject), compose.DownOptions{Volumes: true}) assert.NilError(t, err) } @@ -346,7 +345,7 @@ func TestDownRemoveImages(t *testing.T) { t.Log("-> docker compose down --rmi=local") opts.Images = "local" - err = tested.Down(context.Background(), strings.ToLower(testProject), opts) + err = tested.Down(t.Context(), strings.ToLower(testProject), opts) assert.NilError(t, err) otherImagesToBeRemoved := []string{ @@ -361,7 +360,7 @@ func TestDownRemoveImages(t *testing.T) { t.Log("-> docker compose down --rmi=all") opts.Images = "all" - err = tested.Down(context.Background(), strings.ToLower(testProject), opts) + err = tested.Down(t.Context(), strings.ToLower(testProject), opts) assert.NilError(t, err) } @@ -406,7 +405,7 @@ func TestDownRemoveImages_NoLabel(t *testing.T) { api.EXPECT().ImageRemove(gomock.Any(), "testproject-service1:latest", image.RemoveOptions{}).Return(nil, nil) - err = tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{Images: "local"}) + err = tested.Down(t.Context(), strings.ToLower(testProject), compose.DownOptions{Images: "local"}) assert.NilError(t, err) } diff --git a/pkg/compose/images_test.go b/pkg/compose/images_test.go index 9c0367d8d69..b6eb9cce138 100644 --- a/pkg/compose/images_test.go +++ b/pkg/compose/images_test.go @@ -17,7 +17,6 @@ package compose import ( - "context" "strings" "testing" "time" @@ -40,7 +39,6 @@ func TestImages(t *testing.T) { tested, err := NewComposeService(cli) assert.NilError(t, err) - ctx := context.Background() args := filters.NewArgs(projectFilter(strings.ToLower(testProject))) listOpts := container.ListOptions{All: true, Filters: args} api.EXPECT().ServerVersion(gomock.Any()).Return(types.Version{APIVersion: "1.96"}, nil).AnyTimes() @@ -56,9 +54,9 @@ func TestImages(t *testing.T) { c2 := containerDetail("service1", "456", "running", "bar:2") c2.Ports = []container.Port{{PublicPort: 80, PrivatePort: 90, IP: "localhost"}} c3 := containerDetail("service2", "789", "exited", "foo:1") - api.EXPECT().ContainerList(ctx, listOpts).Return([]container.Summary{c1, c2, c3}, nil) + api.EXPECT().ContainerList(t.Context(), listOpts).Return([]container.Summary{c1, c2, c3}, nil) - images, err := tested.Images(ctx, strings.ToLower(testProject), compose.ImagesOptions{}) + images, err := tested.Images(t.Context(), strings.ToLower(testProject), compose.ImagesOptions{}) expected := map[string]compose.ImageSummary{ "123": { diff --git a/pkg/compose/kill_test.go b/pkg/compose/kill_test.go index b25dc48e6f8..a70228a6569 100644 --- a/pkg/compose/kill_test.go +++ b/pkg/compose/kill_test.go @@ -45,8 +45,7 @@ func TestKillAll(t *testing.T) { name := strings.ToLower(testProject) - ctx := context.Background() - api.EXPECT().ContainerList(ctx, container.ListOptions{ + api.EXPECT().ContainerList(t.Context(), container.ListOptions{ Filters: filters.NewArgs(projectFilter(name), hasConfigHashLabel()), }).Return( []container.Summary{testContainer("service1", "123", false), testContainer("service1", "456", false), testContainer("service2", "789", false)}, nil) @@ -64,7 +63,7 @@ func TestKillAll(t *testing.T) { api.EXPECT().ContainerKill(anyCancellableContext(), "456", "").Return(nil) api.EXPECT().ContainerKill(anyCancellableContext(), "789", "").Return(nil) - err = tested.Kill(ctx, name, compose.KillOptions{}) + err = tested.Kill(t.Context(), name, compose.KillOptions{}) assert.NilError(t, err) } @@ -82,8 +81,7 @@ func TestKillSignal(t *testing.T) { Filters: filters.NewArgs(projectFilter(name), serviceFilter(serviceName), hasConfigHashLabel()), } - ctx := context.Background() - api.EXPECT().ContainerList(ctx, listOptions).Return([]container.Summary{testContainer(serviceName, "123", false)}, nil) + api.EXPECT().ContainerList(t.Context(), listOptions).Return([]container.Summary{testContainer(serviceName, "123", false)}, nil) api.EXPECT().VolumeList( gomock.Any(), volume.ListOptions{ @@ -96,7 +94,7 @@ func TestKillSignal(t *testing.T) { }, nil) api.EXPECT().ContainerKill(anyCancellableContext(), "123", "SIGTERM").Return(nil) - err = tested.Kill(ctx, name, compose.KillOptions{Services: []string{serviceName}, Signal: "SIGTERM"}) + err = tested.Kill(t.Context(), name, compose.KillOptions{Services: []string{serviceName}, Signal: "SIGTERM"}) assert.NilError(t, err) } diff --git a/pkg/compose/logs_test.go b/pkg/compose/logs_test.go index d2292a753f7..b5823c908da 100644 --- a/pkg/compose/logs_test.go +++ b/pkg/compose/logs_test.go @@ -17,7 +17,6 @@ package compose import ( - "context" "io" "strings" "sync" @@ -44,8 +43,7 @@ func TestComposeService_Logs_Demux(t *testing.T) { name := strings.ToLower(testProject) - ctx := context.Background() - api.EXPECT().ContainerList(ctx, containerType.ListOptions{ + api.EXPECT().ContainerList(t.Context(), containerType.ListOptions{ All: true, Filters: filters.NewArgs(oneOffFilter(false), projectFilter(name), hasConfigHashLabel()), }).Return( @@ -87,7 +85,7 @@ func TestComposeService_Logs_Demux(t *testing.T) { } consumer := &testLogConsumer{} - err = tested.Logs(ctx, name, consumer, opts) + err = tested.Logs(t.Context(), name, consumer, opts) require.NoError(t, err) require.Equal( @@ -114,8 +112,7 @@ func TestComposeService_Logs_ServiceFiltering(t *testing.T) { name := strings.ToLower(testProject) - ctx := context.Background() - api.EXPECT().ContainerList(ctx, containerType.ListOptions{ + api.EXPECT().ContainerList(t.Context(), containerType.ListOptions{ All: true, Filters: filters.NewArgs(oneOffFilter(false), projectFilter(name), hasConfigHashLabel()), }).Return( @@ -157,7 +154,7 @@ func TestComposeService_Logs_ServiceFiltering(t *testing.T) { opts := compose.LogOptions{ Project: proj, } - err = tested.Logs(ctx, name, consumer, opts) + err = tested.Logs(t.Context(), name, consumer, opts) require.NoError(t, err) require.Equal(t, []string{"hello c1"}, consumer.LogsForContainer("c1")) diff --git a/pkg/compose/ps_test.go b/pkg/compose/ps_test.go index c76bfdfd235..b006d2f57fa 100644 --- a/pkg/compose/ps_test.go +++ b/pkg/compose/ps_test.go @@ -17,7 +17,6 @@ package compose import ( - "context" "strings" "testing" @@ -37,7 +36,6 @@ func TestPs(t *testing.T) { tested, err := NewComposeService(cli) assert.NilError(t, err) - ctx := context.Background() args := filters.NewArgs(projectFilter(strings.ToLower(testProject)), hasConfigHashLabel()) args.Add("label", "com.docker.compose.oneoff=False") listOpts := containerType.ListOptions{Filters: args, All: false} @@ -45,12 +43,12 @@ func TestPs(t *testing.T) { c2, inspect2 := containerDetails("service1", "456", containerType.StateRunning, "", 0) c2.Ports = []containerType.Port{{PublicPort: 80, PrivatePort: 90, IP: "localhost"}} c3, inspect3 := containerDetails("service2", "789", containerType.StateExited, "", 130) - api.EXPECT().ContainerList(ctx, listOpts).Return([]containerType.Summary{c1, c2, c3}, nil) + api.EXPECT().ContainerList(t.Context(), listOpts).Return([]containerType.Summary{c1, c2, c3}, nil) api.EXPECT().ContainerInspect(anyCancellableContext(), "123").Return(inspect1, nil) api.EXPECT().ContainerInspect(anyCancellableContext(), "456").Return(inspect2, nil) api.EXPECT().ContainerInspect(anyCancellableContext(), "789").Return(inspect3, nil) - containers, err := tested.Ps(ctx, strings.ToLower(testProject), compose.PsOptions{}) + containers, err := tested.Ps(t.Context(), strings.ToLower(testProject), compose.PsOptions{}) expected := []compose.ContainerSummary{ { diff --git a/pkg/compose/publish_test.go b/pkg/compose/publish_test.go index 52398af77c2..8f91f663e69 100644 --- a/pkg/compose/publish_test.go +++ b/pkg/compose/publish_test.go @@ -17,7 +17,6 @@ package compose import ( - "context" "slices" "testing" @@ -32,7 +31,7 @@ import ( ) func Test_createLayers(t *testing.T) { - project, err := loader.LoadWithContext(context.TODO(), types.ConfigDetails{ + project, err := loader.LoadWithContext(t.Context(), types.ConfigDetails{ WorkingDir: "testdata/publish/", Environment: types.Mapping{}, ConfigFiles: []types.ConfigFile{ @@ -45,7 +44,7 @@ func Test_createLayers(t *testing.T) { project.ComposeFiles = []string{"testdata/publish/compose.yaml"} service := &composeService{} - layers, err := service.createLayers(context.TODO(), project, api.PublishOptions{ + layers, err := service.createLayers(t.Context(), project, api.PublishOptions{ WithEnvironment: true, }) assert.NilError(t, err) diff --git a/pkg/compose/stop_test.go b/pkg/compose/stop_test.go index 2508be06bff..adecd1d615b 100644 --- a/pkg/compose/stop_test.go +++ b/pkg/compose/stop_test.go @@ -17,7 +17,6 @@ package compose import ( - "context" "strings" "testing" "time" @@ -41,7 +40,6 @@ func TestStopTimeout(t *testing.T) { tested, err := NewComposeService(cli) assert.NilError(t, err) - ctx := context.Background() api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return( []container.Summary{ testContainer("service1", "123", false), @@ -63,7 +61,7 @@ func TestStopTimeout(t *testing.T) { api.EXPECT().ContainerStop(gomock.Any(), "456", stopConfig).Return(nil) api.EXPECT().ContainerStop(gomock.Any(), "789", stopConfig).Return(nil) - err = tested.Stop(ctx, strings.ToLower(testProject), compose.StopOptions{ + err = tested.Stop(t.Context(), strings.ToLower(testProject), compose.StopOptions{ Timeout: &timeout, }) assert.NilError(t, err) diff --git a/pkg/compose/viz_test.go b/pkg/compose/viz_test.go index ae66de34f41..dad8bac36a3 100644 --- a/pkg/compose/viz_test.go +++ b/pkg/compose/viz_test.go @@ -17,7 +17,6 @@ package compose import ( - "context" "strconv" "testing" @@ -119,10 +118,8 @@ func TestViz(t *testing.T) { tested, err := NewComposeService(cli) require.NoError(t, err) - ctx := context.Background() - t.Run("viz (no ports, networks or image)", func(t *testing.T) { - graphStr, err := tested.Viz(ctx, &project, compose.VizOptions{ + graphStr, err := tested.Viz(t.Context(), &project, compose.VizOptions{ Indentation: " ", IncludePorts: false, IncludeImageName: false, @@ -181,7 +178,7 @@ func TestViz(t *testing.T) { }) t.Run("viz (with ports, networks and image)", func(t *testing.T) { - graphStr, err := tested.Viz(ctx, &project, compose.VizOptions{ + graphStr, err := tested.Viz(t.Context(), &project, compose.VizOptions{ Indentation: "\t", IncludePorts: true, IncludeImageName: true, diff --git a/pkg/compose/volumes_test.go b/pkg/compose/volumes_test.go index 1fb99297a3d..85a838eaeed 100644 --- a/pkg/compose/volumes_test.go +++ b/pkg/compose/volumes_test.go @@ -17,7 +17,6 @@ package compose import ( - "context" "testing" "github.com/docker/docker/api/types/container" @@ -58,7 +57,6 @@ func TestVolumes(t *testing.T) { }, } - ctx := context.Background() args := filters.NewArgs(projectFilter(testProject)) listOpts := container.ListOptions{Filters: args} volumeListArgs := filters.NewArgs(projectFilter(testProject)) @@ -68,20 +66,19 @@ func TestVolumes(t *testing.T) { } containerReturn := []container.Summary{c1, c2} - // Mock API calls - mockApi.EXPECT().ContainerList(ctx, listOpts).Times(2).Return(containerReturn, nil) - mockApi.EXPECT().VolumeList(ctx, volumeListOpts).Times(2).Return(volumeReturn, nil) + mockApi.EXPECT().ContainerList(t.Context(), listOpts).Times(2).Return(containerReturn, nil) + mockApi.EXPECT().VolumeList(t.Context(), volumeListOpts).Times(2).Return(volumeReturn, nil) // Test without service filter - should return all project volumes volumeOptions := api.VolumesOptions{} - volumes, err := tested.Volumes(ctx, testProject, volumeOptions) + volumes, err := tested.Volumes(t.Context(), testProject, volumeOptions) expected := []api.VolumesSummary{vol1, vol2, vol3} assert.NilError(t, err) assert.DeepEqual(t, volumes, expected) // Test with service filter - should only return volumes used by service1 volumeOptions = api.VolumesOptions{Services: []string{"service1"}} - volumes, err = tested.Volumes(ctx, testProject, volumeOptions) + volumes, err = tested.Volumes(t.Context(), testProject, volumeOptions) expected = []api.VolumesSummary{vol1, vol2} assert.NilError(t, err) assert.DeepEqual(t, volumes, expected) diff --git a/pkg/compose/watch_test.go b/pkg/compose/watch_test.go index 560784cd5e4..956ca4e27e7 100644 --- a/pkg/compose/watch_test.go +++ b/pkg/compose/watch_test.go @@ -95,7 +95,7 @@ func TestWatch_Sync(t *testing.T) { // cli.EXPECT().Client().Return(apiClient).AnyTimes() - ctx, cancelFunc := context.WithCancel(context.Background()) + ctx, cancelFunc := context.WithCancel(t.Context()) t.Cleanup(cancelFunc) proj := types.Project{ diff --git a/pkg/e2e/cancel_test.go b/pkg/e2e/cancel_test.go index d3c64155d2f..64f3ff609a9 100644 --- a/pkg/e2e/cancel_test.go +++ b/pkg/e2e/cancel_test.go @@ -39,7 +39,7 @@ func TestComposeCancel(t *testing.T) { t.Run("metrics on cancel Compose build", func(t *testing.T) { const buildProjectPath = "fixtures/build-infinite/compose.yaml" - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(t.Context()) defer cancel() // require a separate groupID from the process running tests, in order to simulate ctrl+C from a terminal. diff --git a/pkg/e2e/up_test.go b/pkg/e2e/up_test.go index b7011391c9d..d34f2061e25 100644 --- a/pkg/e2e/up_test.go +++ b/pkg/e2e/up_test.go @@ -75,7 +75,7 @@ func TestUpDependenciesNotStopped(t *testing.T) { "app", ) - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + ctx, cancel := context.WithTimeout(t.Context(), 15*time.Second) t.Cleanup(cancel) cmd, err := StartWithNewGroupID(ctx, testCmd, upOut, nil) diff --git a/pkg/watch/debounce_test.go b/pkg/watch/debounce_test.go index fd1c40bbf27..39029f845c2 100644 --- a/pkg/watch/debounce_test.go +++ b/pkg/watch/debounce_test.go @@ -27,7 +27,7 @@ import ( func Test_BatchDebounceEvents(t *testing.T) { ch := make(chan FileEvent) clock := clockwork.NewFakeClock() - ctx, stop := context.WithCancel(context.Background()) + ctx, stop := context.WithCancel(t.Context()) t.Cleanup(stop) eventBatchCh := BatchDebounceEvents(ctx, clock, ch) diff --git a/pkg/watch/notify_test.go b/pkg/watch/notify_test.go index 9b68c4d0501..15a4f591379 100644 --- a/pkg/watch/notify_test.go +++ b/pkg/watch/notify_test.go @@ -501,7 +501,7 @@ type notifyFixture struct { func newNotifyFixture(t *testing.T) *notifyFixture { out := bytes.NewBuffer(nil) - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(t.Context()) nf := ¬ifyFixture{ ctx: ctx, cancel: cancel, From 04be56d9e051154a6f858c05fdece2b8856e1b44 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Tue, 20 Jan 2026 10:32:02 +0100 Subject: [PATCH 2/4] test: replace os.Setenv with t.Setenv() Use t.Setenv() which automatically restores the original value when the test completes, eliminating the need for manual cleanup. Go 1.18 modernization pattern. Assisted-By: cagent Signed-off-by: David Gageot --- pkg/compose/loader_test.go | 48 +++++++++++--------------------------- pkg/watch/notify_test.go | 26 ++++++++++----------- 2 files changed, 26 insertions(+), 48 deletions(-) diff --git a/pkg/compose/loader_test.go b/pkg/compose/loader_test.go index 027235ef9aa..8006d4169d0 100644 --- a/pkg/compose/loader_test.go +++ b/pkg/compose/loader_test.go @@ -17,7 +17,6 @@ package compose import ( - "context" "os" "path/filepath" "testing" @@ -48,13 +47,11 @@ services: err := os.WriteFile(composeFile, []byte(composeContent), 0o644) require.NoError(t, err) - // Create compose service service, err := NewComposeService(nil) require.NoError(t, err) // Load the project - ctx := context.Background() - project, err := service.LoadProject(ctx, api.ProjectLoadOptions{ + project, err := service.LoadProject(t.Context(), api.ProjectLoadOptions{ ConfigPaths: []string{composeFile}, }) @@ -87,19 +84,14 @@ services: require.NoError(t, err) // Set environment variable - require.NoError(t, os.Setenv("TEST_VAR", "resolved_value")) - t.Cleanup(func() { - require.NoError(t, os.Unsetenv("TEST_VAR")) - }) + t.Setenv("TEST_VAR", "resolved_value") service, err := NewComposeService(nil) require.NoError(t, err) - ctx := context.Background() - // Test with environment resolution (default) t.Run("WithResolution", func(t *testing.T) { - project, err := service.LoadProject(ctx, api.ProjectLoadOptions{ + project, err := service.LoadProject(t.Context(), api.ProjectLoadOptions{ ConfigPaths: []string{composeFile}, }) require.NoError(t, err) @@ -114,7 +106,7 @@ services: // Test without environment resolution t.Run("WithoutResolution", func(t *testing.T) { - project, err := service.LoadProject(ctx, api.ProjectLoadOptions{ + project, err := service.LoadProject(t.Context(), api.ProjectLoadOptions{ ConfigPaths: []string{composeFile}, ProjectOptionsFns: []cli.ProjectOptionsFn{cli.WithoutEnvironmentResolution}, }) @@ -145,10 +137,8 @@ services: service, err := NewComposeService(nil) require.NoError(t, err) - ctx := context.Background() - // Load only specific services - project, err := service.LoadProject(ctx, api.ProjectLoadOptions{ + project, err := service.LoadProject(t.Context(), api.ProjectLoadOptions{ ConfigPaths: []string{composeFile}, Services: []string{"web", "db"}, }) @@ -177,11 +167,9 @@ services: service, err := NewComposeService(nil) require.NoError(t, err) - ctx := context.Background() - // Without debug profile t.Run("WithoutProfile", func(t *testing.T) { - project, err := service.LoadProject(ctx, api.ProjectLoadOptions{ + project, err := service.LoadProject(t.Context(), api.ProjectLoadOptions{ ConfigPaths: []string{composeFile}, }) require.NoError(t, err) @@ -191,7 +179,7 @@ services: // With debug profile t.Run("WithProfile", func(t *testing.T) { - project, err := service.LoadProject(ctx, api.ProjectLoadOptions{ + project, err := service.LoadProject(t.Context(), api.ProjectLoadOptions{ ConfigPaths: []string{composeFile}, Profiles: []string{"debug"}, }) @@ -216,15 +204,13 @@ services: service, err := NewComposeService(nil) require.NoError(t, err) - ctx := context.Background() - // Track events received var events []string listener := func(event string, metadata map[string]any) { events = append(events, event) } - project, err := service.LoadProject(ctx, api.ProjectLoadOptions{ + project, err := service.LoadProject(t.Context(), api.ProjectLoadOptions{ ConfigPaths: []string{composeFile}, LoadListeners: []api.LoadListener{listener}, }) @@ -251,11 +237,9 @@ services: service, err := NewComposeService(nil) require.NoError(t, err) - ctx := context.Background() - // Without explicit project name t.Run("InferredName", func(t *testing.T) { - project, err := service.LoadProject(ctx, api.ProjectLoadOptions{ + project, err := service.LoadProject(t.Context(), api.ProjectLoadOptions{ ConfigPaths: []string{composeFile}, }) require.NoError(t, err) @@ -265,7 +249,7 @@ services: // With explicit project name t.Run("ExplicitName", func(t *testing.T) { - project, err := service.LoadProject(ctx, api.ProjectLoadOptions{ + project, err := service.LoadProject(t.Context(), api.ProjectLoadOptions{ ConfigPaths: []string{composeFile}, ProjectName: "my-custom-project", }) @@ -288,10 +272,8 @@ services: service, err := NewComposeService(nil) require.NoError(t, err) - ctx := context.Background() - // With compatibility mode - project, err := service.LoadProject(ctx, api.ProjectLoadOptions{ + project, err := service.LoadProject(t.Context(), api.ProjectLoadOptions{ ConfigPaths: []string{composeFile}, Compatibility: true, }) @@ -317,10 +299,8 @@ this is not valid yaml: [[[ service, err := NewComposeService(nil) require.NoError(t, err) - ctx := context.Background() - // Should return an error for invalid YAML - project, err := service.LoadProject(ctx, api.ProjectLoadOptions{ + project, err := service.LoadProject(t.Context(), api.ProjectLoadOptions{ ConfigPaths: []string{composeFile}, }) @@ -332,10 +312,8 @@ func TestLoadProject_MissingComposeFile(t *testing.T) { service, err := NewComposeService(nil) require.NoError(t, err) - ctx := context.Background() - // Should return an error for missing file - project, err := service.LoadProject(ctx, api.ProjectLoadOptions{ + project, err := service.LoadProject(t.Context(), api.ProjectLoadOptions{ ConfigPaths: []string{"/nonexistent/compose.yaml"}, }) diff --git a/pkg/watch/notify_test.go b/pkg/watch/notify_test.go index 15a4f591379..be52ced3a3f 100644 --- a/pkg/watch/notify_test.go +++ b/pkg/watch/notify_test.go @@ -35,20 +35,20 @@ import ( // behavior. func TestWindowsBufferSize(t *testing.T) { - orig := os.Getenv(WindowsBufferSizeEnvVar) - defer os.Setenv(WindowsBufferSizeEnvVar, orig) //nolint:errcheck + t.Run("empty value", func(t *testing.T) { + t.Setenv(WindowsBufferSizeEnvVar, "") + assert.Equal(t, defaultBufferSize, DesiredWindowsBufferSize()) + }) - err := os.Setenv(WindowsBufferSizeEnvVar, "") - require.NoError(t, err) - assert.Equal(t, defaultBufferSize, DesiredWindowsBufferSize()) - - err = os.Setenv(WindowsBufferSizeEnvVar, "a") - require.NoError(t, err) - assert.Equal(t, defaultBufferSize, DesiredWindowsBufferSize()) + t.Run("invalid value", func(t *testing.T) { + t.Setenv(WindowsBufferSizeEnvVar, "a") + assert.Equal(t, defaultBufferSize, DesiredWindowsBufferSize()) + }) - err = os.Setenv(WindowsBufferSizeEnvVar, "10") - require.NoError(t, err) - assert.Equal(t, 10, DesiredWindowsBufferSize()) + t.Run("valid value", func(t *testing.T) { + t.Setenv(WindowsBufferSizeEnvVar, "10") + assert.Equal(t, 10, DesiredWindowsBufferSize()) + }) } func TestNoEvents(t *testing.T) { @@ -114,7 +114,7 @@ func TestGitBranchSwitch(t *testing.T) { f.events = nil // consume all the events in the background - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(t.Context()) done := f.consumeEventsInBackground(ctx) for i, dir := range dirs { From dff482f243ee406ac655764dd9d62434b217d1a8 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Tue, 20 Jan 2026 10:33:54 +0100 Subject: [PATCH 3/4] test: replace os.MkdirTemp with t.TempDir() Use t.TempDir() which automatically cleans up the temporary directory when the test completes, eliminating the need for manual cleanup. Go 1.14 modernization pattern. Assisted-By: cagent Signed-off-by: David Gageot --- cmd/compose/options_test.go | 8 ++------ pkg/e2e/volumes_test.go | 5 +---- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/cmd/compose/options_test.go b/cmd/compose/options_test.go index 1686dfaf77b..50f82752130 100644 --- a/cmd/compose/options_test.go +++ b/cmd/compose/options_test.go @@ -213,10 +213,7 @@ func TestDisplayInterpolationVariables(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - // Create a temporary directory for the test - tmpDir, err := os.MkdirTemp("", "compose-test") - require.NoError(t, err) - defer func() { _ = os.RemoveAll(tmpDir) }() + tmpDir := t.TempDir() // Create a temporary compose file composeContent := ` @@ -230,8 +227,7 @@ services: - UNSET_VAR # optional without default ` composePath := filepath.Join(tmpDir, "docker-compose.yml") - err = os.WriteFile(composePath, []byte(composeContent), 0o644) - require.NoError(t, err) + require.NoError(t, os.WriteFile(composePath, []byte(composeContent), 0o644)) buf := new(bytes.Buffer) cli := mocks.NewMockCli(ctrl) diff --git a/pkg/e2e/volumes_test.go b/pkg/e2e/volumes_test.go index d018ed699d6..df138130db4 100644 --- a/pkg/e2e/volumes_test.go +++ b/pkg/e2e/volumes_test.go @@ -19,7 +19,6 @@ package e2e import ( "fmt" "net/http" - "os" "path/filepath" "runtime" "strings" @@ -104,9 +103,7 @@ func TestProjectVolumeBind(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("Running on Windows. Skipping...") } - tmpDir, err := os.MkdirTemp("", projectName) - assert.NilError(t, err) - defer os.RemoveAll(tmpDir) //nolint + tmpDir := t.TempDir() c.RunDockerComposeCmd(t, "--project-name", projectName, "down") From 71566f427dcae83a5478e5aeed2ee042dd55a79a Mon Sep 17 00:00:00 2001 From: David Gageot Date: Tue, 20 Jan 2026 10:56:25 +0100 Subject: [PATCH 4/4] chore(lint): add forbidigo rules to enforce t.Context() in tests Add linter rules to prevent usage of context.Background() and context.TODO() in test files - t.Context() should be used instead. The rules only apply to *_test.go files, not production code. Note: os.Setenv is not covered by forbidigo due to a limitation where it only catches calls when the return value is assigned. However, errcheck will flag unchecked os.Setenv calls. Assisted-By: cagent Signed-off-by: David Gageot --- .golangci.yml | 14 ++++++++++++++ pkg/compose/kill_test.go | 1 + 2 files changed, 15 insertions(+) diff --git a/.golangci.yml b/.golangci.yml index 48898c45717..7d75550fea0 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -8,6 +8,7 @@ linters: - depguard - errcheck - errorlint + - forbidigo - gocritic - gocyclo - gomodguard @@ -38,6 +39,15 @@ linters: desc: use stdlib slices package - pkg: gopkg.in/yaml.v2 desc: compose-go uses yaml.v3 + forbidigo: + analyze-types: true + forbid: + - pattern: 'context\.Background' + pkg: '^context$' + msg: "in tests, use t.Context() instead of context.Background()" + - pattern: 'context\.TODO' + pkg: '^context$' + msg: "in tests, use t.Context() instead of context.TODO()" gocritic: disabled-checks: - paramTypeCombine @@ -74,6 +84,10 @@ linters: - third_party$ - builtin$ - examples$ + rules: + - path-except: '_test\.go' + linters: + - forbidigo issues: max-issues-per-linter: 0 max-same-issues: 0 diff --git a/pkg/compose/kill_test.go b/pkg/compose/kill_test.go index a70228a6569..f27606b7f2e 100644 --- a/pkg/compose/kill_test.go +++ b/pkg/compose/kill_test.go @@ -127,6 +127,7 @@ func containerLabels(service string, oneOff bool) map[string]string { } func anyCancellableContext() gomock.Matcher { + //nolint:forbidigo // This creates a context type for gomock matching, not for actual test usage ctxWithCancel, cancel := context.WithCancel(context.Background()) cancel() return gomock.AssignableToTypeOf(ctxWithCancel)