1package docker
2
3import (
4 "bytes"
5 "context"
6 "errors"
7 "net/http"
8 "net/http/httptest"
9 "strings"
10 "testing"
11
12 "github.com/bnema/zerowrap"
13 "github.com/moby/moby/api/types/container"
14 "github.com/moby/moby/client"
15 "github.com/stretchr/testify/assert"
16 "github.com/stretchr/testify/require"
17)
18
19func TestRuntime_InspectImageEnv_RedactsValuesInDebugLog(t *testing.T) {
20 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
21 assert.Equal(t, http.MethodGet, r.Method)
22 assert.Contains(t, r.URL.Path, "/images/")
23 assert.Contains(t, r.URL.Path, "/json")
24 w.Header().Set("Content-Type", "application/json")
25 _, _ = w.Write([]byte(`{
26 "Config": {
27 "Env": ["API_KEY=super-secret-key", "DATABASE_URL=postgres://user:pass@example/db", "PORT=8080", "MALFORMED_SECRET_VALUE", "=empty-key-secret"]
28 }
29 }`))
30 }))
31 defer server.Close()
32
33 host := strings.TrimPrefix(server.URL, "http://")
34 cli, err := client.New(client.WithHost("tcp://"+host), client.WithAPIVersion("1.41"), client.WithHTTPClient(server.Client()))
35 require.NoError(t, err)
36 runtime := NewRuntimeWithClient(cli)
37
38 var logs bytes.Buffer
39 log := zerowrap.New(zerowrap.Config{Level: "debug", Format: "json", Output: &logs})
40 ctx := zerowrap.WithCtx(context.Background(), log)
41
42 envVars, err := runtime.InspectImageEnv(ctx, "example/app:latest")
43 require.NoError(t, err)
44 assert.Equal(t, []string{"API_KEY=super-secret-key", "DATABASE_URL=postgres://user:pass@example/db", "PORT=8080", "MALFORMED_SECRET_VALUE", "=empty-key-secret"}, envVars)
45
46 logOutput := logs.String()
47 assert.Contains(t, logOutput, "env_keys")
48 assert.Contains(t, logOutput, "API_KEY")
49 assert.Contains(t, logOutput, "DATABASE_URL")
50 assert.Contains(t, logOutput, "PORT")
51 assert.Contains(t, logOutput, "[malformed]")
52 assert.NotContains(t, logOutput, "super-secret-key")
53 assert.NotContains(t, logOutput, "postgres://user:pass@example/db")
54 assert.NotContains(t, logOutput, "MALFORMED_SECRET_VALUE")
55 assert.NotContains(t, logOutput, "empty-key-secret")
56 assert.NotContains(t, logOutput, "API_KEY=super-secret-key")
57}
58
59func TestWaitForVolumeArchiveContainerIgnoresNilErrorBeforeStatus(t *testing.T) {
60 statusCh := make(chan container.WaitResponse, 1)
61 errCh := make(chan error, 1)
62 errCh <- nil
63 statusCh <- container.WaitResponse{StatusCode: 7}
64
65 statusCode, err := waitForVolumeArchiveContainer(statusCh, errCh)
66
67 require.NoError(t, err)
68 assert.Equal(t, int64(7), statusCode)
69}
70
71func TestWaitForVolumeArchiveContainerHandlesClosedErrorChannelBeforeStatus(t *testing.T) {
72 statusCh := make(chan container.WaitResponse, 1)
73 errCh := make(chan error)
74 close(errCh)
75 statusCh <- container.WaitResponse{StatusCode: 3}
76
77 statusCode, err := waitForVolumeArchiveContainer(statusCh, errCh)
78
79 require.NoError(t, err)
80 assert.Equal(t, int64(3), statusCode)
81}
82
83func TestWaitForVolumeArchiveContainerReturnsErrorChannelError(t *testing.T) {
84 statusCh := make(chan container.WaitResponse)
85 errCh := make(chan error, 1)
86 wantErr := errors.New("wait failed")
87 errCh <- wantErr
88
89 statusCode, err := waitForVolumeArchiveContainer(statusCh, errCh)
90
91 require.ErrorIs(t, err, wantErr)
92 assert.Equal(t, int64(0), statusCode)
93}
94
95func TestWaitForVolumeArchiveContainerErrorsWhenStatusChannelCloses(t *testing.T) {
96 statusCh := make(chan container.WaitResponse)
97 errCh := make(chan error)
98 close(statusCh)
99 close(errCh)
100
101 statusCode, err := waitForVolumeArchiveContainer(statusCh, errCh)
102
103 require.Error(t, err)
104 assert.Contains(t, err.Error(), "wait channel closed")
105 assert.Equal(t, int64(0), statusCode)
106}