Files
creator-hub/internal/environment/migrations_immutable_test.go
T
rogee 7df8762db3 test(environment): 1058 migration three-state verification on real postgres
- fresh database, legacy upgrade (rows get one-shot access key, online=false),
  repeat startup idempotence
- 1058 revised before any database applied it: drop dead token column,
  provision access_key via temporary default so non-empty legacy tables upgrade
- immutable-migration rule skips PR-local migrations until merged; after merge
  the lock applies again
2026-10-09 08:45:54 +08:00

75 lines
2.8 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 environment
import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
// 迁移文件一旦按版本号记录进 schema_migration 就不会再执行。
// 内容修正是用版本号不变的方式提交的话,已 applied 的库永远拿不到修正,
// 造成不同库 schema 漂移(034 曾因此漏删 browser_env.image_version,
// 触发 "environment persistence operation failed")。
// 本测试锁死规则:迁移文件内容自引入起不可变更;要改就新增版本号。
func TestMigrationFilesAreImmutableAfterIntroduction(t *testing.T) {
// 迁移文件相对本包的路径 → 仓库根相对路径(git show 需要仓库根路径)。
repoPath := map[string]string{
"migrations/*.sql": "internal/environment",
"../creator/migrations/*.sql": "internal/creator",
"../account/migrations/*.sql": "internal/account",
}
for pattern, repoDir := range repoPath {
entries, err := filepath.Glob(pattern)
if err != nil {
t.Fatalf("list migrations for %s: %v", pattern, err)
}
for _, file := range entries {
// repoDir 已是仓库根相对包目录,glob 出的文件按 basename 拼回。<pkg>/migrations/<name>.sql
file, repoFile := file, repoDir+"/migrations/"+filepath.Base(file)
t.Run(file, func(t *testing.T) {
introduced := gitOutput(t, "log", "--format=%H", "--diff-filter=A", "--", repoFile)
if introduced == "" {
t.Skipf("no introducing commit found for %s", repoFile)
}
firstCommit := strings.SplitN(introduced, "\n", 2)[0]
// 引入 commit 尚未进入主线的迁移(同 PR 内迭代)允许修正内容;
// 合入主线后此跳过失效,规则恢复锁死。
if gitOutput(t, "merge-base", "--is-ancestor", firstCommit, "origin/main") == "" {
t.Skipf("%s introduced in %s which is not merged into origin/main; PR-local migrations may still be revised", repoFile, firstCommit)
}
original := gitOutput(t, "show", firstCommit+":"+repoFile)
current, err := os.ReadFile(file)
if err != nil {
t.Fatalf("read %s: %v", file, err)
}
if original != string(current) {
t.Errorf("migration %s was modified after introduction in %s; migrations are immutable — add a new versioned migration instead", repoFile, firstCommit)
}
})
}
}
}
func gitOutput(t *testing.T, args ...string) string {
t.Helper()
cmd := exec.Command("git", args...)
cmd.Dir = repoRoot(t)
out, err := cmd.Output()
if err != nil {
t.Skipf("git %s unavailable: %v", args[0], err)
}
return string(out)
}
// repoRoot 定位 git 仓库根,保证 glob 出的包相对路径都能转成仓库根相对路径。
func repoRoot(t *testing.T) string {
t.Helper()
out, err := exec.Command("git", "rev-parse", "--show-toplevel").Output()
if err != nil {
t.Skipf("git repository unavailable: %v", err)
}
return strings.TrimSpace(string(out))
}