- 验证迁移文件正确嵌入 embed.FS - 测试所有迁移文件可读且非空 - 验证迁移文件命名规范(NNNN_前缀) - 测试迁移文件排序一致性 - 验证初始迁移文件包含预期 SQL 内容
172 lines
3.8 KiB
Go
172 lines
3.8 KiB
Go
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
|
|
}
|