package usage import ( "context" "fmt" "testing" "time" "github.com/openteam/server/internal/store" "github.com/shopspring/decimal" ) // 保留期只删过期明细,余额流水一条不动;未开启(0)时什么都不做。 func TestRetentionPurgesOldLogsOnly(t *testing.T) { db := openTestDB(t) u := seedUser(t, db, 100) old := time.Now().UTC().AddDate(0, 0, -40) fresh := time.Now().UTC() for i := 0; i < 5; i++ { db.Create(&store.UsageLog{TraceID: fmt.Sprintf("old-%d", i), UserID: u, ModelName: "m1", Cost: decimal.NewFromFloat(0.01), Status: store.UsageStatusSuccess, CreatedAt: old}) } for i := 0; i < 3; i++ { db.Create(&store.UsageLog{TraceID: fmt.Sprintf("new-%d", i), UserID: u, ModelName: "m1", Cost: decimal.NewFromFloat(0.01), Status: store.UsageStatusSuccess, CreatedAt: fresh}) } db.Create(&store.BalanceLog{UserID: u, Change: decimal.NewFromFloat(-0.05), BalanceAfter: decimal.NewFromFloat(99.95), Type: store.BalanceTypeUsage, RefID: "usage:old-0"}) n := purgeOlderThan(context.Background(), db, 30) if n != 5 { t.Fatalf("应删除 5 条过期明细,实际 %d", n) } var logs, ledgers int64 db.Model(&store.UsageLog{}).Count(&logs) db.Model(&store.BalanceLog{}).Count(&ledgers) if logs != 3 { t.Fatalf("应保留 3 条新明细,实际 %d", logs) } if ledgers != 1 { t.Fatalf("余额流水不应被清理,实际 %d 条", ledgers) } } // 未配置保留期时不启动、不删数据。 func TestRetentionDisabled(t *testing.T) { db := openTestDB(t) u := seedUser(t, db, 100) db.Create(&store.UsageLog{TraceID: "x", UserID: u, ModelName: "m1", Cost: decimal.NewFromFloat(0.01), Status: store.UsageStatusSuccess, CreatedAt: time.Now().UTC().AddDate(0, 0, -400)}) stop := StartRetention(context.Background(), db, 0) stop() var n int64 db.Model(&store.UsageLog{}).Count(&n) if n != 1 { t.Fatalf("未开启保留期时不应删除数据,实际剩 %d", n) } } // 分批删除:行数超过一批时应全部删完。 func TestRetentionBatches(t *testing.T) { db := openTestDB(t) u := seedUser(t, db, 100) old := time.Now().UTC().AddDate(0, 0, -90) logs := make([]store.UsageLog, 0, retentionBatch+50) for i := 0; i < retentionBatch+50; i++ { logs = append(logs, store.UsageLog{TraceID: fmt.Sprintf("b-%d", i), UserID: u, ModelName: "m1", Cost: decimal.NewFromFloat(0.001), Status: store.UsageStatusSuccess, CreatedAt: old}) } if err := db.CreateInBatches(logs, 500).Error; err != nil { t.Fatal(err) } n := purgeOlderThan(context.Background(), db, 30) if n != int64(len(logs)) { t.Fatalf("应删除 %d 条,实际 %d", len(logs), n) } var left int64 db.Model(&store.UsageLog{}).Count(&left) if left != 0 { t.Fatalf("应全部删完,实际剩 %d", left) } }