package repository import ( "encoding/json" "reflect" "regexp" "testing" "github.com/DATA-DOG/go-sqlmock" "github.com/wmms/wmms/internal/model" "github.com/wmms/wmms/pkg/database" ) func TestPartnerCreateSeparatesLegalEntityAndOrganizationRelationship(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("open sql mock: %v", err) } t.Cleanup(func() { database.DB = nil; db.Close() }) database.DB = db partner := &model.BusinessPartner{ OrganizationID: 1, PartnerCode: "SUP-001", LegalName: "示例供应商", IsSupplier: true, SupplierStatus: "active", } if err := json.Unmarshal([]byte(`{"supplier_categories":["material","finished_product"],"customer_categories":[]}`), partner); err != nil { t.Fatal(err) } mock.ExpectBegin() mock.ExpectExec(regexp.QuoteMeta(`INSERT INTO business_partners (legal_name, unified_credit_code, contact_name, contact_phone, address) VALUES (?, NULLIF(?, ''), NULLIF(?, ''), NULLIF(?, ''), NULLIF(?, ''))`)). WithArgs("示例供应商", "", "", "", ""). WillReturnResult(sqlmock.NewResult(9, 1)) mock.ExpectExec(regexp.QuoteMeta(`INSERT INTO organization_partners (organization_id, business_partner_id, partner_code, is_supplier, is_customer, supplier_status, customer_status, settlement_currency, payment_terms, supplier_categories, customer_categories) VALUES (?, ?, ?, ?, ?, NULLIF(?, ''), NULLIF(?, ''), NULLIF(?, ''), NULLIF(?, ''), ?, ?)`)). WithArgs(int64(1), int64(9), "SUP-001", true, false, "active", "", "", "", "material,finished_product", ""). WillReturnResult(sqlmock.NewResult(15, 1)) mock.ExpectCommit() if err := NewPartnerRepository().Create(partner); err != nil { t.Fatalf("create partner: %v", err) } if partner.ID != 15 || partner.BusinessPartnerID != 9 { t.Fatalf("unexpected identifiers: %+v", partner) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func TestPartnerListReturnsIndependentCategoriesAndEmptyArrays(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatal(err) } database.DB = db t.Cleanup(func() { database.DB = nil; db.Close() }) mock.ExpectQuery("SELECT COUNT").WithArgs(int64(1)).WillReturnRows(sqlmock.NewRows([]string{"total"}).AddRow(2)) mock.ExpectQuery("SELECT op.id").WithArgs(int64(1), 20, 0).WillReturnRows(sqlmock.NewRows([]string{ "id", "organization_id", "business_partner_id", "partner_code", "legal_name", "unified_credit_code", "contact_name", "contact_phone", "address", "is_supplier", "is_customer", "supplier_status", "customer_status", "settlement_currency", "payment_terms", "supplier_categories", "customer_categories", }).AddRow(1, 1, 10, "P-001", "公司", "", "", "", "", true, true, "active", "active", "", "", "material,finished_product", "finished_product"). AddRow(2, 1, 11, "P-002", "历史公司", "", "", "", "", true, false, "active", "", "", "", "", "")) items, total, err := NewPartnerRepository().List(1, "supplier", 20, 0, "") if err != nil { t.Fatal(err) } if total != 2 || len(items) != 2 { t.Fatalf("unexpected list: %v / %v", items, total) } if !reflect.DeepEqual(items[0].SupplierCategories, []string{"material", "finished_product"}) || !reflect.DeepEqual(items[0].CustomerCategories, []string{"finished_product"}) { t.Fatalf("categories mixed or lost: %+v", items[0]) } if items[1].SupplierCategories == nil || items[1].CustomerCategories == nil { t.Fatal("unclassified categories must be empty arrays") } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func TestPartnerUpdatePreservesOmittedCategoriesAndAllowsClearing(t *testing.T) { for _, tc := range []struct { name, body string supplier, customer interface{} }{ {"omitted", `{}`, nil, nil}, {"clear supplier", `{"supplier_categories":[]}`, "", nil}, {"change customer", `{"customer_categories":["material","finished_product"]}`, nil, "material,finished_product"}, } { t.Run(tc.name, func(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatal(err) } database.DB = db t.Cleanup(func() { database.DB = nil; db.Close() }) item := model.BusinessPartner{ID: 12, OrganizationID: 2, LegalName: "公司", IsSupplier: true, IsCustomer: true} if err := json.Unmarshal([]byte(tc.body), &item); err != nil { t.Fatal(err) } mock.ExpectBegin() mock.ExpectQuery("SELECT business_partner_id").WithArgs(int64(12), int64(2)).WillReturnRows(sqlmock.NewRows([]string{"business_partner_id"}).AddRow(7)) mock.ExpectExec("UPDATE business_partners").WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectExec("UPDATE organization_partners SET.*supplier_categories = COALESCE.*customer_categories = COALESCE"). WithArgs(true, true, "", "", "", "", tc.supplier, tc.customer, int64(12), int64(2)).WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectCommit() if err := NewPartnerRepository().Update(&item); err != nil { t.Fatal(err) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } }) } }