// 功能描述:验证结算金额确认、溢出回滚及已有订单的幂等恢复。 // 版本:1.0.0。 package user import ( "fmt" "math" "strings" "testing" "git.apinb.com/heqiapp/platforms/backend/api/internal/models" "github.com/DATA-DOG/go-sqlmock" ) func TestCheckoutMoneyAndRollback(t *testing.T) { for _, tc := range []struct { name string price, expected int64 code int }{ {"数量按服务端价格结算", 12800, 25600, 0}, {"价格变化回滚库存", 12800, 12800, 2401}, {"金额溢出不扣库存", math.MaxInt64, 0, 1711}, } { t.Run(tc.name, func(t *testing.T) { mock := primaryTestDB(t) expectAddressAccount(mock) mock.ExpectQuery(`SELECT .* FROM "ec_order"`).WillReturnRows(sqlmock.NewRows([]string{"id"})) mock.ExpectQuery(`SELECT .* FROM "user_address"`).WillReturnRows(sqlmock.NewRows([]string{"id", "identity", "address"}).AddRow(2, "owned", "测试地址")) mock.ExpectBegin() mock.ExpectQuery(`SELECT .* FROM "ec_product".*FOR UPDATE`).WillReturnRows(sqlmock.NewRows([]string{"id", "identity", "price_amount", "stock_quantity"}).AddRow(3, "product", tc.price, 20)) if tc.code != 1711 { mock.ExpectExec(`UPDATE "ec_product" SET "stock_quantity"`).WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectQuery(`SELECT .* FROM "ec_product_image"`). WillReturnRows(sqlmock.NewRows([]string{"id", "image_uri"}).AddRow(8, "/uploads/products/cover.png")) } if tc.code == 0 { mock.ExpectQuery(`INSERT INTO "ec_order"`).WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(5)) mock.ExpectQuery(`INSERT INTO "ec_order_item"`).WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(6)) mock.ExpectCommit() } else { mock.ExpectRollback() mock.ExpectQuery(`SELECT .* FROM "ec_order"`).WillReturnRows(sqlmock.NewRows([]string{"id"})) } ctx, response := addressContext(fmt.Sprintf(`{"request_no":"order-request","address_identity":"owned","contact_name":"收货人","contact_phone":"13800000001","expected_payable_amount":%d,"items":[{"product_identity":"product","quantity":2}]}`, tc.expected), "") CreateShopOrder(ctx) if !strings.Contains(response.Body.String(), fmt.Sprintf(`"code":%d`, tc.code)) { t.Fatal(response.Body.String()) } if tc.code == 0 && !strings.Contains(response.Body.String(), `"payable_amount":25600`) { t.Fatal("未按数量返回总额") } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } }) } } func TestShopOrderProductSnapshotIncludesCover(t *testing.T) { value := shopOrderProductSnapshot(models.EcProduct{Entity: models.Entity{Identity: "product"}, Name: "报警器", ProductCode: "BJQ-01"}, "/uploads/products/cover.png") for _, expected := range []string{`"identity":"product"`, `"name":"报警器"`, `"product_code":"BJQ-01"`, `"image_url":"/uploads/products/cover.png"`} { if !strings.Contains(value, expected) { t.Fatalf("订单商品快照缺少 %s: %s", expected, value) } } } func TestCheckoutRetrySurvivesAddressRemoval(t *testing.T) { mock := primaryTestDB(t) expectAddressAccount(mock) mock.ExpectQuery(`SELECT .* FROM "ec_order".*request_no = \$1 AND user_account_id = \$2`).WillReturnRows(sqlmock.NewRows([]string{"id", "identity", "payable_amount"}).AddRow(5, "existing-order", 25600)) ctx, response := addressContext(`{"request_no":"order-request","address_identity":"removed","contact_name":"收货人","contact_phone":"13800000001","items":[{"product_identity":"product","quantity":2}]}`, "") CreateShopOrder(ctx) if !strings.Contains(response.Body.String(), `"identity":"existing-order"`) { t.Fatal(response.Body.String()) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } }