Scert/backend/internal/app/app_test.go

111 lines
4.4 KiB
Go

package app
import (
"context"
"path/filepath"
"testing"
"time"
"git.pcux.cn/xuans/Scert/backend/internal/config"
"git.pcux.cn/xuans/Scert/backend/internal/domain"
"git.pcux.cn/xuans/Scert/backend/internal/repository"
)
func TestNewWiresApplicationServices(t *testing.T) {
baseDir := t.TempDir()
cfg := config.Default(baseDir)
application, err := NewWithConfigPath(context.Background(), cfg, filepath.Join(cfg.Paths.Config, "scert.json"))
if err != nil {
t.Fatalf("初始化应用失败:%v", err)
}
defer application.Close()
if application.Repository == nil || application.Servers == nil || application.Tasks == nil {
t.Fatal("数据库仓储未完整注入应用实例")
}
if application.Deployment == nil || application.Certificates == nil || application.Workflow == nil || application.Renewal == nil {
t.Fatal("证书申请、部署或续期服务未完整注入应用实例")
}
if application.SecretStore == nil || application.SSHKeys == nil || application.DNSVerification == nil || application.HTTP == nil {
t.Fatal("敏感信息、SSH、DNS 或 HTTP 服务未完整注入应用实例")
}
}
func TestNewWithConfigPathRecoversInterruptedTasks(t *testing.T) {
baseDir := t.TempDir()
cfg := config.Default(baseDir)
configPath := filepath.Join(cfg.Paths.Config, "scert.json")
first, err := NewWithConfigPath(context.Background(), cfg, configPath)
if err != nil {
t.Fatalf("首次初始化应用失败:%v", err)
}
item, err := domain.NewDomain("restart.example.com", domain.CertificateSingle, nil)
if err != nil {
_ = first.Close()
t.Fatalf("创建测试域名失败:%v", err)
}
if err := first.Repository.Create(context.Background(), item); err != nil {
_ = first.Close()
t.Fatalf("保存测试域名失败:%v", err)
}
now := time.Date(2026, 8, 30, 12, 0, 0, 0, time.UTC)
tasks := []repository.Task{
{ID: "restart-queued", DomainID: item.ID, TaskType: "certificate_issue", Status: "queued", Stage: "queued", Message: "等待执行", CreatedAt: now, UpdatedAt: now},
{ID: "restart-running", DomainID: item.ID, TaskType: "certificate_deploy", Status: "running", Stage: "deploy", Message: "正在部署", StartedAt: &now, CreatedAt: now, UpdatedAt: now},
{ID: "restart-success", DomainID: item.ID, TaskType: "certificate_validate", Status: "success", Stage: "validate", Message: "已完成", FinishedAt: &now, CreatedAt: now, UpdatedAt: now},
}
for _, task := range tasks {
if err := first.Tasks.CreateTask(context.Background(), task); err != nil {
_ = first.Close()
t.Fatalf("保存测试任务 %s 失败:%v", task.ID, err)
}
}
attempt := repository.TaskAttempt{
ID: "restart-attempt", TaskID: "restart-running", AttemptNumber: 1, Stage: "deploy", Status: "running", Message: "正在部署", StartedAt: &now, CreatedAt: now,
}
if err := first.Tasks.CreateAttempt(context.Background(), attempt); err != nil {
_ = first.Close()
t.Fatalf("保存测试任务尝试失败:%v", err)
}
if err := first.Close(); err != nil {
t.Fatalf("关闭首次应用失败:%v", err)
}
second, err := NewWithConfigPath(context.Background(), cfg, configPath)
if err != nil {
t.Fatalf("重启后初始化应用失败:%v", err)
}
defer second.Close()
for _, expectedID := range []string{"restart-queued", "restart-running"} {
task, getErr := second.Tasks.GetTask(context.Background(), expectedID)
if getErr != nil {
t.Fatalf("读取恢复任务 %s 失败:%v", expectedID, getErr)
}
if task.Status != "failed" || task.Stage != "service_restart" || task.FinishedAt == nil {
t.Fatalf("任务 %s 未按服务重启恢复:%+v", expectedID, task)
}
if task.Message != "服务重启导致任务中断,请重新执行" {
t.Fatalf("任务 %s 中断说明不正确:%s", expectedID, task.Message)
}
}
recoveredAttempt, err := second.Tasks.ListAttempts(context.Background(), "restart-running")
if err != nil {
t.Fatalf("读取恢复任务尝试失败:%v", err)
}
if len(recoveredAttempt) != 1 || recoveredAttempt[0].Status != "failed" || recoveredAttempt[0].FinishedAt == nil {
t.Fatalf("任务尝试未按服务重启恢复:%+v", recoveredAttempt)
}
completed, err := second.Tasks.GetTask(context.Background(), "restart-success")
if err != nil {
t.Fatalf("读取已完成任务失败:%v", err)
}
if completed.Status != "success" || completed.Stage != "validate" || completed.Message != "已完成" {
t.Fatalf("已完成任务不应被重启恢复逻辑修改:%+v", completed)
}
}