feat: 新增 MySQL 专项测试能力
- 新增 backend/tests/mysql/ 目录,包含 Docker Compose 配置和测试文件 - 新增 Makefile 命令: test-mysql, test-mysql-up, test-mysql-down, test-mysql-quick - 使用 build tag 控制测试启用,默认不运行 - 测试覆盖: 迁移正确性、外键约束、UNIQUE 约束、并发写入 - 发现 statsRepo.Record 存在并发 bug(检查-然后-操作竞态条件)
This commit is contained in:
126
backend/tests/mysql/migration_test.go
Normal file
126
backend/tests/mysql/migration_test.go
Normal file
@@ -0,0 +1,126 @@
|
||||
//go:build mysql
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMigration_TablesExist(t *testing.T) {
|
||||
db := SetupMySQLTestDB(t)
|
||||
|
||||
var tables []string
|
||||
err := db.Raw("SHOW TABLES").Scan(&tables).Error
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedTables := []string{"providers", "models", "usage_stats"}
|
||||
for _, expected := range expectedTables {
|
||||
assert.Contains(t, tables, expected, "表 %s 应存在", expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigration_TableColumns(t *testing.T) {
|
||||
db := SetupMySQLTestDB(t)
|
||||
|
||||
t.Run("providers 表字段", func(t *testing.T) {
|
||||
var columns []struct {
|
||||
Field string
|
||||
Type string
|
||||
Null string
|
||||
}
|
||||
err := db.Raw("SHOW COLUMNS FROM providers").Scan(&columns).Error
|
||||
require.NoError(t, err)
|
||||
|
||||
columnMap := make(map[string]string)
|
||||
for _, col := range columns {
|
||||
columnMap[col.Field] = col.Type
|
||||
}
|
||||
|
||||
assert.Contains(t, columnMap["id"], "varchar", "id 应为 VARCHAR 类型")
|
||||
assert.Contains(t, columnMap["name"], "varchar", "name 应为 VARCHAR 类型")
|
||||
assert.Contains(t, columnMap["api_key"], "varchar", "api_key 应为 VARCHAR 类型")
|
||||
assert.Contains(t, columnMap["base_url"], "varchar", "base_url 应为 VARCHAR 类型")
|
||||
assert.Contains(t, columnMap["protocol"], "varchar", "protocol 应为 VARCHAR 类型")
|
||||
assert.Contains(t, columnMap["enabled"], "tinyint", "enabled 应为 TINYINT (BOOLEAN) 类型")
|
||||
assert.Contains(t, columnMap["created_at"], "datetime", "created_at 应为 DATETIME 类型")
|
||||
assert.Contains(t, columnMap["updated_at"], "datetime", "updated_at 应为 DATETIME 类型")
|
||||
})
|
||||
|
||||
t.Run("models 表字段", func(t *testing.T) {
|
||||
var columns []struct {
|
||||
Field string
|
||||
Type string
|
||||
}
|
||||
err := db.Raw("SHOW COLUMNS FROM models").Scan(&columns).Error
|
||||
require.NoError(t, err)
|
||||
|
||||
columnMap := make(map[string]string)
|
||||
for _, col := range columns {
|
||||
columnMap[col.Field] = col.Type
|
||||
}
|
||||
|
||||
assert.Contains(t, columnMap["id"], "varchar", "id 应为 VARCHAR 类型")
|
||||
assert.Contains(t, columnMap["provider_id"], "varchar", "provider_id 应为 VARCHAR 类型")
|
||||
assert.Contains(t, columnMap["model_name"], "varchar", "model_name 应为 VARCHAR 类型")
|
||||
assert.Contains(t, columnMap["enabled"], "tinyint", "enabled 应为 TINYINT (BOOLEAN) 类型")
|
||||
assert.Contains(t, columnMap["created_at"], "datetime", "created_at 应为 DATETIME 类型")
|
||||
})
|
||||
|
||||
t.Run("usage_stats 表字段", func(t *testing.T) {
|
||||
var columns []struct {
|
||||
Field string
|
||||
Type string
|
||||
}
|
||||
err := db.Raw("SHOW COLUMNS FROM usage_stats").Scan(&columns).Error
|
||||
require.NoError(t, err)
|
||||
|
||||
columnMap := make(map[string]string)
|
||||
for _, col := range columns {
|
||||
columnMap[col.Field] = col.Type
|
||||
}
|
||||
|
||||
assert.Contains(t, columnMap["id"], "int", "id 应为 INT 类型")
|
||||
assert.Contains(t, columnMap["provider_id"], "varchar", "provider_id 应为 VARCHAR 类型")
|
||||
assert.Contains(t, columnMap["model_name"], "varchar", "model_name 应为 VARCHAR 类型")
|
||||
assert.Contains(t, columnMap["request_count"], "int", "request_count 应为 INT 类型")
|
||||
assert.Contains(t, columnMap["date"], "date", "date 应为 DATE 类型")
|
||||
})
|
||||
}
|
||||
|
||||
func TestMigration_IndexesExist(t *testing.T) {
|
||||
db := SetupMySQLTestDB(t)
|
||||
|
||||
t.Run("models 表索引", func(t *testing.T) {
|
||||
var indexes []struct {
|
||||
KeyName string
|
||||
}
|
||||
err := db.Raw("SHOW INDEX FROM models").Scan(&indexes).Error
|
||||
require.NoError(t, err)
|
||||
|
||||
indexMap := make(map[string]bool)
|
||||
for _, idx := range indexes {
|
||||
indexMap[idx.KeyName] = true
|
||||
}
|
||||
|
||||
assert.True(t, indexMap["idx_models_provider_id"], "idx_models_provider_id 索引应存在")
|
||||
assert.True(t, indexMap["idx_models_model_name"], "idx_models_model_name 索引应存在")
|
||||
})
|
||||
|
||||
t.Run("usage_stats 表索引", func(t *testing.T) {
|
||||
var indexes []struct {
|
||||
KeyName string
|
||||
}
|
||||
err := db.Raw("SHOW INDEX FROM usage_stats").Scan(&indexes).Error
|
||||
require.NoError(t, err)
|
||||
|
||||
indexMap := make(map[string]bool)
|
||||
for _, idx := range indexes {
|
||||
indexMap[idx.KeyName] = true
|
||||
}
|
||||
|
||||
assert.True(t, indexMap["idx_usage_stats_provider_model_date"], "idx_usage_stats_provider_model_date 索引应存在")
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user