| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- package service
- import (
- "testing"
- "github.com/mhsanaei/3x-ui/v3/internal/database"
- "github.com/mhsanaei/3x-ui/v3/internal/database/model"
- "github.com/mhsanaei/3x-ui/v3/internal/tuic"
- "github.com/mhsanaei/3x-ui/v3/internal/xray"
- )
- func audit3CreateTuicClient(t *testing.T, svc *ClientService, inboundSvc *InboundService, ib *model.Inbound, email, id string) model.ClientRecord {
- t.Helper()
- if _, err := svc.Create(inboundSvc, &ClientCreatePayload{Client: model.Client{Email: email, ID: id, Password: "pw", Enable: true}, InboundIds: []int{ib.Id}}); err != nil {
- t.Fatalf("public Create %s: %v", email, err)
- }
- return lookupClientRecord(t, email)
- }
- func audit3Traffic(t *testing.T, email string) xray.ClientTraffic {
- t.Helper()
- var out xray.ClientTraffic
- if err := database.GetDB().Where("email = ?", email).First(&out).Error; err != nil {
- t.Fatal(err)
- }
- return out
- }
- func TestAudit3DeletedIdentityMustNotChargeDuplicateUUID(t *testing.T) {
- setupBulkDB(t)
- svc, inboundSvc := &ClientService{}, &InboundService{}
- ibB := mkInbound(t, 28001, model.TUIC, `{"clients":[]}`)
- ibA := mkInbound(t, 28002, model.TUIC, `{"clients":[]}`)
- const id = "a0000000-0000-0000-0000-000000000031"
- audit3CreateTuicClient(t, svc, inboundSvc, ibB, "b@audit3", id)
- a := audit3CreateTuicClient(t, svc, inboundSvc, ibA, "a@audit3", id)
- trafficID := audit3RuntimeTrafficID(t, inboundSvc, ibA, "a@audit3")
- if _, err := svc.Delete(inboundSvc, a.Id, true); err != nil {
- t.Fatalf("public Delete keepTraffic: %v", err)
- }
- if _, _, err := inboundSvc.AddTraffic(nil, []*xray.ClientTraffic{{Email: "a@audit3", TuicTrafficID: trafficID, TuicUUID: id, TuicInboundId: ibA.Id, Up: 123, Down: 456}}); err != nil {
- t.Fatal(err)
- }
- old, other := audit3Traffic(t, "a@audit3"), audit3Traffic(t, "b@audit3")
- if old.Up != 123 || old.Down != 456 || other.Up != 0 || other.Down != 0 {
- t.Fatalf("retired A=%d/%d; unrelated B=%d/%d, want A=123/456 and B=0/0", old.Up, old.Down, other.Up, other.Down)
- }
- }
- func TestAudit3RenameAndRotateCredentialsMustPreserveRetiredUsage(t *testing.T) {
- setupBulkDB(t)
- svc, inboundSvc := &ClientService{}, &InboundService{}
- ib := mkInbound(t, 28003, model.TUIC, `{"clients":[]}`)
- const oldID = "a0000000-0000-0000-0000-000000000032"
- a := audit3CreateTuicClient(t, svc, inboundSvc, ib, "old@audit3", oldID)
- trafficID := audit3RuntimeTrafficID(t, inboundSvc, ib, "old@audit3")
- if _, err := svc.Update(inboundSvc, a.Id, model.Client{Email: "new@audit3", ID: "a0000000-0000-0000-0000-000000000033", Password: "newpw", Enable: true}, 0); err != nil {
- t.Fatalf("public rename + rotate: %v", err)
- }
- if _, _, err := inboundSvc.AddTraffic(nil, []*xray.ClientTraffic{{Email: "old@audit3", TuicTrafficID: trafficID, TuicUUID: oldID, TuicInboundId: ib.Id, Up: 123, Down: 456}}); err != nil {
- t.Fatal(err)
- }
- got := audit3Traffic(t, "new@audit3")
- if got.Up != 123 || got.Down != 456 {
- t.Fatalf("new account usage=%d/%d, want retired 123/456", got.Up, got.Down)
- }
- }
- func TestAudit3PublicRenamePreservesRetiredUsage(t *testing.T) {
- setupBulkDB(t)
- svc, inboundSvc := &ClientService{}, &InboundService{}
- ib := mkInbound(t, 28004, model.TUIC, `{"clients":[]}`)
- const id = "a0000000-0000-0000-0000-000000000034"
- a := audit3CreateTuicClient(t, svc, inboundSvc, ib, "old@audit3", id)
- trafficID := audit3RuntimeTrafficID(t, inboundSvc, ib, "old@audit3")
- if _, err := svc.Update(inboundSvc, a.Id, model.Client{Email: "new@audit3", ID: id, Password: "pw", Enable: true}, 0); err != nil {
- t.Fatalf("public rename: %v", err)
- }
- if _, _, err := inboundSvc.AddTraffic(nil, []*xray.ClientTraffic{{Email: "old@audit3", TuicTrafficID: trafficID, TuicUUID: id, TuicInboundId: ib.Id, Up: 123, Down: 456}}); err != nil {
- t.Fatal(err)
- }
- got := audit3Traffic(t, "new@audit3")
- if got.Up != 123 || got.Down != 456 {
- t.Fatalf("new account usage=%d/%d, want retired 123/456", got.Up, got.Down)
- }
- }
- func TestAudit3SameInboundDuplicateUUIDMustBeRejectedOrBillMatchedPrincipal(t *testing.T) {
- setupBulkDB(t)
- svc, inboundSvc := &ClientService{}, &InboundService{}
- ib := mkInbound(t, 28005, model.TUIC, `{"clients":[]}`)
- const id = "a0000000-0000-0000-0000-000000000035"
- audit3CreateTuicClient(t, svc, inboundSvc, ib, "a@audit3", id)
- if _, err := svc.Create(inboundSvc, &ClientCreatePayload{Client: model.Client{Email: "b@audit3", ID: id, Password: "different-pw", Enable: true}, InboundIds: []int{ib.Id}}); err != nil {
- t.Logf("duplicate correctly rejected: %v", err)
- return
- }
- t.Fatal("public Create accepted duplicate UUID")
- }
- func audit3RuntimeTrafficID(t *testing.T, service *InboundService, inbound *model.Inbound, email string) int {
- t.Helper()
- fresh, err := service.GetInbound(inbound.Id)
- if err != nil {
- t.Fatal(err)
- }
- built, err := service.buildInboundForLocalRuntime(database.GetDB(), fresh)
- if err != nil {
- t.Fatal(err)
- }
- instance, ok := tuic.InstanceFromInbound(built)
- if !ok {
- t.Fatal("invalid runtime settings")
- }
- for _, client := range instance.Clients {
- if client.Email == email {
- if client.TrafficID == 0 || client.TrafficID != audit3Traffic(t, email).Id {
- t.Fatal("runtime snapshot lacks immutable accounting identity")
- }
- return client.TrafficID
- }
- }
- t.Fatal("runtime client missing")
- return 0
- }
- func TestTuicDeletedTrafficRowCannotBillReusedEmail(t *testing.T) {
- setupBulkDB(t)
- db := database.GetDB()
- old := xray.ClientTraffic{Email: "reused@audit3", Enable: true}
- if err := db.Create(&old).Error; err != nil {
- t.Fatal(err)
- }
- if err := db.Delete(&old).Error; err != nil {
- t.Fatal(err)
- }
- replacement := xray.ClientTraffic{Email: old.Email, Enable: true}
- if err := db.Create(&replacement).Error; err != nil {
- t.Fatal(err)
- }
- if old.Id == replacement.Id {
- t.Fatal("accounting primary key reused")
- }
- if _, _, err := (&InboundService{}).AddTraffic(nil, []*xray.ClientTraffic{{Email: old.Email, TuicTrafficID: old.Id, Up: 123, Down: 456}}); err != nil {
- t.Fatal(err)
- }
- got := audit3Traffic(t, old.Email)
- if got.Up != 0 || got.Down != 0 {
- t.Fatalf("retired client billed replacement: %+v", got)
- }
- }
|