package notification import ( "encoding/json" "io" "net/http" "net/http/httptest" "strings" "testing" "git.pcux.cn/xuans/Scert/backend/internal/deployment" htmlparser "golang.org/x/net/html" ) func TestFailedTaskCannotHaveCompletedSummary(t *testing.T) { for _, status := range []string{"failed", "error", "unknown"} { t.Run(status, func(t *testing.T) { payload, err := New(newTestConfig("http://mail.example.test"), nil).buildPayload(Event{ Status: status, Domain: "example.test", Deployment: deployment.Report{Total: 2, Success: 2, Status: "success"}, }) if err != nil { t.Fatal(err) } for _, body := range []string{payload.Text, payload.HTML} { if strings.Contains(body, "已完成") { t.Fatal("失败或未知任务不能显示完成摘要") } } }) } } func TestRecoveryWarningSurvivesHiddenDetails(t *testing.T) { for _, report := range []deployment.Report{ {RecoveryStatus: "unconfirmed"}, {RecoveryStatus: "failed"}, {RecoveryStatus: "recovery_required"}, {Results: []deployment.Result{{Status: "failed", Message: "已恢复旧文件,但服务或站点恢复状态未确认,已保留部署锁"}}}, } { cfg := newTestConfig("http://mail.example.test") cfg.IncludeDeploymentDetails = false payload, err := New(cfg, nil).buildPayload(Event{Status: "failed", Domain: "example.test", Deployment: report}) if err != nil { t.Fatal(err) } for _, body := range []string{payload.Text, payload.HTML} { if !strings.Contains(body, "勿直接重试或删除锁") { t.Fatal("隐藏部署明细不能隐藏恢复安全提示") } } } } func TestRecoveryWarningUsesFailureCodes(t *testing.T) { for _, code := range []deployment.FailureCode{ deployment.FailureRollback, deployment.FailureRuntimeUnknown, deployment.FailureRecoveryPending, deployment.FailureRecoveryUnknown, deployment.FailureLock, deployment.FailureLockRelease, } { t.Run(string(code), func(t *testing.T) { cfg := newTestConfig("http://mail.example.test") cfg.IncludeDeploymentDetails = false payload, err := New(cfg, nil).buildPayload(Event{ Status: "failed", Domain: "example.test", Error: "primary failure", Deployment: deployment.Report{Results: []deployment.Result{ {Status: "failed", Message: "first failure"}, {Status: "failed", Message: "second failure"}, {Server: "hidden-server", Status: "failed", FailureCode: code}, }}, }) if err != nil { t.Fatal(err) } for _, body := range []string{payload.Text, payload.HTML} { if !strings.Contains(body, "勿直接重试或删除锁") || strings.Contains(body, "hidden-server") { t.Fatal("恢复警告必须独立展示且不能暴露已隐藏的服务器名称") } } }) } } func TestVerifiedRecoveryDoesNotRequireManualUnlock(t *testing.T) { event := Event{Status: "failed", Deployment: deployment.Report{ RecoveryStatus: "success", Results: []deployment.Result{{FailureCode: deployment.FailureRecoveryRestored}}, }} if recoveryWarning(event) != "" { t.Fatal("已确认恢复的任务不应提示恢复未确认") } event = Event{Status: "failed", Error: "已保留部署锁"} if recoveryWarning(event) == "" { t.Fatal("仅任务错误携带恢复信息时也应提示核查") } } func TestMailUsesCertificateDomainInsteadOfInternalID(t *testing.T) { for _, tc := range []struct { domain string domainID string domains []string expected string }{ {domain: "", domainID: "internal-domain-id", domains: []string{" ", "example.test"}, expected: "example.test"}, {domain: "", domainID: "internal-domain-id", domains: nil, expected: "未指定域名"}, {domain: "b6824906-fea9-4f2a-a682-3c2b1ade1ee3", domains: []string{"scert-multi-20260930.apmmk.cn"}, expected: "scert-multi-20260930.apmmk.cn"}, } { payload, err := New(newTestConfig("http://mail.example.test"), nil).buildPayload(Event{ Status: "success", Domain: tc.domain, DomainID: tc.domainID, Domains: tc.domains, }) if err != nil { t.Fatal(err) } for _, body := range []string{payload.Subject, payload.Text, payload.HTML} { if (tc.domainID != "" && strings.Contains(body, tc.domainID)) || strings.Contains(body, "b6824906-fea9-4f2a-a682-3c2b1ade1ee3") || !strings.Contains(body, tc.expected) { t.Fatalf("用户邮件应显示证书域名而非内部记录编号或UUID: %s", body) } } } } func TestAllMailTemplatesHaveValidBodyStyles(t *testing.T) { for _, eventType := range []string{"certificate_issue", "certificate_renewal", "management_renewal", "certificate_import", "unknown"} { for _, status := range []string{"success", "failed", "error"} { payload, err := New(newTestConfig("http://mail.example.test"), nil).buildPayload(Event{ Type: eventType, Status: status, Domain: "example.test", }) if err != nil { t.Fatal(err) } assertMailBodyStyle(t, payload.HTML) assertReferenceMailStyle(t, payload.HTML) } } assertMailBodyStyle(t, buildSecurityHTML("01234567")) assertReferenceMailStyle(t, buildSecurityHTML("01234567")) if strings.Contains(buildSecurityHTML(``), "