| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- package service
- import (
- "strings"
- "testing"
- "github.com/mhsanaei/3x-ui/v3/internal/database"
- "github.com/mhsanaei/3x-ui/v3/internal/database/model"
- )
- func TestUpdate_PersistsFields_NoInbound(t *testing.T) {
- cases := []struct {
- name string
- mutate func(c *model.Client)
- readBack func(rec *model.ClientRecord) any
- want any
- }{
- {
- name: "subId",
- mutate: func(c *model.Client) { c.SubID = "new-sub-id" },
- readBack: func(rec *model.ClientRecord) any { return rec.SubID },
- want: "new-sub-id",
- },
- {
- name: "totalGB cleared to zero",
- mutate: func(c *model.Client) { c.TotalGB = 0 },
- readBack: func(rec *model.ClientRecord) any { return rec.TotalGB },
- want: int64(0),
- },
- {
- name: "expiryTime",
- mutate: func(c *model.Client) { c.ExpiryTime = 1700000000 },
- readBack: func(rec *model.ClientRecord) any { return rec.ExpiryTime },
- want: int64(1700000000),
- },
- {
- name: "limitIp",
- mutate: func(c *model.Client) { c.LimitIP = 7 },
- readBack: func(rec *model.ClientRecord) any { return rec.LimitIP },
- want: 7,
- },
- {
- name: "tgId",
- mutate: func(c *model.Client) { c.TgID = 9876543210 },
- readBack: func(rec *model.ClientRecord) any { return rec.TgID },
- want: int64(9876543210),
- },
- {
- name: "comment cleared to empty",
- mutate: func(c *model.Client) { c.Comment = "" },
- readBack: func(rec *model.ClientRecord) any { return rec.Comment },
- want: "",
- },
- {
- name: "reset",
- mutate: func(c *model.Client) { c.Reset = 30 },
- readBack: func(rec *model.ClientRecord) any { return rec.Reset },
- want: 30,
- },
- {
- name: "flow",
- mutate: func(c *model.Client) { c.Flow = "xtls-rprx-vision" },
- readBack: func(rec *model.ClientRecord) any { return rec.Flow },
- want: "xtls-rprx-vision",
- },
- {
- name: "security",
- mutate: func(c *model.Client) { c.Security = "aes-128-gcm" },
- readBack: func(rec *model.ClientRecord) any { return rec.Security },
- want: "aes-128-gcm",
- },
- {
- name: "uuid rotated",
- mutate: func(c *model.Client) { c.ID = "22222222-2222-2222-2222-222222222222" },
- readBack: func(rec *model.ClientRecord) any { return rec.UUID },
- want: "22222222-2222-2222-2222-222222222222",
- },
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- setupBulkDB(t)
- svc := &ClientService{}
- inboundSvc := &InboundService{}
- email := "noib-" + strings.ReplaceAll(strings.ToLower(tc.name), " ", "-") + "@x"
- rec := &model.ClientRecord{
- Email: email,
- UUID: "11111111-1111-1111-1111-111111111111",
- SubID: email,
- TotalGB: 5,
- ExpiryTime: 1000,
- LimitIP: 1,
- TgID: 1,
- Comment: "seeded",
- Reset: 1,
- Flow: "seeded-flow",
- Security: "seeded-sec",
- }
- if err := database.GetDB().Create(rec).Error; err != nil {
- t.Fatalf("create record: %v", err)
- }
- updated := rec.ToClient()
- tc.mutate(updated)
- if _, err := svc.Update(inboundSvc, rec.Id, *updated); err != nil {
- t.Fatalf("Update: %v", err)
- }
- got, err := svc.GetByID(rec.Id)
- if err != nil {
- t.Fatalf("GetByID: %v", err)
- }
- if tc.readBack(got) != tc.want {
- t.Fatalf("%s: not persisted for no-inbound client, got %v, want %v", tc.name, tc.readBack(got), tc.want)
- }
- })
- }
- }
- func TestUpdate_NoInbound_PreservesCredentialsWhenOmitted(t *testing.T) {
- setupBulkDB(t)
- svc := &ClientService{}
- inboundSvc := &InboundService{}
- email := "noib-preserve@x"
- rec := &model.ClientRecord{
- Email: email,
- UUID: "11111111-1111-1111-1111-111111111111",
- SubID: email,
- Password: "seeded-pw",
- Auth: "seeded-auth",
- Secret: "seeded-secret",
- }
- if err := database.GetDB().Create(rec).Error; err != nil {
- t.Fatalf("create record: %v", err)
- }
- updated := rec.ToClient()
- updated.ID = ""
- updated.Password = ""
- updated.Auth = ""
- updated.Secret = ""
- updated.Comment = "only comment changed"
- if _, err := svc.Update(inboundSvc, rec.Id, *updated); err != nil {
- t.Fatalf("Update: %v", err)
- }
- got, err := svc.GetByID(rec.Id)
- if err != nil {
- t.Fatalf("GetByID: %v", err)
- }
- if got.UUID != "11111111-1111-1111-1111-111111111111" {
- t.Fatalf("uuid wiped on partial update, got %q", got.UUID)
- }
- if got.Password != "seeded-pw" {
- t.Fatalf("password wiped on partial update, got %q", got.Password)
- }
- if got.Auth != "seeded-auth" {
- t.Fatalf("auth wiped on partial update, got %q", got.Auth)
- }
- if got.Secret != "seeded-secret" {
- t.Fatalf("secret wiped on partial update, got %q", got.Secret)
- }
- if got.Comment != "only comment changed" {
- t.Fatalf("comment not persisted, got %q", got.Comment)
- }
- }
- func TestApplyClientRecordMerge_MirrorsSyncInboundRules(t *testing.T) {
- row := &model.ClientRecord{
- UUID: "kept-uuid",
- Password: "kept-pw",
- Flow: "kept-flow",
- TotalGB: 9,
- Group: "kept-group",
- Comment: "kept-comment",
- }
- incoming := &model.ClientRecord{
- Password: "new-pw",
- TotalGB: 0,
- Comment: "new-comment",
- }
- applyClientRecordMerge(row, incoming)
- if row.UUID != "kept-uuid" {
- t.Fatalf("empty incoming UUID should preserve stored UUID, got %q", row.UUID)
- }
- if row.Password != "new-pw" {
- t.Fatalf("non-empty incoming Password should overwrite, got %q", row.Password)
- }
- if row.Flow != "" {
- t.Fatalf("incoming Flow is unconditional and should overwrite with empty, got %q", row.Flow)
- }
- if row.TotalGB != 0 {
- t.Fatalf("incoming TotalGB is unconditional and should overwrite with zero, got %v", row.TotalGB)
- }
- if row.Group != "kept-group" {
- t.Fatalf("empty incoming Group should preserve stored group, got %q", row.Group)
- }
- if row.Comment != "new-comment" {
- t.Fatalf("incoming Comment is unconditional and should overwrite, got %q", row.Comment)
- }
- }
|