test: H-02 补充 migrations 测试

- 验证迁移文件正确嵌入 embed.FS
- 测试所有迁移文件可读且非空
- 验证迁移文件命名规范(NNNN_前缀)
- 测试迁移文件排序一致性
- 验证初始迁移文件包含预期 SQL 内容
This commit is contained in:
phamnazage-jpg
2026-06-02 06:50:02 +08:00
parent 3838d78b00
commit 97502b8a86

View File

@@ -0,0 +1,171 @@
package migrations
import (
"testing"
)
func TestFilesEmbedded(t *testing.T) {
// Test that migration files are properly embedded
entries, err := Files.ReadDir(".")
if err != nil {
t.Fatalf("Failed to read embedded migrations: %v", err)
}
if len(entries) == 0 {
t.Fatal("No migration files embedded")
}
// Verify we have SQL files
hasSQL := false
for _, entry := range entries {
if !entry.IsDir() && len(entry.Name()) > 4 && entry.Name()[len(entry.Name())-4:] == ".sql" {
hasSQL = true
break
}
}
if !hasSQL {
t.Fatal("No .sql files found in embedded migrations")
}
}
func TestMigrationFilesReadable(t *testing.T) {
// Test that all embedded migration files can be read
entries, err := Files.ReadDir(".")
if err != nil {
t.Fatalf("Failed to read embedded migrations: %v", err)
}
for _, entry := range entries {
if entry.IsDir() {
continue
}
// Skip non-SQL files
name := entry.Name()
if len(name) < 4 || name[len(name)-4:] != ".sql" {
continue
}
content, err := Files.ReadFile(name)
if err != nil {
t.Errorf("Failed to read migration file %s: %v", name, err)
continue
}
if len(content) == 0 {
t.Errorf("Migration file %s is empty", name)
}
// Verify content looks like SQL
contentStr := string(content)
if len(contentStr) < 10 {
t.Errorf("Migration file %s content too short", name)
}
}
}
func TestMigrationFileNaming(t *testing.T) {
// Test that migration files follow naming convention (NNNN_description.sql)
entries, err := Files.ReadDir(".")
if err != nil {
t.Fatalf("Failed to read embedded migrations: %v", err)
}
for _, entry := range entries {
if entry.IsDir() {
continue
}
name := entry.Name()
if len(name) < 4 || name[len(name)-4:] != ".sql" {
continue
}
// Check naming pattern: should start with digits followed by underscore
if len(name) < 5 {
t.Errorf("Migration file %s name too short", name)
continue
}
// Should start with at least 4 digits
hasPrefix := false
for i := 0; i < 4 && i < len(name); i++ {
if name[i] >= '0' && name[i] <= '9' {
if i == 3 {
hasPrefix = true
}
} else {
break
}
}
if !hasPrefix {
t.Errorf("Migration file %s does not follow naming convention (should start with 4 digits)", name)
}
}
}
func TestMigrationFilesSorted(t *testing.T) {
// Test that migration files can be sorted consistently
entries, err := Files.ReadDir(".")
if err != nil {
t.Fatalf("Failed to read embedded migrations: %v", err)
}
var sqlFiles []string
for _, entry := range entries {
if entry.IsDir() {
continue
}
name := entry.Name()
if len(name) > 4 && name[len(name)-4:] == ".sql" {
sqlFiles = append(sqlFiles, name)
}
}
if len(sqlFiles) < 2 {
t.Skip("Not enough SQL files to test sorting")
}
// Verify files can be compared
for i := 1; i < len(sqlFiles); i++ {
if sqlFiles[i] < sqlFiles[i-1] {
t.Errorf("Files not sorted: %s should come before %s", sqlFiles[i-1], sqlFiles[i])
}
}
}
func TestInitialMigrationContent(t *testing.T) {
// Test that the initial migration file exists and has expected content
content, err := Files.ReadFile("0001_init.sql")
if err != nil {
t.Skipf("0001_init.sql not found: %v", err)
}
contentStr := string(content)
// Initial migration should contain CREATE TABLE statements
if len(contentStr) < 50 {
t.Error("Initial migration file seems too short")
}
// Should contain typical SQL keywords
hasCreate := containsAny(contentStr, []string{"CREATE", "create"})
hasTable := containsAny(contentStr, []string{"TABLE", "table"})
if !hasCreate || !hasTable {
t.Error("Initial migration should contain CREATE TABLE statements")
}
}
func containsAny(s string, substrs []string) bool {
for _, substr := range substrs {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
}
return false
}