package service import ( "testing" "github.com/DATA-DOG/go-sqlmock" "github.com/wmms/wmms/internal/model" "github.com/wmms/wmms/pkg/database" ) // 前端表单在「新增」时可能残留上次编辑过的 id(PartnerList.vue 的 // openCreate 只做 Object.assign、不删多余键)。创建必须无视这个 id, // 否则会走成 UPDATE 把旧记录覆盖掉——这正是本次要修的缺陷。 func TestPartnerCreateInsertsEvenWhenItemCarriesStaleID(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("sqlmock: %v", err) } database.DB = db t.Cleanup(func() { database.DB = nil; db.Close() }) mock.ExpectBegin() mock.ExpectExec("INSERT INTO business_partners").WillReturnResult(sqlmock.NewResult(2, 1)) mock.ExpectExec("INSERT INTO organization_partners").WillReturnResult(sqlmock.NewResult(2, 1)) mock.ExpectCommit() item := &model.BusinessPartner{ID: 1, PartnerCode: "SUP-002", LegalName: "新伙伴", IsSupplier: true} if err := NewPartnerService().Create(item); err != nil { t.Fatalf("Create: %v", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatalf("应当执行 INSERT,实际 SQL 不是插入: %v", err) } } // 更新接口漏传 id 时必须报错,否则会静默写 0 号记录(什么都不改)而不自知。 func TestPartnerUpdateRequiresID(t *testing.T) { err := NewPartnerService().Update(&model.BusinessPartner{PartnerCode: "SUP-001", LegalName: "伙伴", IsSupplier: true}) if err == nil { t.Fatal("缺少 id 的更新应当被拒绝") } } // 拆方法不能把校验丢了。 func TestPartnerCreateStillValidatesName(t *testing.T) { err := NewPartnerService().Create(&model.BusinessPartner{PartnerCode: "SUP-001", IsSupplier: true}) if err == nil { t.Fatal("legal_name 为空应当被拒绝") } } func TestPartnerCreateStillRequiresARole(t *testing.T) { err := NewPartnerService().Create(&model.BusinessPartner{PartnerCode: "SUP-001", LegalName: "伙伴"}) if err == nil { t.Fatal("既非供应商也非客户时应当被拒绝") } } // MasterDataSettings.vue 的 openCreate 同样是 Object.assign 复用表单, // 分类新增也会带上前一次编辑残留的 id,必须走 INSERT。 func TestCategoryCreateInsertsEvenWhenItemCarriesStaleID(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("sqlmock: %v", err) } database.DB = db t.Cleanup(func() { database.DB = nil; db.Close() }) mock.ExpectExec("INSERT INTO material_categories").WillReturnResult(sqlmock.NewResult(9, 1)) item := &model.MaterialCategory{ID: 1, Code: "A99", Name: "新分类"} if err := NewMasterDataService().CreateCategory(item); err != nil { t.Fatalf("CreateCategory: %v", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatalf("应当执行 INSERT,实际 SQL 不是插入: %v", err) } } func TestUnitUpdateRequiresID(t *testing.T) { err := NewMasterDataService().UpdateUnit(&model.Unit{Code: "pcs", Name: "件"}) if err == nil { t.Fatal("缺少 id 的更新应当被拒绝") } } func TestCodeRuleUpdateRequiresID(t *testing.T) { err := NewMasterDataService().UpdateCodeRule(&model.CodeRule{CategoryID: 1, Prefix: "A", SequenceLength: 4}) if err == nil { t.Fatal("缺少 id 的更新应当被拒绝") } } // 单位的小数位校验不能因为拆方法而丢失。 func TestUnitCreateStillValidatesPrecisionScale(t *testing.T) { err := NewMasterDataService().CreateUnit(&model.Unit{Code: "kg", Name: "千克", PrecisionScale: 9}) if err == nil { t.Fatal("precision_scale 超过 6 应当被拒绝") } }