Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add tidb integration test cases #6014

Merged
merged 2 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions tests/associations_belongs_to_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
Expand Down
2 changes: 2 additions & 0 deletions tests/associations_many2many_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions tests/associations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions tests/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 &
4 changes: 2 additions & 2 deletions tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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 => ../
11 changes: 11 additions & 0 deletions tests/helper_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tests_test

import (
"os"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -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"
}
132 changes: 132 additions & 0 deletions tests/migrate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion tests/sql_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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`
}

Expand Down
2 changes: 1 addition & 1 deletion tests/tests_all.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash -e

dialects=("sqlite" "mysql" "postgres" "sqlserver")
dialects=("sqlite" "mysql" "postgres" "sqlserver" "tidb")

if [[ $(pwd) == *"gorm/tests"* ]]; then
cd ..
Expand Down
7 changes: 7 additions & 0 deletions tests/tests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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{})
Expand Down