package renewchange import ( "reflect" "strings" "testing" "time" "github.com/gogf/gf/v2/os/gtime" ) func TestOrderDOLocalSchemaMatchesProductionContract(t *testing.T) { want := []string{ "Id", "OrderSn", "ParentOrderId", "ParentOrderSn", "CustomerType", "CustomerAgentId", "EndUserId", "AgentId", "OrderType", "OrderStatus", "ShippingStatus", "ReturnStatus", "Consignee", "Mobile", "Email", "Address", "AreaCode", "BuyerNote", "AdminNote", "Remark", "ShippingMethod", "LogisticsId", "LogisticsName", "ShippingTypeId", "ShippingTypeName", "TrackingNo", "ShippingTime", "ReceivedTime", "PayType", "PayStatus", "PayOrderSn", "PayTime", "TotalAmount", "ProductAmount", "ShippingFee", "ServiceFee", "InvoiceFee", "CouponAmount", "DiscountAmount", "Balance", "PaidAmount", "OnlinePaidAmount", "OfflinePaidAmount", "UnpaidAmount", "UnrefundAmount", "IsNeedCommission", "DistributionStatus", "TenantId", "CreatedDept", "CreatedBy", "CreatedAt", "UpdatedBy", "UpdatedAt", } assertDOFields(t, reflect.TypeOf(OrderDO{}), want) } func TestOrderItemDOLocalSchemaMatchesProductionContract(t *testing.T) { want := []string{ "ItemId", "OrderId", "OrderSn", "CustomerType", "CustomerAgentId", "EndUserId", "Price", "Quantity", "ProductId", "ProductName", "ProductSn", "PicThumb", "SkuId", "SkuData", "DeliveryQuantity", "ProductType", "IsGift", "AgentId", "PrepayPrice", "Commission", "OriginPrice", "PromotionData", "ExtraSkuData", "PayMode", "ServiceBeginTime", "ServiceEndTime", "TenantId", "CreatedDept", "CreatedBy", "CreatedAt", "UpdatedBy", "UpdatedAt", } assertDOFields(t, reflect.TypeOf(OrderItemDO{}), want) } func TestOrderDOsHaveExplicitORMColumnMappings(t *testing.T) { for _, testCase := range []struct { name string typ reflect.Type }{ {name: "order", typ: reflect.TypeOf(OrderDO{})}, {name: "order item", typ: reflect.TypeOf(OrderItemDO{})}, {name: "change history", typ: reflect.TypeOf(ChangeBindHistoryDO{})}, {name: "change order update", typ: reflect.TypeOf(changeOrderUpdateDO{})}, {name: "change order item update", typ: reflect.TypeOf(changeOrderItemUpdateDO{})}, {name: "change card update", typ: reflect.TypeOf(changeCardUpdateDO{})}, } { t.Run(testCase.name, func(t *testing.T) { for i := 0; i < testCase.typ.NumField(); i++ { field := testCase.typ.Field(i) if field.Name == "Meta" { continue } if got := field.Tag.Get("orm"); got == "" { t.Errorf("%s has no explicit orm column mapping", field.Name) } } }) } } func TestOrderDOTagsMatchProductionColumns(t *testing.T) { assertDOColumnTags(t, reflect.TypeOf(OrderDO{}), map[string]string{ "OrderSn": "order_sn", "CustomerAgentId": "customer_agent_id", "EndUserId": "end_user_id", "AgentId": "agent_id", "OrderType": "order_type", "OrderStatus": "order_status", "ShippingStatus": "shipping_status", "ReturnStatus": "return_status", "BuyerNote": "buyer_note", "AdminNote": "admin_note", "Remark": "remark", "PayType": "pay_type", "PayStatus": "pay_status", "PayTime": "pay_time", "TotalAmount": "total_amount", "ProductAmount": "product_amount", "PaidAmount": "paid_amount", "OnlinePaidAmount": "online_paid_amount", "OfflinePaidAmount": "offline_paid_amount", "TenantId": "tenant_id", "CreatedDept": "created_dept", "CreatedBy": "created_by", "CreatedAt": "created_at", "UpdatedBy": "updated_by", "UpdatedAt": "updated_at", }) assertDOColumnTags(t, reflect.TypeOf(OrderItemDO{}), map[string]string{ "ItemId": "item_id", "OrderId": "order_id", "OrderSn": "order_sn", "CustomerAgentId": "customer_agent_id", "EndUserId": "end_user_id", "Price": "price", "Quantity": "quantity", "ProductId": "product_id", "ProductName": "product_name", "ProductSn": "product_sn", "SkuData": "sku_data", "ProductType": "product_type", "IsGift": "is_gift", "AgentId": "agent_id", "OriginPrice": "origin_price", "ExtraSkuData": "extra_sku_data", "PayMode": "pay_mode", "ServiceBeginTime": "service_begin_time", "ServiceEndTime": "service_end_time", "TenantId": "tenant_id", "CreatedDept": "created_dept", "CreatedBy": "created_by", "CreatedAt": "created_at", "UpdatedBy": "updated_by", "UpdatedAt": "updated_at", }) } func TestNextOrderSNIsGeneratedBeforeInsert(t *testing.T) { got, err := NextOrderSN(time.Date(2026, 8, 3, 10, 2, 50, 0, time.Local)) if err != nil { t.Fatalf("NextOrderSN returned error: %v", err) } if !strings.HasPrefix(got, "SO20260803100250") { t.Fatalf("NextOrderSN = %q, want SO prefix and timestamp", got) } if len(got) != len("SO20260803100250")+8 { t.Fatalf("NextOrderSN length = %d, want %d", len(got), len("SO20260803100250")+8) } } func TestValidateContractOrderRequiredFields(t *testing.T) { now := gtime.NewFromTime(time.Date(2026, 8, 3, 10, 2, 50, 0, time.Local)) order := &OrderDO{ OrderSn: "SO20260803100250ABCDEFGH", CustomerAgentId: int64(200), AgentId: int64(100), TenantId: "000000", CreatedDept: int64(100), CreatedBy: int64(1), CreatedAt: now, UpdatedBy: int64(1), UpdatedAt: now, } if err := ValidateOrderDO(order); err != nil { t.Fatalf("ValidateOrderDO returned error: %v", err) } for _, testCase := range []struct { name string clear func(*OrderDO) }{ {name: "order number", clear: func(order *OrderDO) { order.OrderSn = "" }}, {name: "customer agent", clear: func(order *OrderDO) { order.CustomerAgentId = int64(0) }}, {name: "seller agent", clear: func(order *OrderDO) { order.AgentId = int64(0) }}, {name: "tenant", clear: func(order *OrderDO) { order.TenantId = "" }}, {name: "creator department", clear: func(order *OrderDO) { order.CreatedDept = int64(0) }}, {name: "creator", clear: func(order *OrderDO) { order.CreatedBy = int64(0) }}, {name: "updater", clear: func(order *OrderDO) { order.UpdatedBy = int64(0) }}, {name: "created time", clear: func(order *OrderDO) { order.CreatedAt = nil }}, {name: "updated time", clear: func(order *OrderDO) { order.UpdatedAt = nil }}, } { t.Run(testCase.name, func(t *testing.T) { invalid := *order testCase.clear(&invalid) if err := ValidateOrderDO(&invalid); err == nil { t.Fatalf("ValidateOrderDO accepted missing %s", testCase.name) } }) } } func TestValidateContractOrderItemRequiredFields(t *testing.T) { beginAt := gtime.NewFromTime(time.Date(2026, 8, 3, 0, 0, 0, 0, time.Local)) endAt := gtime.NewFromTime(time.Date(2027, 8, 3, 0, 0, 0, 0, time.Local)) item := &OrderItemDO{ OrderId: int64(1), OrderSn: "SO20260803100250ABCDEFGH", CustomerAgentId: int64(200), AgentId: int64(100), Quantity: 2, ProductId: int64(300), ProductName: "package", ProductSn: "PKG001", ServiceBeginTime: beginAt, ServiceEndTime: endAt, TenantId: "000000", CreatedDept: int64(100), CreatedBy: int64(1), CreatedAt: beginAt, UpdatedBy: int64(1), UpdatedAt: beginAt, } if err := ValidateOrderItemDO(item); err != nil { t.Fatalf("ValidateOrderItemDO returned error: %v", err) } for _, testCase := range []struct { name string clear func(*OrderItemDO) }{ {name: "order id", clear: func(item *OrderItemDO) { item.OrderId = int64(0) }}, {name: "order number", clear: func(item *OrderItemDO) { item.OrderSn = "" }}, {name: "customer agent", clear: func(item *OrderItemDO) { item.CustomerAgentId = int64(0) }}, {name: "seller agent", clear: func(item *OrderItemDO) { item.AgentId = int64(0) }}, {name: "quantity", clear: func(item *OrderItemDO) { item.Quantity = 0 }}, {name: "product id", clear: func(item *OrderItemDO) { item.ProductId = int64(0) }}, {name: "product name", clear: func(item *OrderItemDO) { item.ProductName = "" }}, {name: "product code", clear: func(item *OrderItemDO) { item.ProductSn = "" }}, {name: "service start", clear: func(item *OrderItemDO) { item.ServiceBeginTime = nil }}, {name: "service end", clear: func(item *OrderItemDO) { item.ServiceEndTime = nil }}, {name: "tenant", clear: func(item *OrderItemDO) { item.TenantId = "" }}, {name: "creator department", clear: func(item *OrderItemDO) { item.CreatedDept = int64(0) }}, {name: "creator", clear: func(item *OrderItemDO) { item.CreatedBy = int64(0) }}, {name: "updater", clear: func(item *OrderItemDO) { item.UpdatedBy = int64(0) }}, {name: "created time", clear: func(item *OrderItemDO) { item.CreatedAt = nil }}, {name: "updated time", clear: func(item *OrderItemDO) { item.UpdatedAt = nil }}, } { t.Run(testCase.name, func(t *testing.T) { invalid := *item testCase.clear(&invalid) if err := ValidateOrderItemDO(&invalid); err == nil { t.Fatalf("ValidateOrderItemDO accepted missing %s", testCase.name) } }) } } func assertDOFields(t *testing.T, typ reflect.Type, want []string) { t.Helper() got := make([]string, 0, typ.NumField()-1) for i := 0; i < typ.NumField(); i++ { field := typ.Field(i) if field.Name == "Meta" { continue } got = append(got, field.Name) } if !reflect.DeepEqual(got, want) { t.Fatalf("%s fields mismatch\n got: %v\nwant: %v", typ.Name(), got, want) } } func assertDOColumnTags(t *testing.T, typ reflect.Type, want map[string]string) { t.Helper() for fieldName, column := range want { field, ok := typ.FieldByName(fieldName) if !ok { t.Fatalf("%s missing field %s", typ.Name(), fieldName) } if got := field.Tag.Get("orm"); got != column { t.Fatalf("%s.%s orm tag = %q, want %q", typ.Name(), fieldName, got, column) } } }