Scert/backend/internal/repository/task_validation_test.go
xuans 24a9683c43 保存证书校验后台任务及独立结果的修复前基线
校验结果事务持久化、三库迁移及前端详情接线已实现,完整回归仍在执行。审查发现前端校验幂等键包含路径分隔符,下一批补回归并修复;本提交不是正式发布或生产验收结论。
2026-10-02 20:25:28 +08:00

111 lines
4.9 KiB
Go

package repository
import (
"testing"
"time"
"git.pcux.cn/xuans/Scert/backend/internal/certificate"
)
func TestTaskValidationPersistsAndEnforcesVisibility(t *testing.T) {
repo, db := newTaskRepositoryTest(t)
defer db.Close()
completion := seedTaskCompletion(t, repo)
if _, err := db.ExecContext(t.Context(), `UPDATE tasks SET task_type = 'certificate_validate' WHERE id = ?`, completion.TaskID); err != nil {
t.Fatal(err)
}
if _, err := db.ExecContext(t.Context(), `INSERT INTO users (id, username, password_hash, role, status)
VALUES ('reader', 'reader', 'test-only', 'user', 'active'), ('outsider', 'outsider', 'test-only', 'user', 'active')`); err != nil {
t.Fatal(err)
}
if _, err := db.ExecContext(t.Context(), `INSERT INTO project_members (project_id, user_id, role) VALUES ('default', 'reader', 'read_only')`); err != nil {
t.Fatal(err)
}
completion.Stage = "certificate_validate"
completion.AuditAction = "certificate_validate"
completion.Validation = &certificate.ValidationResult{
Domains: []string{"example.test", "*.example.test"}, SerialNumber: "1234",
FingerprintSHA256: "test-fingerprint", NotBefore: time.Now().UTC().Truncate(time.Second),
NotAfter: time.Now().UTC().Add(24 * time.Hour).Truncate(time.Second), ChainLength: 2,
}
if err := repo.FinishTaskAttempt(t.Context(), completion); err != nil {
t.Fatal(err)
}
reader := NewSQLTaskRepository(db)
items, err := reader.ListVisibleTaskValidations(t.Context(), "reader", completion.TaskID)
if err != nil || len(items) != 1 || items[0].AttemptID != completion.AttemptID || items[0].AttemptNumber != 1 {
t.Fatalf("重建仓储后校验结果丢失:%+v %v", items, err)
}
got := items[0].Validation
if len(got.Domains) != 2 || got.SerialNumber != "1234" || got.FingerprintSHA256 != "test-fingerprint" || got.ChainLength != 2 || !got.NotAfter.Equal(completion.Validation.NotAfter) {
t.Fatalf("校验详情不完整:%+v", got)
}
for _, actor := range []string{"outsider", "", "missing"} {
items, err := reader.ListVisibleTaskValidations(t.Context(), actor, completion.TaskID)
if err != nil || len(items) != 0 {
t.Fatalf("无权限账户读取校验结果:%s %+v %v", actor, items, err)
}
}
if _, err := db.ExecContext(t.Context(), `DELETE FROM project_members WHERE user_id = 'reader'`); err != nil {
t.Fatal(err)
}
items, err = reader.ListVisibleTaskValidations(t.Context(), "reader", completion.TaskID)
if err != nil || len(items) != 0 {
t.Fatalf("撤权后仍可读取校验结果:%+v %v", items, err)
}
reports, err := repo.ListTaskDeploymentReports(t.Context(), completion.TaskID)
if err != nil || len(reports) != 0 {
t.Fatalf("校验结果被伪装成部署报告:%+v %v", reports, err)
}
}
func TestTaskValidationFailureRollsBackAllRecords(t *testing.T) {
for _, scenario := range []string{"result_insert", "audit_insert", "wrong_task_type", "mixed_report", "failed_status", "foreign_attempt"} {
t.Run(scenario, func(t *testing.T) {
repo, db := newTaskRepositoryTest(t)
defer db.Close()
completion := seedTaskCompletion(t, repo)
completion.Validation = &certificate.ValidationResult{Domains: []string{"example.test"}}
completion.AuditAction = "certificate_validate"
if scenario != "wrong_task_type" {
if _, err := db.ExecContext(t.Context(), `UPDATE tasks SET task_type = 'certificate_validate' WHERE id = ?`, completion.TaskID); err != nil {
t.Fatal(err)
}
}
switch scenario {
case "result_insert", "audit_insert":
table := "task_validation_results"
if scenario == "audit_insert" {
table = "audit_logs"
}
if _, err := db.ExecContext(t.Context(), `CREATE TRIGGER reject_validation BEFORE INSERT ON `+table+` BEGIN SELECT RAISE(ABORT, 'injected failure'); END`); err != nil {
t.Fatal(err)
}
case "mixed_report":
completion.Report = completionTestReport()
case "failed_status":
completion.Status = "failed"
case "foreign_attempt":
completion.AttemptID = "missing-attempt"
}
if err := repo.FinishTaskAttempt(t.Context(), completion); err == nil {
t.Fatal("无效校验结果或写入失败必须回滚终态")
}
task, err := repo.GetTask(t.Context(), completion.TaskID)
if err != nil || task.Status != "running" || task.FinishedAt != nil {
t.Fatalf("失败后任务被部分提交:%+v %v", task, err)
}
attempts, err := repo.ListAttempts(t.Context(), completion.TaskID)
if err != nil || len(attempts) != 1 || attempts[0].Status != "running" || attempts[0].FinishedAt != nil {
t.Fatalf("失败后尝试被部分提交:%+v %v", attempts, err)
}
for _, table := range []string{"task_validation_results", "task_deployment_reports", "task_deployment_results", "audit_logs"} {
var count int
if err := db.QueryRowContext(t.Context(), `SELECT COUNT(*) FROM `+table+` WHERE task_id = ?`, completion.TaskID).Scan(&count); err != nil || count != 0 {
t.Fatalf("失败后出现部分记录:%s %d %v", table, count, err)
}
}
})
}
}