From 2a4f622f44356378905be8d5117b8c03738ff169 Mon Sep 17 00:00:00 2001 From: panxiao81 Date: Fri, 25 Sep 2026 16:30:09 +0000 Subject: [PATCH] =?UTF-8?q?test:=20=E5=88=86=E7=A6=BB=20OpenBao=20?= =?UTF-8?q?=E9=95=9C=E5=83=8F=E6=8B=89=E5=8F=96=E4=B8=8E=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E8=B6=85=E6=97=B6=E5=B9=B6=E4=BF=9D=E7=95=99=E8=AF=8A=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../openbao/credentials_integration_test.go | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/internal/database/adapter/openbao/credentials_integration_test.go b/internal/database/adapter/openbao/credentials_integration_test.go index 52817ec..9ade050 100644 --- a/internal/database/adapter/openbao/credentials_integration_test.go +++ b/internal/database/adapter/openbao/credentials_integration_test.go @@ -41,13 +41,15 @@ import ( // 只连接本测试创建的无持久卷 dev server,不接受生产地址或环境 token。 func baoFixture(t *testing.T) *bao.Client { t.Helper() + const image = "openbao/openbao@sha256:5b2486ab0fb90bbc788cc345b0a08616dfb375873ee8be5df3a2fd4d378a67e0" + prepareBaoImage(t, image) + // 冷缓存拉取不占用容器启动和健康检查的一分钟预算。 ctx, cancel := context.WithTimeout(t.Context(), time.Minute) defer cancel() - const image = "openbao/openbao@sha256:5b2486ab0fb90bbc788cc345b0a08616dfb375873ee8be5df3a2fd4d378a67e0" - output, err := exec.CommandContext(ctx, "docker", "run", "--rm", "-d", "-p", "127.0.0.1::8200", + output, err := exec.CommandContext(ctx, "docker", "run", "--pull=never", "--rm", "-d", "-p", "127.0.0.1::8200", image, "server", "-dev", "-dev-root-token-id="+fixtureToken, "-dev-listen-address=0.0.0.0:8200").Output() if err != nil { - t.Fatal("cannot start isolated OpenBao fixture") + t.Fatalf("cannot start isolated OpenBao fixture: %s", baoCommandError(ctx, err)) } id := strings.TrimSpace(string(output)) if !regexp.MustCompile(`^[a-f0-9]{64}$`).MatchString(id) { @@ -63,7 +65,7 @@ func baoFixture(t *testing.T) *bao.Client { output, err = exec.CommandContext(ctx, "docker", "inspect", "--format", `{{(index (index .NetworkSettings.Ports "8200/tcp") 0).HostPort}}`, id).Output() if err != nil { - t.Fatal("cannot inspect fixture port") + t.Fatalf("cannot inspect fixture port: %s", baoCommandError(ctx, err)) } client := fixtureClient(t, "http://127.0.0.1:"+strings.TrimSpace(string(output))) client.SetMaxRetries(0) @@ -79,6 +81,44 @@ func baoFixture(t *testing.T) *bao.Client { } } +func prepareBaoImage(t *testing.T, image string) { + t.Helper() + ctx, cancel := context.WithTimeout(t.Context(), 5*time.Minute) + defer cancel() + if exec.CommandContext(ctx, "docker", "image", "inspect", image).Run() == nil { + return + } + t.Log("pulling isolated OpenBao fixture image (timeout: 5m)") + if _, err := exec.CommandContext(ctx, "docker", "pull", image).Output(); err != nil { + t.Fatalf("cannot pull OpenBao fixture image: %s", baoCommandError(ctx, err)) + } +} + +// 保留 Docker stderr 与超时原因,但不泄露测试 token/password 或完整命令参数。 +func baoCommandError(ctx context.Context, err error) string { + detail := err.Error() + if exitErr, ok := errors.AsType[*exec.ExitError](err); ok { + detail += ": " + strings.TrimSpace(string(exitErr.Stderr)) + } + if ctx.Err() != nil { + detail += ": " + ctx.Err().Error() + } + return strings.NewReplacer(fixtureToken, "[REDACTED]", fixturePassword, "[REDACTED]").Replace(detail) +} + +func TestBaoCommandError(t *testing.T) { + err := &exec.ExitError{Stderr: []byte("registry unavailable " + fixtureToken + " " + fixturePassword)} + ctx, cancel := context.WithCancel(t.Context()) + cancel() + detail := baoCommandError(ctx, err) + if !strings.Contains(detail, "registry unavailable") || !strings.Contains(detail, "context canceled") { + t.Fatal("Docker diagnostic or context failure was lost") + } + if strings.Contains(detail, fixtureToken) || strings.Contains(detail, fixturePassword) { + t.Fatal("Docker diagnostic exposed fixture credentials") + } +} + func TestCredentialConcurrentCreateWithRealOpenBao(t *testing.T) { root := baoFixture(t) store := fixtureStore(t, root)