package renewchange import ( "strings" "testing" "github.com/gogf/gf/v2/os/gtime" "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/consts" rc "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/repository/renewchange" ) func TestResolveCurrentSubscriptionsRejectsMissingAndDuplicatePriorities(t *testing.T) { tests := []struct { name string rows []*rc.SimCardBindPkg }{ {name: "missing"}, { name: "duplicate effective", rows: []*rc.SimCardBindPkg{ {Iccid: "8986001", EffectiveStatus: consts.EffectiveStatusEffective}, {Iccid: "8986001", EffectiveStatus: consts.EffectiveStatusEffective}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if _, _, err := resolveCurrentSubscriptions(tt.rows, []string{"8986001"}); err == nil { t.Fatal("resolveCurrentSubscriptions() error = nil") } }) } } func TestSelectCurrentSubscriptionIgnoresCardAddPackage(t *testing.T) { iccid := "8986000000000000103" formal := &rc.SimCardBindPkg{ Iccid: iccid, PackageType: consts.PackageTypeCard, PackageCode: "CARD-FORMAL", EffectiveStatus: consts.EffectiveStatusEffective, } addPackage := &rc.SimCardBindPkg{ Iccid: iccid, PackageType: consts.PackageTypeCardAddPackage, PackageCode: "CARD-ADD", EffectiveStatus: consts.EffectiveStatusEffective, } current, future, err := selectCurrentSubscription([]*rc.SimCardBindPkg{formal, addPackage}, iccid) if err != nil { t.Fatalf("selectCurrentSubscription() error = %v", err) } if current != formal { t.Fatalf("current package = %+v, want formal package", current) } if len(future) != 0 { t.Fatalf("future package count = %d, want 0", len(future)) } } func TestSelectCurrentSubscriptionIgnoresPoolAddPackage(t *testing.T) { iccid := "8986000000000000104" formal := &rc.SimCardBindPkg{ Iccid: iccid, PackageType: consts.PackageTypePool, PackageCode: "POOL-FORMAL", EffectiveStatus: consts.EffectiveStatusEffective, } addPackage := &rc.SimCardBindPkg{ Iccid: iccid, PackageType: consts.PackageTypePoolAddPackage, PackageCode: "POOL-ADD", EffectiveStatus: consts.EffectiveStatusEffective, } current, _, err := selectCurrentSubscription([]*rc.SimCardBindPkg{addPackage, formal}, iccid) if err != nil { t.Fatalf("selectCurrentSubscription() error = %v", err) } if current != formal { t.Fatalf("current package = %+v, want formal package", current) } } func TestSelectCurrentSubscriptionRejectsTwoEffectiveFormalPackages(t *testing.T) { iccid := "8986000000000000102" rows := []*rc.SimCardBindPkg{ { Iccid: iccid, PackageType: consts.PackageTypeCard, PackageCode: "FORMAL-1", EffectiveStatus: consts.EffectiveStatusEffective, }, { Iccid: iccid, PackageType: consts.PackageTypePool, PackageCode: "FORMAL-2", EffectiveStatus: consts.EffectiveStatusEffective, }, } _, _, err := selectCurrentSubscription(rows, iccid) if err == nil { t.Fatal("selectCurrentSubscription() error = nil, want duplicate formal package error") } if !strings.Contains(err.Error(), "多个生效中的套餐订购记录") { t.Fatalf("error = %q, want duplicate formal package message", err.Error()) } } func TestResolveCurrentSubscriptionsAllowsChainedPendingRenewals(t *testing.T) { rows := []*rc.SimCardBindPkg{ {Iccid: "8986001", SubscriptionNo: "SUB-E", EffectiveStatus: consts.EffectiveStatusEffective}, {Iccid: "8986001", SubscriptionNo: "SUB-P2", EffectiveStatus: consts.EffectiveStatusPending, ExpectedExpireTime: gtime.NewFromStr("2028-01-01 00:00:00")}, {Iccid: "8986001", SubscriptionNo: "SUB-P1", EffectiveStatus: consts.EffectiveStatusPending, ExpectedExpireTime: gtime.NewFromStr("2027-01-01 00:00:00")}, } current, future, err := resolveCurrentSubscriptions(rows, []string{"8986001"}) if err != nil { t.Fatalf("resolveCurrentSubscriptions() error = %v", err) } if current["8986001"].SubscriptionNo != "SUB-E" { t.Fatalf("current subscription = %q, want SUB-E", current["8986001"].SubscriptionNo) } if len(future["8986001"]) != 2 { t.Fatalf("future subscriptions len = %d, want 2", len(future["8986001"])) } if future["8986001"][0].SubscriptionNo != "SUB-P1" || future["8986001"][1].SubscriptionNo != "SUB-P2" { t.Fatalf("future subscriptions order = %+v, want SUB-P1,SUB-P2", future["8986001"]) } } func TestResolveCurrentSubscriptionsFallsBackToLatestExpiredSubscription(t *testing.T) { rows := []*rc.SimCardBindPkg{ { Iccid: "8986001", SubscriptionNo: "SUB-X1", EffectiveStatus: consts.EffectiveStatusExpired, ExpectedExpireTime: gtime.NewFromStr("2026-01-01 00:00:00"), }, { Iccid: "8986001", SubscriptionNo: "SUB-X2", EffectiveStatus: consts.EffectiveStatusExpired, ExpectedExpireTime: gtime.NewFromStr("2026-07-01 00:00:00"), }, { Iccid: "8986001", SubscriptionNo: "SUB-T", EffectiveStatus: consts.EffectiveStatusTerminated, }, } current, future, err := resolveCurrentSubscriptions(rows, []string{"8986001"}) if err != nil { t.Fatalf("resolveCurrentSubscriptions() error = %v", err) } if current["8986001"] == nil || current["8986001"].SubscriptionNo != "SUB-X2" { t.Fatalf("current subscription = %+v, want SUB-X2", current["8986001"]) } if len(future["8986001"]) != 0 { t.Fatalf("future subscriptions = %+v, want empty", future["8986001"]) } } func TestResolveCurrentSubscriptionsPrefersPendingOverExpiredHistory(t *testing.T) { rows := []*rc.SimCardBindPkg{ { Iccid: "8986001", SubscriptionNo: "SUB-X", EffectiveStatus: consts.EffectiveStatusExpired, ExpectedExpireTime: gtime.NewFromStr("2026-07-01 00:00:00"), }, { Iccid: "8986001", SubscriptionNo: "SUB-P", EffectiveStatus: consts.EffectiveStatusPending, ExpectedExpireTime: gtime.NewFromStr("2027-07-01 00:00:00"), }, } current, future, err := resolveCurrentSubscriptions(rows, []string{"8986001"}) if err != nil { t.Fatalf("resolveCurrentSubscriptions() error = %v", err) } if current["8986001"] == nil || current["8986001"].SubscriptionNo != "SUB-P" { t.Fatalf("current subscription = %+v, want SUB-P", current["8986001"]) } if len(future["8986001"]) != 0 { t.Fatalf("future subscriptions = %+v, want empty", future["8986001"]) } } func TestResolveCurrentSubscriptionsStillRejectsOnlyTerminatedOrVoidedHistory(t *testing.T) { rows := []*rc.SimCardBindPkg{ {Iccid: "8986001", EffectiveStatus: consts.EffectiveStatusTerminated}, {Iccid: "8986001", EffectiveStatus: consts.EffectiveStatusVoided}, } if _, _, err := resolveCurrentSubscriptions(rows, []string{"8986001"}); err == nil { t.Fatal("terminated/voided history was accepted as current subscription") } } func TestValidateCurrentSubscriptionChecksPackageAndBusinessSource(t *testing.T) { card, bind, expected := validSubscriptionValidationFixture() if err := validateCurrentSubscription(card, bind, expected); err != nil { t.Fatalf("validateCurrentSubscription() error = %v", err) } bind.PackageCode = "OTHER" if err := validateCurrentSubscription(card, bind, expected); err == nil { t.Fatal("package mismatch was accepted") } bind.PackageCode = expected.PackageCode bind.BatchNo = "OUT-OTHER" card.OutboundBatchNo = "OUT-OTHER" if err := validateCurrentSubscription(card, bind, expected); err == nil { t.Fatal("source batch mismatch was accepted") } } func TestValidateCurrentSubscriptionIgnoresSourceForInputSelfReference(t *testing.T) { card, bind, expected := validSubscriptionValidationFixture() bind.BatchNo = "OUT-ACTUAL" card.OutboundBatchNo = "" expected.SourceBatchNo = "" if err := validateCurrentSubscription(card, bind, expected); err != nil { t.Fatalf("input source should be ignored, got %v", err) } } func TestValidateCurrentSubscriptionRejectsIdentityChanges(t *testing.T) { tests := []struct { name string mutate func(*rc.SimCardInfo, *rc.SimCardBindPkg, *subscriptionValidation) }{ {name: "ICCID", mutate: func(card *rc.SimCardInfo, _ *rc.SimCardBindPkg, _ *subscriptionValidation) { card.Iccid = "8986002" }}, {name: "MSISDN", mutate: func(card *rc.SimCardInfo, _ *rc.SimCardBindPkg, _ *subscriptionValidation) { card.Msisdn = "13900000000" }}, {name: "IMSI", mutate: func(card *rc.SimCardInfo, _ *rc.SimCardBindPkg, _ *subscriptionValidation) { card.Imsi = "460002" }}, {name: "tenant", mutate: func(card *rc.SimCardInfo, _ *rc.SimCardBindPkg, _ *subscriptionValidation) { card.TenantId = "OTHER" }}, {name: "customer", mutate: func(card *rc.SimCardInfo, _ *rc.SimCardBindPkg, _ *subscriptionValidation) { card.DeptId = 118 }}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { card, bind, expected := validSubscriptionValidationFixture() tt.mutate(card, bind, &expected) if err := validateCurrentSubscription(card, bind, expected); err == nil { t.Fatalf("%s change was accepted", tt.name) } }) } } func TestValidateCurrentSubscriptionAllowsAllocatedCardWithHistoricalBindDept(t *testing.T) { card, bind, expected := validSubscriptionValidationFixture() bind.DeptId = 100 if err := validateCurrentSubscription(card, bind, expected); err != nil { t.Fatalf("allocated card with historical bind department was rejected: %v", err) } } func validSubscriptionValidationFixture() (*rc.SimCardInfo, *rc.SimCardBindPkg, subscriptionValidation) { expected := subscriptionValidation{ ICCID: "8986001", MSISDN: "13800000000", IMSI: "460001", TenantID: "000000", CustomerDeptID: 117, PackageCode: "PKG-OLD", SourceBatchNo: "OUT-1", } card := &rc.SimCardInfo{ Iccid: expected.ICCID, Msisdn: expected.MSISDN, Imsi: expected.IMSI, DeptId: expected.CustomerDeptID, CurrentPackageCode: expected.PackageCode, PackageStatus: consts.PackageStatusEffective, OutboundBatchNo: expected.SourceBatchNo, TenantId: expected.TenantID, } bind := &rc.SimCardBindPkg{ Iccid: expected.ICCID, Msisdn: expected.MSISDN, DeptId: expected.CustomerDeptID, PackageCode: expected.PackageCode, EffectiveStatus: consts.EffectiveStatusEffective, BatchNo: expected.SourceBatchNo, TenantId: expected.TenantID, } return card, bind, expected }