| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 |
- package gormvrtx
- import (
- "testing"
- "gorm.io/gorm"
- )
- // SvcM 软删除模型 — 报告 #1/#2/#3 的回归: 零 DeletedAt 必须落成 NULL,
- // 软删除作用域 (deleted_at IS NULL) 下 UPDATE/Delete/First 必须生效。
- type SvcM struct {
- gorm.Model
- Title string `gorm:"size:32"`
- Port int
- }
- func (SvcM) TableName() string { return "services_m" }
- func TestSoftDeleteScope(t *testing.T) {
- db := openTest(t)
- if err := db.AutoMigrate(&SvcM{}); err != nil {
- t.Fatal(err)
- }
- if err := db.Create(&SvcM{Title: "原始", Port: 11}).Error; err != nil {
- t.Fatal(err)
- }
- // #1: 单字段更新 (GORM 自动附加 deleted_at IS NULL 作用域)
- if err := db.Model(&SvcM{}).Where("id = ?", 1).Update("title", "字面量A").Error; err != nil {
- t.Fatal(err)
- }
- var m SvcM
- if err := db.First(&m, 1).Error; err != nil {
- t.Fatal(err)
- }
- if m.Title != "字面量A" || m.Port != 11 {
- t.Fatalf("#1 更新未生效: %+v", m)
- }
- // #2: UPDATE 带 IS NULL 条件 (GORM 写路径的软删除作用域)
- res := db.Model(&SvcM{}).Where("id = ? AND deleted_at IS NULL", 1).Update("port", 33)
- if res.Error != nil || res.RowsAffected != 1 {
- t.Fatalf("#2 IS NULL 作用域失效: rows=%d err=%v", res.RowsAffected, res.Error)
- }
- // #3: 结构体 Updates (含零值 DeletedAt) — deleted_at 必须保持 NULL
- m.Title = "结构B"
- res = db.Model(&SvcM{}).Where("id = ?", 1).Updates(&m)
- if res.Error != nil || res.RowsAffected != 1 {
- t.Fatalf("#3 Updates: rows=%d err=%v", res.RowsAffected, res.Error)
- }
- var m2 SvcM
- if err := db.Unscoped().First(&m2, 1).Error; err != nil {
- t.Fatal(err)
- }
- if m2.Title != "结构B" || m2.DeletedAt.Valid {
- t.Fatalf("#3 零 DeletedAt 未归一成 NULL: %+v", m2)
- }
- // 软删除 + 作用域隐藏
- if err := db.Delete(&SvcM{}, 1).Error; err != nil {
- t.Fatal(err)
- }
- if err := db.First(&m, 1).Error; err == nil {
- t.Fatalf("软删除后 First 仍命中")
- }
- if err := db.Unscoped().First(&m2, 1).Error; err != nil {
- t.Fatalf("Unscoped 读不到: %v", err)
- }
- if !m2.DeletedAt.Valid {
- t.Fatalf("软删除标记未写入")
- }
- }
|