Scert/backend/internal/cli/sqlite_upgrade_history_test.go

85 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cli
import (
"database/sql"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"git.pcux.cn/xuans/Scert/backend/internal/database"
)
// 以当批源码目录为预期,同时校验正式程序写入的迁移历史连续且完整。
func expectedSQLiteCLIMigrationVersion(t *testing.T, db *sql.DB) int {
t.Helper()
entries, err := os.ReadDir(filepath.Join("..", "database", "migrations", "sqlite"))
if err != nil || len(entries) == 0 {
t.Fatalf("无法读取本批 SQLite 迁移目录:%v", err)
}
for index, entry := range entries {
if entry.IsDir() || !strings.HasPrefix(entry.Name(), fmt.Sprintf("%04d_", index+1)) || !strings.HasSuffix(entry.Name(), ".sql") {
t.Fatalf("SQLite 迁移文件序列无效:%s", entry.Name())
}
}
rows, err := db.QueryContext(t.Context(), "SELECT version FROM schema_migrations ORDER BY version")
if err != nil {
t.Fatal(err)
}
defer rows.Close()
count := 0
for rows.Next() {
var version int
if err := rows.Scan(&version); err != nil {
t.Fatal(err)
}
count++
if version != count {
t.Fatalf("正式程序迁移历史不连续:期望 %d,实际 %d", count, version)
}
}
if err := rows.Err(); err != nil {
t.Fatal(err)
}
if count != len(entries) {
t.Fatalf("正式程序迁移不完整:源码 %d 项,数据库 %d 项", len(entries), count)
}
return len(entries)
}
// 仅在隔离样本中写入终态事务,用于验证存储恢复,不作为远端恢复证据。
func seedSQLiteCLIDeploymentHistory(t *testing.T, fixture sqliteBusinessFixture) {
t.Helper()
db, err := database.Open(t.Context(), fixture.config.Database)
if err != nil {
t.Fatal(err)
}
defer db.Close()
for _, statement := range []string{
`INSERT INTO task_idempotency (domain_id, idempotency_key, task_id) VALUES ('domain', 'backup-deploy-key', 'task')`,
`INSERT INTO task_attempts (id, task_id, attempt_number, stage, status, message) VALUES ('backup-attempt', 'task', 1, 'certificate_deploy', 'success', '历史部署尝试')`,
`INSERT INTO task_deployment_results (task_id, attempt_id, attempt_number, result_index, server_id, server_name, status, message, backup_id) VALUES ('task', 'backup-attempt', 1, 0, 'server', '历史服务器', 'success', '历史部署结果', 'backup-transaction')`,
`INSERT INTO deployment_transactions (id, task_id, attempt_id, attempt_number, result_index, server_id, server_name, certificate_path, certificate_chain_path, private_key_path, temporary_paths_json, installed_paths_json, backups_json, lock_paths_json, reload_attempted, phase, status, message, recovery_evidence_json) VALUES ('backup-transaction', 'task', 'backup-attempt', 1, 0, 'server', '历史服务器', '/test/cert.pem', '/test/chain.pem', '/test/key.pem', '[]', '["/test/cert.pem"]', '[]', '[]', 1, 'committed', 'committed', '历史事务', '{"version":1,"lock_token":"fixture-lock","address":"192.0.2.10","port":22}')`,
} {
if _, err := db.DB.ExecContext(t.Context(), statement); err != nil {
t.Fatal(err)
}
}
}
// 主动改变新增表,避免用空表或未变化的数据宣称恢复成功。
func mutateSQLiteCLIDeploymentHistory(t *testing.T, db *sql.DB) {
t.Helper()
for _, statement := range []string{
`UPDATE task_idempotency SET idempotency_key = 'changed-key'`,
`UPDATE task_attempts SET message = '待恢复尝试'`,
`UPDATE task_deployment_results SET message = '待恢复结果'`,
`UPDATE deployment_transactions SET recovery_evidence_json = '{}', message = '待恢复证据'`,
} {
if _, err := db.ExecContext(t.Context(), statement); err != nil {
t.Fatal(err)
}
}
}