feat: add tidb integration test cases (#6014)

* feat: support tidb integration test

* feat: update the mysql driver version to test
This commit is contained in:
Cheese 2023-02-08 16:29:09 +08:00 committed by GitHub
parent 878ac51e98
commit 02b7e26f6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 199 additions and 4 deletions

View File

@ -167,3 +167,36 @@ jobs:
- name: Tests
run: GITHUB_ACTION=true GORM_DIALECT=sqlserver GORM_DSN="sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm" ./tests/tests_all.sh
tidb:
strategy:
matrix:
dbversion: [ 'v6.5.0' ]
go: [ '1.19', '1.18' ]
platform: [ ubuntu-latest ]
runs-on: ${{ matrix.platform }}
steps:
- name: Setup TiDB
uses: Icemap/tidb-action@main
with:
port: 9940
version: ${{matrix.dbversion}}
- name: Set up Go 1.x
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go }}
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: go mod package cache
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}
- name: Tests
run: GITHUB_ACTION=true GORM_DIALECT=tidb GORM_DSN="root:@tcp(localhost:9940)/test?charset=utf8&parseTime=True&loc=Local" ./tests/tests_all.sh

View File

@ -138,6 +138,7 @@ func TestBelongsToAssociation(t *testing.T) {
unexistCompanyID := company.ID + 9999999
user = User{Name: "invalid-user-with-invalid-belongs-to-foreign-key", CompanyID: &unexistCompanyID}
if err := DB.Create(&user).Error; err == nil {
tidbSkip(t, "not support the foreign key feature")
t.Errorf("should have gotten foreign key violation error")
}
}

View File

