Files
goauto/server/app/jobs/execution_log_test.go
T

114 lines
4.1 KiB
Go

package jobs
import (
"errors"
"testing"
"time"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"go-admin/app/jobs/models"
)
func jobExecutionTestDB(t *testing.T) *gorm.DB {
t.Helper()
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
if err != nil {
t.Fatal(err)
}
if err := db.AutoMigrate(&models.SysJobExecutionLog{}); err != nil {
t.Fatal(err)
}
return db
}
func TestRunWithExecutionLogRecordsSuccessAndSanitizedFailure(t *testing.T) {
db := jobExecutionTestDB(t)
core := JobCore{JobId: 7, Name: "测试任务", InvokeTarget: "TestTarget"}
if err := runWithExecutionLog(db, core, func() error { return nil }); err != nil {
t.Fatal(err)
}
rawSecret := "token=secret-value productSpec=private"
if err := runWithExecutionLog(db, core, func() error { return errors.New(rawSecret) }); err == nil {
t.Fatal("failed execution must return its error")
}
var records []models.SysJobExecutionLog
if err := db.Order("id asc").Find(&records).Error; err != nil {
t.Fatal(err)
}
if len(records) != 2 || records[0].Status != models.JobExecutionSucceeded || records[1].Status != models.JobExecutionFailed {
t.Fatalf("unexpected records: %+v", records)
}
if records[1].ErrorCode != executionErrorExecFailed || records[1].ErrorMessage == rawSecret || records[1].ErrorMessage == "" {
t.Fatalf("failure was not safely summarized: %+v", records[1])
}
if records[0].FinishedAt == nil || records[1].FinishedAt == nil || records[0].ExecutionID == records[1].ExecutionID {
t.Fatal("execution lifecycle or unique IDs were not recorded")
}
}
func TestRecoverInterruptedExecutionLogs(t *testing.T) {
db := jobExecutionTestDB(t)
started := time.Now().UTC().Add(-2 * time.Second)
record := models.SysJobExecutionLog{
ExecutionID: "00000000-0000-4000-8000-000000000010", JobID: 9,
JobNameSnapshot: "中断任务", InvokeTargetSnapshot: "Interrupted",
TriggerType: models.JobTriggerScheduled, Status: models.JobExecutionRunning, StartedAt: started,
}
if err := db.Create(&record).Error; err != nil {
t.Fatal(err)
}
if err := RecoverInterruptedExecutionLogs(db); err != nil {
t.Fatal(err)
}
if err := db.First(&record, record.ID).Error; err != nil {
t.Fatal(err)
}
if record.Status != models.JobExecutionInterrupted || record.FinishedAt == nil || record.ErrorCode != executionErrorInterrupted || record.DurationMS < 1000 {
t.Fatalf("record was not safely interrupted: %+v", record)
}
}
func TestMissingExecutionTargetHasPublicSafeMessage(t *testing.T) {
code, message := publicExecutionFailure(missingExecutionTarget("SecretTarget"))
if code != executionErrorTargetMissing || message != "任务调用目标未注册" {
t.Fatalf("unexpected target failure %s %s", code, message)
}
}
func TestExecJobRecordsMissingTargetAsFailure(t *testing.T) {
db := jobExecutionTestDB(t)
previousJobList := jobList
jobList = map[string]JobExec{}
t.Cleanup(func() { jobList = previousJobList })
job := &ExecJob{JobCore: JobCore{JobId: 10, Name: "未注册任务", InvokeTarget: "MissingTarget"}, DB: db}
job.Run()
var record models.SysJobExecutionLog
if err := db.First(&record).Error; err != nil {
t.Fatal(err)
}
if record.Status != models.JobExecutionFailed || record.ErrorCode != executionErrorTargetMissing || record.ErrorMessage != "任务调用目标未注册" {
t.Fatalf("missing target was not safely recorded: %+v", record)
}
}
func TestRunWithExecutionLogSafelyRecordsPanic(t *testing.T) {
db := jobExecutionTestDB(t)
err := runWithExecutionLog(db, JobCore{JobId: 11, Name: "异常任务", InvokeTarget: "Panic"}, func() error {
panic("provider-secret")
})
if code, message := publicExecutionFailure(err); code != executionErrorPanicked || message != "任务执行异常中断" {
t.Fatalf("panic was not returned as a safe failure: %s %s", code, message)
}
var record models.SysJobExecutionLog
if err := db.First(&record).Error; err != nil {
t.Fatal(err)
}
if record.Status != models.JobExecutionFailed || record.ErrorCode != executionErrorPanicked || record.ErrorMessage != "任务执行异常中断" {
t.Fatalf("panic was not safely persisted: %+v", record)
}
}