Scert/backend/internal/deployment/diagnostics_test.go

98 lines
4.3 KiB
Go

package deployment
import (
"context"
"errors"
"strings"
"testing"
)
type lockReleaseFailureClient struct {
*memoryClient
}
func (c *lockReleaseFailureClient) Unlock(context.Context, string) error {
return errors.New("secret-path lock-release-error")
}
func TestDeploymentLockReleaseFailureOverridesSuccess(t *testing.T) {
client := &lockReleaseFailureClient{memoryClient: newMemoryClient()}
server := testServer("unlock")
report := NewService(fixedClientFactory{client}).Deploy(t.Context(), []Target{{Server: server}}, testArtifact(t))
if report.Status != "failed" || report.Success != 0 || report.Failed != 1 || report.Results[0].FailureCode != FailureLockRelease {
t.Fatalf("锁释放失败不得保留成功状态:%+v", report)
}
public := PublicResult(report.Results[0])
if strings.Contains(public.Message, "secret-path") || !strings.Contains(public.Message, "部署锁释放未完成") {
t.Fatalf("锁释放诊断错误:%+v", public)
}
}
func TestPublicDeploymentDiagnosticsAreAllowlisted(t *testing.T) {
for code, expected := range publicFailureMessages {
t.Run(string(code), func(t *testing.T) {
raw := Result{ServerID: "server-1", Status: "failed", FailureCode: code, Message: "secret-path private-key remote-output"}
public := PublicResult(raw)
if public.Message != expected || public.FailureCode != code || public.ServerID != raw.ServerID {
t.Fatalf("固定诊断或结果身份丢失:%+v", public)
}
if PublicStoredFailureMessage(public.Message) != expected {
t.Fatal("持久化诊断无法读取")
}
if PublicStoredFailureMessage(public.Message+" secret-path") != "证书部署失败" {
t.Fatal("包含额外敏感内容的诊断不应放行")
}
})
}
for _, code := range []FailureCode{"", "secret-path", "lock_unavailable\nprivate-key"} {
public := PublicResult(Result{Status: "failed", FailureCode: code, Message: "无法取得部署锁 /secret-path"})
if public.FailureCode != "" || public.Message != "证书部署失败" {
t.Fatalf("未知错误码或原始错误泄露:%+v", public)
}
}
public := PublicResult(Result{Status: "success", FailureCode: "secret-path", Message: "部署完成"})
if public.FailureCode != "" || public.Message != "部署完成" {
t.Fatalf("成功结果错误:%+v", public)
}
}
func TestDeploymentFailureCodesDescribeActualRecoveryBoundary(t *testing.T) {
for _, scenario := range []struct {
name string
code FailureCode
configure func(*memoryClient, Server)
}{
{"lock", FailureLock, func(c *memoryClient, s Server) { c.files[s.PrivateKeyPath+".scert-lock"] = memoryFile{} }},
{"upload", FailureUpload, func(c *memoryClient, _ Server) { c.uploadError = ".scert-tmp-" }},
{"replace", FailureReplace, func(c *memoryClient, _ Server) { c.renameError = ".scert-tmp-" }},
{"validation", FailureValidation, func(c *memoryClient, _ Server) { c.runErrors = []error{errors.New("secret-path")} }},
{"reload", FailureRuntimeUnknown, func(c *memoryClient, _ Server) { c.runErrors = []error{nil, errors.New("secret-path")} }},
{"validation_rollback", FailureRollback, func(c *memoryClient, _ Server) {
c.runErrors = []error{errors.New("secret-path")}
c.restoreError = true
}},
{"reload_rollback", FailureRollback, func(c *memoryClient, _ Server) {
c.runErrors = []error{nil, errors.New("secret-path")}
c.restoreError = true
}},
{"cleanup", FailureCleanup, func(c *memoryClient, _ Server) { c.removeError = ".scert-backup-" }},
} {
t.Run(scenario.name, func(t *testing.T) {
client := newMemoryClient()
server := testServer("diagnostics")
for _, path := range []string{server.CertificatePath, server.CertificateChainPath, server.PrivateKeyPath} {
client.files[path] = memoryFile{data: []byte("original"), mode: 0o600}
}
scenario.configure(client, server)
report := NewService(fixedClientFactory{client}).Deploy(t.Context(), []Target{{Server: server}}, testArtifact(t))
if report.Status != "failed" || len(report.Results) != 1 || report.Results[0].FailureCode != scenario.code {
t.Fatalf("失败阶段或恢复边界错误:%+v", report)
}
public := PublicResult(report.Results[0])
if strings.Contains(public.Message, "secret-path") || public.Message == "证书部署失败" {
t.Fatalf("阶段诊断缺失或泄露底层信息:%+v", public)
}
})
}
}