|
|
@@ -129,6 +129,65 @@ func TestMigrationRequirements_CleansLegacyZeroAddrTag(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+func TestMigrationRemoveOrphanedTraffics(t *testing.T) {
|
|
|
+ setupConflictDB(t)
|
|
|
+ db := database.GetDB()
|
|
|
+ clientSvc := &ClientService{}
|
|
|
+ inboundSvc := &InboundService{}
|
|
|
+
|
|
|
+ const attachedEmail = "[email protected]"
|
|
|
+ attachedClient := model.Client{Email: attachedEmail, ID: "11111111-1111-1111-1111-111111111111", SubID: attachedEmail, Enable: true}
|
|
|
+ attachedIb := mkInbound(t, 30003, model.VLESS, clientsSettings(t, []model.Client{attachedClient}))
|
|
|
+ if err := clientSvc.SyncInbound(nil, attachedIb.Id, []model.Client{attachedClient}); err != nil {
|
|
|
+ t.Fatalf("seed attached client: %v", err)
|
|
|
+ }
|
|
|
+ mkTraffic(t, attachedIb.Id, attachedEmail, 0, 0, 0, 0, true)
|
|
|
+
|
|
|
+ const detachedEmail = "[email protected]"
|
|
|
+ detachedClient := model.Client{Email: detachedEmail, ID: "22222222-2222-2222-2222-222222222222", SubID: detachedEmail, Enable: true}
|
|
|
+ detachedIb := mkInbound(t, 30004, model.VLESS, clientsSettings(t, []model.Client{detachedClient}))
|
|
|
+ if err := clientSvc.SyncInbound(nil, detachedIb.Id, []model.Client{detachedClient}); err != nil {
|
|
|
+ t.Fatalf("seed detached client: %v", err)
|
|
|
+ }
|
|
|
+ mkTraffic(t, detachedIb.Id, detachedEmail, 123, 456, 0, 0, true)
|
|
|
+ detachedRec := lookupClientRecord(t, detachedEmail)
|
|
|
+ if _, err := clientSvc.Detach(inboundSvc, detachedRec.Id, []int{detachedIb.Id}); err != nil {
|
|
|
+ t.Fatalf("Detach: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ const jsonOnlyEmail = "[email protected]"
|
|
|
+ jsonOnlyClient := model.Client{Email: jsonOnlyEmail, ID: "33333333-3333-3333-3333-333333333333", SubID: jsonOnlyEmail, Enable: true}
|
|
|
+ jsonOnlyIb := mkInbound(t, 30005, model.VLESS, clientsSettings(t, []model.Client{jsonOnlyClient}))
|
|
|
+ mkTraffic(t, jsonOnlyIb.Id, jsonOnlyEmail, 0, 0, 0, 0, true)
|
|
|
+
|
|
|
+ const trulyOrphanedEmail = "[email protected]"
|
|
|
+ mkTraffic(t, attachedIb.Id, trulyOrphanedEmail, 0, 0, 0, 0, true)
|
|
|
+
|
|
|
+ inboundSvc.MigrationRemoveOrphanedTraffics()
|
|
|
+
|
|
|
+ cases := []struct {
|
|
|
+ name string
|
|
|
+ email string
|
|
|
+ want int64
|
|
|
+ }{
|
|
|
+ {"attached, in clients table and JSON", attachedEmail, 1},
|
|
|
+ {"detached-but-alive, in clients table only", detachedEmail, 1},
|
|
|
+ {"seeder-skipped-but-live, in JSON only", jsonOnlyEmail, 1},
|
|
|
+ {"truly orphaned, in neither", trulyOrphanedEmail, 0},
|
|
|
+ }
|
|
|
+ for _, c := range cases {
|
|
|
+ t.Run(c.name, func(t *testing.T) {
|
|
|
+ var got int64
|
|
|
+ if err := db.Model(xray.ClientTraffic{}).Where("email = ?", c.email).Count(&got).Error; err != nil {
|
|
|
+ t.Fatalf("count client_traffics for %s: %v", c.email, err)
|
|
|
+ }
|
|
|
+ if got != c.want {
|
|
|
+ t.Errorf("client_traffics count for %s: got %d, want %d", c.email, got, c.want)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
func TestMigrationRequirements_NormalizesShareAddressFields(t *testing.T) {
|
|
|
setupConflictDB(t)
|
|
|
db := database.GetDB()
|