Ошибка создания / удаления из-за кэшированного идентификатора? - PullRequest
0 голосов
/ 23 февраля 2020

У меня есть следующий код (настроен для Go модульного тестирования) в Visual Studio Code (VSCode). В основном он состоит из 4 модульных тестов:

  1. TestGormCreateDB: пересоздать MySQL таблицу пользователей базы данных
  2. TestCreateMoon: добавить запись пользователя "Moon" в таблицу пользователей
  3. TestCreateDaltrey: добавить пользователя "Daltrey" в таблицу пользователей
  4. TestDeleteDaltrey: удалить пользователя "Daltrey" из таблицы пользователей

Если у вас есть MySQL, вы можете запустить его в VSCode, заменив mySQLHost, mySQLUser, mySQLPass и mySQLDBName с вашими значениями (сначала необходимо создать пустую базу данных в MySQL). Затем в VSCode вы можете вручную запустить TestGormCreateDB, TestCreateMoon, TestCreateDaltrey, TestDeleteDaltrey, TestCreateDaltrey, TestDeleteDaltrey et c ...

Каждый раз, когда запись Daltrey удаляется, а затем воссоздается, автоматически MySQL увеличенный идентификатор увеличивается на 1. В конечном итоге либо TestCreateDaltrey, либо TestDeleteDaltrey, не сообщая об ошибке, не обновят базу данных (иногда TestCreateDaltrey не сможет добавить новую запись, а в других случаях TestDeleteDaltrey не удалит существующую запись, которая должна). Это связано с тем, что в какой-то момент идентификатор в структуре User {} по модели GORM принимает предыдущий идентификатор из предыдущего Create или Delete, как если бы ID был кэширован, а идентификатор в структуре User {} начинает использовать недопустимый идентификатор ( запись в таблице пользователей имеет ID 4, но код находит Daltrey как имеющий ID 3). Я не могу сказать, является ли это моей ошибкой, ошибкой модульного теста VSCode (структура пользователя кэшируется при выполнении одного модульного теста на другую), ошибкой кэширования GORM или даже ошибкой кэша MySQL.

package main

import (
    "fmt"
    "log"
    "testing"

    // MySQL db driver. Aliased to blank identifier, because we need the init()
    //   function to run, but we are not using anything else in the driver.
    // _ "github.com/go-sql-driver/mysql"
    _ "github.com/jinzhu/gorm/dialects/mysql"

    "github.com/jinzhu/gorm"
)

const (
    mySQLHost    = "localhost" // Replace with your MySQL host.
    mySQLPort    = "3306"
    mySQLUser    = "mysqluser" // Enter MySQL user.
    mySQLPass    = "mysqlpass" // Enter MySQL password.
    mySQLDBName  = "mysqldb"   // Enter MySQL database name.
    mySQLCharset = "utf8mb4"   // See https://mathiasbynens.be/notes/mysql-utf8mb4
)

type User struct {
    gorm.Model
    Name  string `gorm:"not null"`
    Email string `gorm:"unique_index; not null"`
}

// Db provides global access to the database.
var db *gorm.DB

func init() {
    var err error
    db, err = gorm.Open("mysql", getDSN())
    if err != nil {
        fmt.Println("failed to connect to database", mySQLDBName)
        log.Fatal(err)
    }
    err = db.DB().Ping()
    if err != nil {
        fmt.Println("failed to ping database", mySQLDBName)
    }
    fmt.Println("successfully connected to database")
    return
}

func getDSN() string {
    dsn := mySQLUser + ":" + mySQLPass
    dsn += "@tcp(" + mySQLHost + ":" + mySQLPort + ")"
    dsn += "/" + mySQLDBName
    dsn += "?charset=" + mySQLCharset
    dsn += "&parseTime=True"
    dsn += "&loc=Local"
    return dsn
}

// String to satisfy the Stringer interface.
func (u User) String() string {
    return fmt.Sprintf("user: ID=%d Name=%s Email=%s", u.ID, u.Name, u.Email)
}

// NewUser creates a new user struct, not adding user to database.
func NewUser(name, email string) User {
    return User{Name: name, Email: email}
}

// NewDatabaseUser creates a new user in the database.
func NewDatabaseUser(name, email string) (user User, err error) {
    // Create a user.
    user = NewUser(name, email)
    fmt.Println("NewDatabaseUser (before Create):", user)
    // Create new user in the database.
    err = user.Create()
    fmt.Println("NewDatabaseUser (after Create):", user)
    if err != nil {
        return User{}, err
    }
    return user, nil
}

// Create a new user, save user info into the database
func (u *User) Create() (err error) {
    err = db.Debug().Create(&u).Error
    fmt.Println("Create:", u)
    if err != nil {
        return err
    }
    return nil
}

// UserByEmail gets a single user given the email
func UserByEmail(email string) (u User, err error) {
    err = db.Debug().Where("email = ?", email).First(&u).Error
    fmt.Println("UserByEmail:", u)
    if err != nil {
        return User{}, err
    }
    return u, nil
}

// Delete user from database
func (u *User) Delete() (err error) {
    delUser, err := UserByEmail(u.Email)
    fmt.Println("Delete delUser: ", delUser)
    if err != nil {
        return err
    }
    // Double check that user has primary key ID.  If primary key is 0,
    //   GORM will delete all users from the database!
    if delUser.ID <= 0 {
        return fmt.Errorf("attempt to delete user with invalid id %d", delUser.ID)
    }
    err = db.Debug().Unscoped().Delete(&delUser).Error
    if err != nil {
        return err
    }
    return nil
}

func TestGormCreateDB(t *testing.T) {
    // Cleanup old stuff.
    if err := db.Debug().DropTableIfExists("users").Error; err != nil {
        t.Fatal(err)
    }

    // Create new stuff.
    if err := db.Debug().CreateTable(&User{}).Error; err != nil {
        t.Fatal(err)
    }
}

func TestCreateMoon(t *testing.T) {
    // Create new user in the database.
    u, err := NewDatabaseUser("Keith Moon", "keith.moon@who.com")
    if err != nil {
        t.Error(err)
    } else {
        fmt.Println("created", u)
    }
}

func TestCreateDaltrey(t *testing.T) {
    // Create new user in the database.
    u, err := NewDatabaseUser("Roger Daltrey", "roger.daltrey@who.com")
    if err != nil {
        t.Error(err)
    } else {
        fmt.Println("created", u)
    }
}

func TestDeleteDaltrey(t *testing.T) {
    u := User{Email: "roger.daltrey@who.com"}
    fmt.Println("TestDeleteUser:", u)
    err := u.Delete()
    if err != nil {
        t.Fatalf("delete user with email %s failed: %v\n", u.Email, err)
    }
    fmt.Printf("user with email %s deleted\n", u.Email)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...