package logic import ( "context" "errors" "testing" "time" adminentity "spark-server/internal/module/admin/entity" adminmodel "spark-server/internal/module/admin/model" friendentity "spark-server/internal/module/friend/entity" groupentity "spark-server/internal/module/group/entity" msgentity "spark-server/internal/module/message/entity" userentity "spark-server/internal/module/user/entity" "spark-server/internal/pkg/errs" ) type seqID struct{ n int64 } func (s *seqID) NextID() (int64, error) { s.n++ return s.n, nil } type memoryAdmin struct { users []adminmodel.UserRow emails map[string]int64 groups map[int64]adminmodel.GroupRow members map[int64][]adminmodel.GroupMemberRow groupMsgs map[int64][]adminmodel.MessageRow msgs []adminmodel.MessageRow convs []adminmodel.ConversationRow complaints []adminmodel.ComplaintRow stats *adminentity.ImStats ips []adminmodel.SameIPRankRow owners map[int64]int64 } func (m *memoryAdmin) ListUsers(_ context.Context, emailLike string, page, size int) ([]adminmodel.UserRow, int64, error) { start := page * size if start >= len(m.users) { return nil, int64(len(m.users)), nil } end := start + size if end > len(m.users) { end = len(m.users) } _ = emailLike return m.users[start:end], int64(len(m.users)), nil } func (m *memoryAdmin) UsersByEmail(_ context.Context, email string) ([]adminmodel.UserRow, error) { out := make([]adminmodel.UserRow, 0) for _, u := range m.users { if u.Email == email { out = append(out, u) } } return out, nil } func (m *memoryAdmin) UserByID(_ context.Context, id int64) (*adminmodel.UserRow, error) { for i := range m.users { if m.users[i].ID == id { cp := m.users[i] return &cp, nil } } return nil, errs.ErrNotFound } func (m *memoryAdmin) UsersByNickname(_ context.Context, nickname string) ([]adminmodel.UserRow, error) { out := make([]adminmodel.UserRow, 0) for _, u := range m.users { if u.Nickname == nickname { out = append(out, u) } } return out, nil } func (m *memoryAdmin) UsersByDevice(_ context.Context, _ string, deviceIDNum uint64) ([]adminmodel.UserRow, error) { out := make([]adminmodel.UserRow, 0) for _, u := range m.users { if u.DeviceID == deviceIDNum { out = append(out, u) } } return out, nil } func (m *memoryAdmin) UsersByIP(_ context.Context, _ string) ([]adminmodel.UserRow, error) { return m.users, nil } func (m *memoryAdmin) SetUserStatusByEmail(_ context.Context, email string, status userentity.Status) error { for i := range m.users { if m.users[i].Email == email { m.users[i].Status = int(status) return nil } } return errs.ErrNotFound } func (m *memoryAdmin) SetNicknameByEmail(_ context.Context, email, nickname string) error { for i := range m.users { if m.users[i].Email == email { m.users[i].Nickname = nickname return nil } } return errs.ErrNotFound } func (m *memoryAdmin) ClearAvatarByEmail(_ context.Context, email string) error { for i := range m.users { if m.users[i].Email == email { m.users[i].Avatar = "" return nil } } return errs.ErrNotFound } func (m *memoryAdmin) SameIPRank(_ context.Context, _ int) ([]adminmodel.SameIPRankRow, error) { return m.ips, nil } func (m *memoryAdmin) LookupUserIDByEmail(_ context.Context, email string) (int64, error) { if m.emails == nil { return 0, nil } return m.emails[email], nil } func (m *memoryAdmin) ListGroups(_ context.Context, _ string) ([]adminmodel.GroupRow, error) { out := make([]adminmodel.GroupRow, 0, len(m.groups)) for id := range m.groups { out = append(out, m.groups[id]) } return out, nil } func (m *memoryAdmin) GroupByID(_ context.Context, groupID int64) (*adminmodel.GroupRow, error) { g, ok := m.groups[groupID] if !ok { return nil, errs.ErrNotFound } return &g, nil } func (m *memoryAdmin) ActiveGroupOwner(_ context.Context, groupID int64) (int64, error) { id, ok := m.owners[groupID] if !ok { return 0, errs.ErrNotFound } return id, nil } func (m *memoryAdmin) ListGroupMembers(_ context.Context, groupID int64) ([]adminmodel.GroupMemberRow, error) { return m.members[groupID], nil } func (m *memoryAdmin) ListGroupMessages(_ context.Context, groupID int64, _ int) ([]adminmodel.MessageRow, error) { return m.groupMsgs[groupID], nil } func (m *memoryAdmin) ListComplaints(_ context.Context, _ int) ([]adminmodel.ComplaintRow, error) { return m.complaints, nil } func (m *memoryAdmin) InsertComplaint(_ context.Context, r *friendentity.Report) error { if r == nil { return errs.ErrInvalidArgument } m.complaints = append(m.complaints, adminmodel.ComplaintRow{ ID: r.ID, UserID: r.UserID, TargetID: r.TargetID, Reason: int(r.Reason), Detail: r.Detail, CreatedAt: r.CreatedAt, }) return nil } func (m *memoryAdmin) ImStats(context.Context) (*adminentity.ImStats, error) { return m.stats, nil } func (m *memoryAdmin) ListRecentMessages(_ context.Context, _ int) ([]adminmodel.MessageRow, error) { return m.msgs, nil } func (m *memoryAdmin) ListUserConversations(_ context.Context, _ int64) ([]adminmodel.ConversationRow, error) { return m.convs, nil } func TestAdminServiceNotReady(t *testing.T) { s := &Service{} if _, _, err := s.ListUsers(t.Context(), "", 0, 0); !errors.Is(err, ErrNotReady) { t.Fatalf("list %v", err) } } func TestAdminServiceOps(t *testing.T) { now := time.Unix(1_700_000_000, 0).UTC() active := now repo := &memoryAdmin{ users: []adminmodel.UserRow{{ ID: 11, Email: "a@spark.com", Nickname: "Ada", SparkID: "ada_ok01", Status: 1, DeviceID: 7, Platform: "ios", CreatedAt: now, LastActiveAt: &active, }}, emails: map[string]int64{"a@spark.com": 11, "b@spark.com": 12}, groups: map[int64]adminmodel.GroupRow{ 3: {ID: 3, Name: "g", OwnerID: 11, Status: int(groupentity.StatusActive), MemberCount: 2, CreatedAt: now, UpdatedAt: now}, }, members: map[int64][]adminmodel.GroupMemberRow{ 3: {{UserID: 11, Role: int(groupentity.RoleOwner), Nickname: "", UNick: "Ada", Email: "a@spark.com"}}, }, groupMsgs: map[int64][]adminmodel.MessageRow{ 3: {{ID: 99, SenderID: 11, MessageType: int16(msgentity.TypeText), CreatedAt: now}}, }, msgs: []adminmodel.MessageRow{ {ID: 1, SenderID: 11, ReceiverID: 12, MessageType: int16(msgentity.TypeText), Content: []byte(`{"t":1,"text":{"body":"hi"}}`), CreatedAt: now, ChatType: "private"}, }, convs: []adminmodel.ConversationRow{ {ID: 4, Type: 1, UserSmall: 11, UserBig: 12, LastMsgID: 1, LastMsgAt: now}, {ID: 5, Type: 2, GroupID: 3, LastMsgID: 99, LastMsgAt: now}, }, complaints: []adminmodel.ComplaintRow{{ID: 1, Email: "a@spark.com", TargetID: 12, Reason: 1, CreatedAt: now}}, stats: &adminentity.ImStats{MessageCount: 2}, ips: []adminmodel.SameIPRankRow{{IP: "1.1.1.1", UserCount: 2, DeviceCount: 3}}, owners: map[int64]int64{3: 11}, } svc := NewService(repo, nil, &seqID{n: 100}) ctx := t.Context() list, total, err := svc.ListUsers(ctx, "", -1, 0) if err != nil || total != 1 || len(list) != 1 || list[0].Email != "a@spark.com" { t.Fatalf("list %+v %d %v", list, total, err) } byMail, err := svc.UserByEmail(ctx, "A@spark.com") if err != nil || byMail["email"] != "a@spark.com" { t.Fatalf("email %+v %v", byMail, err) } if _, err := svc.UserByEmail(ctx, " "); !errors.Is(err, errs.ErrInvalidArgument) { t.Fatalf("empty email %v", err) } u, err := svc.UserByID(ctx, 11) if err != nil || u.ID != 11 || u.DeviceID != "7" { t.Fatalf("by id %+v %v", u, err) } nicks, err := svc.UsersByNickname(ctx, "Ada") if err != nil || len(nicks) != 1 { t.Fatalf("nick %+v %v", nicks, err) } devs, err := svc.UsersByDevice(ctx, "7") if err != nil || len(devs) != 1 { t.Fatalf("dev %+v %v", devs, err) } ips, err := svc.UsersByIP(ctx, "1.1.1.1") if err != nil || len(ips) != 1 { t.Fatalf("ip %+v %v", ips, err) } if err := svc.BanByEmail(ctx, "a@spark.com", true); err != nil { t.Fatal(err) } if repo.users[0].Status != int(userentity.StatusDisabled) { t.Fatal("ban") } if err := svc.ResetNicknameByEmail(ctx, "a@spark.com", ""); err != nil || repo.users[0].Nickname != "用户" { t.Fatalf("nick %v %s", err, repo.users[0].Nickname) } if err := svc.ResetAvatarByEmail(ctx, "a@spark.com"); err != nil || repo.users[0].Avatar != "" { t.Fatal(err) } rank, err := svc.SameIPRank(ctx, 0) if err != nil || len(rank) != 1 || rank[0].Count != 2 { t.Fatalf("rank %+v %v", rank, err) } groups, err := svc.ListGroups(ctx, "") if err != nil || len(groups) != 1 || groups[0].GroupID != "3" { t.Fatalf("groups %+v %v", groups, err) } detail, err := svc.GroupDetail(ctx, "3") if err != nil || detail == nil || len(detail.Members) != 1 || detail.Members[0].Role != "owner" { t.Fatalf("detail %+v %v", detail, err) } if _, err := svc.GroupDetail(ctx, "x"); !errors.Is(err, errs.ErrNotFound) { t.Fatalf("bad gid %v", err) } if err := svc.DissolveGroup(ctx, "3"); !errors.Is(err, ErrNotReady) { t.Fatalf("dissolve %v", err) } stats, err := svc.ImStats(ctx) if err != nil || stats.MessageCount != 2 { t.Fatalf("stats %+v %v", stats, err) } msgs, err := svc.ListMessages(ctx, "hi", 0) if err != nil || len(msgs) != 1 { t.Fatalf("msgs %+v %v", msgs, err) } convs, gviews, st, err := svc.ListConversations(ctx, 11) if err != nil || st != nil || len(convs) != 2 || len(gviews) != 1 { t.Fatalf("convs %+v %+v %v", convs, gviews, err) } _, _, st, err = svc.ListConversations(ctx, 0) if err != nil || st == nil { t.Fatalf("global stats %v", err) } cs, err := svc.ListComplaints(ctx) if err != nil || len(cs) != 1 || cs[0].Target != "12" { t.Fatalf("complaints %+v %v", cs, err) } added, err := svc.AddComplaint(ctx, "a@spark.com", "b@spark.com", "spam", "ads") if err != nil || added == nil || added.Reason != "spam" { t.Fatalf("add %+v %v", added, err) } if _, err := svc.AddComplaint(ctx, "", "12", "other", ""); !errors.Is(err, errs.ErrInvalidArgument) { t.Fatalf("bad complaint %v", err) } }