@ -95,6 +95,8 @@ func TestMany2ManyAssociation(t *testing.T) {
}
func TestMany2ManyOmitAssociations(t *testing.T) {
tidbSkip(t, "not support the foreign key feature")
user := *GetUser("many2many_omit_associations", Config{Languages: 2})
if err := DB.Omit("Languages.*").Create(&user).Error; err == nil {

View File

@ -71,6 +71,8 @@ func TestAssociationNotNullClear(t *testing.T) {
}
func TestForeignKeyConstraints(t *testing.T) {
tidbSkip(t, "not support the foreign key feature")
type Profile struct {
ID uint
Name string
@ -126,6 +128,8 @@ func TestForeignKeyConstraints(t *testing.T) {
}
func TestForeignKeyConstraintsBelongsTo(t *testing.T) {
tidbSkip(t, "not support the foreign key feature")
type Profile struct {
ID uint
Name string

View File

@ -29,3 +29,8 @@ services:
- MSSQL_DB=gorm
- MSSQL_USER=gorm
- MSSQL_PASSWORD=LoremIpsum86
tidb:
image: 'pingcap/tidb:v6.5.0'
ports:
- 9940:4000
command: /tidb-server -store unistore -path "" -lease 0s > tidb.log 2>&1 &

View File

@ -8,11 +8,11 @@ require (
github.com/lib/pq v1.10.7
github.com/mattn/go-sqlite3 v1.14.16 // indirect
golang.org/x/crypto v0.5.0 // indirect
gorm.io/driver/mysql v1.4.5
gorm.io/driver/mysql v1.4.6
gorm.io/driver/postgres v1.4.6
gorm.io/driver/sqlite v1.4.4
gorm.io/driver/sqlserver v1.4.2
gorm.io/gorm v1.24.3
gorm.io/gorm v1.24.5
)
replace gorm.io/gorm => ../

View File

@ -1,6 +1,7 @@
package tests_test
import (
"os"
"sort"
"strconv"
"strings"
@ -235,3 +236,13 @@ func CheckUser(t *testing.T, user User, expect User) {
}
})
}
func tidbSkip(t *testing.T, reason string) {
if isTiDB() {
t.Skipf("This test case skipped, because of TiDB '%s'", reason)
}
}
func isTiDB() bool {
return os.Getenv("GORM_DIALECT") == "tidb"
}

View File

@ -374,7 +374,137 @@ func TestMigrateIndexes(t *testing.T) {
}
}
func TestTiDBMigrateColumns(t *testing.T) {
if !isTiDB() {
t.Skip()
}
// TiDB can't change column constraint and has auto_random feature
type ColumnStruct struct {
ID int `gorm:"primarykey;default:auto_random()"`
Name string
Age int `gorm:"default:18;comment:my age"`
Code string `gorm:"unique;comment:my code;"`
Code2 string
Code3 string `gorm:"unique"`
}
DB.Migrator().DropTable(&ColumnStruct{})
if err := DB.AutoMigrate(&ColumnStruct{}); err != nil {
t.Errorf("Failed to migrate, got %v", err)
}
type ColumnStruct2 struct {
ID int `gorm:"primarykey;default:auto_random()"`
Name string `gorm:"size:100"`
Code string `gorm:"unique;comment:my code2;default:hello"`
Code2 string `gorm:"comment:my code2;default:hello"`
}
if err := DB.Table("column_structs").Migrator().AlterColumn(&ColumnStruct{}, "Name"); err != nil {
t.Fatalf("no error should happened when alter column, but got %v", err)
}
if err := DB.Table("column_structs").AutoMigrate(&ColumnStruct2{}); err != nil {
t.Fatalf("no error should happened when auto migrate column, but got %v", err)
}
if columnTypes, err := DB.Migrator().ColumnTypes(&ColumnStruct{}); err != nil {
t.Fatalf("no error should returns for ColumnTypes")
} else {
stmt := &gorm.Statement{DB: DB}
stmt.Parse(&ColumnStruct2{})
for _, columnType := range columnTypes {
switch columnType.Name() {
case "id":
if v, ok := columnType.PrimaryKey(); !ok || !v {
t.Fatalf("column id primary key should be correct, name: %v, column: %#v", columnType.Name(), columnType)
}
case "name":
dataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name()))
if !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) {
t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v", columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType)
}
if length, ok := columnType.Length(); !ok || length != 100 {
t.Fatalf("column name length should be correct, name: %v, length: %v, expects: %v, column: %#v", columnType.Name(), length, 100, columnType)
}
case "age":
if v, ok := columnType.DefaultValue(); !ok || v != "18" {
t.Fatalf("column age default value should be correct, name: %v, column: %#v", columnType.Name(), columnType)
}
if v, ok := columnType.Comment(); !ok || v != "my age" {
t.Fatalf("column age comment should be correct, name: %v, column: %#v", columnType.Name(), columnType)
}
case "code":
if v, ok := columnType.Unique(); !ok || !v {
t.Fatalf("column code unique should be correct, name: %v, column: %#v", columnType.Name(), columnType)
}
if v, ok := columnType.DefaultValue(); !ok || v != "hello" {
t.Fatalf("column code default value should be correct, name: %v, column: %#v, default value: %v", columnType.Name(), columnType, v)
}
if v, ok := columnType.Comment(); !ok || v != "my code2" {
t.Fatalf("column code comment should be correct, name: %v, column: %#v", columnType.Name(), columnType)
}
case "code2":
// Code2 string `gorm:"comment:my code2;default:hello"`
if v, ok := columnType.DefaultValue(); !ok || v != "hello" {
t.Fatalf("column code default value should be correct, name: %v, column: %#v, default value: %v", columnType.Name(), columnType, v)
}
if v, ok := columnType.Comment(); !ok || v != "my code2" {
t.Fatalf("column code comment should be correct, name: %v, column: %#v", columnType.Name(), columnType)
}
}
}
}
type NewColumnStruct struct {
gorm.Model
Name string
NewName string
}
if err := DB.Table("column_structs").Migrator().AddColumn(&NewColumnStruct{}, "NewName"); err != nil {
t.Fatalf("Failed to add column, got %v", err)
}
if !DB.Table("column_structs").Migrator().HasColumn(&NewColumnStruct{}, "NewName") {
t.Fatalf("Failed to find added column")
}
if err := DB.Table("column_structs").Migrator().DropColumn(&NewColumnStruct{}, "NewName"); err != nil {
t.Fatalf("Failed to add column, got %v", err)
}
if DB.Table("column_structs").Migrator().HasColumn(&NewColumnStruct{}, "NewName") {
t.Fatalf("Found deleted column")
}
if err := DB.Table("column_structs").Migrator().AddColumn(&NewColumnStruct{}, "NewName"); err != nil {
t.Fatalf("Failed to add column, got %v", err)
}
if err := DB.Table("column_structs").Migrator().RenameColumn(&NewColumnStruct{}, "NewName", "new_new_name"); err != nil {
t.Fatalf("Failed to add column, got %v", err)
}
if !DB.Table("column_structs").Migrator().HasColumn(&NewColumnStruct{}, "new_new_name") {
t.Fatalf("Failed to found renamed column")
}
if err := DB.Table("column_structs").Migrator().DropColumn(&NewColumnStruct{}, "new_new_name"); err != nil {
t.Fatalf("Failed to add column, got %v", err)
}
if DB.Table("column_structs").Migrator().HasColumn(&NewColumnStruct{}, "new_new_name") {
t.Fatalf("Found deleted column")
}
}
func TestMigrateColumns(t *testing.T) {
tidbSkip(t, "use another test case")
sqlite := DB.Dialector.Name() == "sqlite"
sqlserver := DB.Dialector.Name() == "sqlserver"
@ -853,6 +983,8 @@ func TestUniqueColumn(t *testing.T) {
AssertEqual(t, "", value)
AssertEqual(t, false, ok)
tidbSkip(t, "can't change column constraint")
// null -> empty string
err = DB.Table("unique_tests").AutoMigrate(&UniqueTest3{})
if err != nil {

View File

@ -29,7 +29,7 @@ func TestRow(t *testing.T) {
}
table := "gorm.users"
if DB.Dialector.Name() != "mysql" {
if DB.Dialector.Name() != "mysql" || isTiDB() {
table = "users" // other databases doesn't support select with `database.table`
}

View File

@ -1,6 +1,6 @@
#!/bin/bash -e
dialects=("sqlite" "mysql" "postgres" "sqlserver")
dialects=("sqlite" "mysql" "postgres" "sqlserver" "tidb")
if [[ $(pwd) == *"gorm/tests"* ]]; then
cd ..

View File

@ -21,6 +21,7 @@ var (
mysqlDSN = "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local"
postgresDSN = "user=gorm password=gorm dbname=gorm host=localhost port=9920 sslmode=disable TimeZone=Asia/Shanghai"
sqlserverDSN = "sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm"
tidbDSN = "root:@tcp(localhost:9940)/test?charset=utf8&parseTime=True&loc=Local"
)
func init() {
@ -80,6 +81,12 @@ func OpenTestConnection() (db *gorm.DB, err error) {
dbDSN = sqlserverDSN
}
db, err = gorm.Open(sqlserver.Open(dbDSN), &gorm.Config{})
case "tidb":
log.Println("testing tidb...")
if dbDSN == "" {
dbDSN = tidbDSN
}
db, err = gorm.Open(mysql.Open(dbDSN), &gorm.Config{})
default:
log.Println("testing sqlite3...")
db, err = gorm.Open(sqlite.Open(filepath.Join(os.TempDir(), "gorm.db")), &gorm.Config{})