287 lines
8.9 KiB
Go
287 lines
8.9 KiB
Go
package logic
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"spark-server/internal/infrastructure/id"
|
|
convlogic "spark-server/internal/module/conversation/logic"
|
|
convmodel "spark-server/internal/module/conversation/model"
|
|
delivermodel "spark-server/internal/module/deliver/model"
|
|
"spark-server/internal/module/friend/entity"
|
|
friendmodel "spark-server/internal/module/friend/model"
|
|
msgentity "spark-server/internal/module/message/entity"
|
|
msgmodel "spark-server/internal/module/message/model"
|
|
userentity "spark-server/internal/module/user/entity"
|
|
userlogic "spark-server/internal/module/user/logic"
|
|
"spark-server/internal/module/user/logictest"
|
|
"spark-server/internal/pkg/errs"
|
|
)
|
|
|
|
type captureGreeter struct {
|
|
n int
|
|
a, b int64
|
|
}
|
|
|
|
func (c *captureGreeter) BecameFriends(_ context.Context, a, b int64) error {
|
|
c.n++
|
|
c.a, c.b = a, b
|
|
return nil
|
|
}
|
|
|
|
func testFriends(t *testing.T) (*Service, *userlogic.Service, *captureGreeter) {
|
|
t.Helper()
|
|
ids, err := id.New(1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
users := logictest.Users(ids, nil)
|
|
g := &captureGreeter{}
|
|
return NewService(friendmodel.NewMemoryRepo(), users, ids, g), users, g
|
|
}
|
|
|
|
func twoUsers(t *testing.T, users *userlogic.Service) (*userentity.User, *userentity.User) {
|
|
t.Helper()
|
|
ctx := t.Context()
|
|
a, err := users.AuthenticateEmail(ctx, "test01@spark.com", "1", &userentity.LoginDevice{DeviceID: 1, DeviceType: userentity.DeviceIOS})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b, err := users.AuthenticateEmail(ctx, "test02@spark.com", "1", &userentity.LoginDevice{DeviceID: 2, DeviceType: userentity.DeviceWeb})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return a, b
|
|
}
|
|
|
|
func TestApplyByEmailSparkIDQRAndDedupe(t *testing.T) {
|
|
svc, users, _ := testFriends(t)
|
|
ctx := t.Context()
|
|
a, b := twoUsers(t, users)
|
|
first, err := svc.Apply(ctx, a.ID, &ApplyInput{Email: "test02@spark.com", Reason: "hi", Remark: "work"})
|
|
if err != nil || first.Status != entity.RequestPending || first.RequestID == 0 {
|
|
t.Fatalf("email apply %+v %v", first, err)
|
|
}
|
|
second, err := svc.Apply(ctx, a.ID, &ApplyInput{SparkID: b.SparkID, Reason: "again"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if second.RequestID != first.RequestID {
|
|
t.Fatalf("dedupe %d %d", first.RequestID, second.RequestID)
|
|
}
|
|
third, err := svc.Apply(ctx, a.ID, &ApplyInput{QRCode: b.QRCode, Reason: "qr"})
|
|
if err != nil || third.RequestID != first.RequestID {
|
|
t.Fatalf("qr %+v %v", third, err)
|
|
}
|
|
list, err := svc.ListRequests(ctx, b.ID, 20)
|
|
if err != nil || len(list.Items) != 1 || list.Items[0].Reason != "qr" || list.Items[0].From.UID != a.ID {
|
|
t.Fatalf("inbox %+v %v", list, err)
|
|
}
|
|
}
|
|
|
|
func TestAcceptBecomesFriendsAndGreeting(t *testing.T) {
|
|
svc, users, g := testFriends(t)
|
|
ctx := t.Context()
|
|
a, b := twoUsers(t, users)
|
|
applied, err := svc.Apply(ctx, a.ID, &ApplyInput{Email: "test02@spark.com", Remark: "from-a"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := svc.Accept(ctx, b.ID, AcceptInput{RequestID: applied.RequestID, Remark: "from-b"})
|
|
if err != nil || got.UID != a.ID {
|
|
t.Fatalf("accept %+v %v", got, err)
|
|
}
|
|
if g.n != 1 {
|
|
t.Fatalf("greeting %d", g.n)
|
|
}
|
|
if err := svc.CanSend(ctx, a.ID, b.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
view, err := svc.Get(ctx, a.ID, b.ID)
|
|
if err != nil || view.Remark != "from-a" || view.Relation != entity.RelationFriend {
|
|
t.Fatalf("a view %+v %v", view, err)
|
|
}
|
|
view, err = svc.Get(ctx, b.ID, a.ID)
|
|
if err != nil || view.Remark != "from-b" {
|
|
t.Fatalf("b view %+v %v", view, err)
|
|
}
|
|
if _, err := svc.Get(ctx, a.ID, a.ID); !errors.Is(err, errs.ErrNotFound) {
|
|
t.Fatalf("self %v", err)
|
|
}
|
|
}
|
|
|
|
func TestMutualApplyAutoAccept(t *testing.T) {
|
|
svc, users, g := testFriends(t)
|
|
ctx := t.Context()
|
|
a, b := twoUsers(t, users)
|
|
if _, err := svc.Apply(ctx, a.ID, &ApplyInput{UID: b.ID, Remark: "a-note"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
out, err := svc.Apply(ctx, b.ID, &ApplyInput{Email: "test01@spark.com", Remark: "b-note"})
|
|
if err != nil || out.Status != entity.RequestAccepted {
|
|
t.Fatalf("%+v %v", out, err)
|
|
}
|
|
if g.n != 1 {
|
|
t.Fatalf("greeting %d", g.n)
|
|
}
|
|
if err := svc.CanSend(ctx, b.ID, a.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestNoUnidirectionalDeleteAndBlock(t *testing.T) {
|
|
svc, users, _ := testFriends(t)
|
|
ctx := t.Context()
|
|
a, b := twoUsers(t, users)
|
|
applied, _ := svc.Apply(ctx, a.ID, &ApplyInput{Email: "test02@spark.com"})
|
|
if _, err := svc.Accept(ctx, b.ID, AcceptInput{RequestID: applied.RequestID}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := svc.Delete(ctx, a.ID, b.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := svc.CanSend(ctx, b.ID, a.ID); !errors.Is(err, errs.ErrForbidden) {
|
|
t.Fatalf("after delete %v", err)
|
|
}
|
|
applied, _ = svc.Apply(ctx, a.ID, &ApplyInput{Email: "test02@spark.com"})
|
|
if _, err := svc.Accept(ctx, b.ID, AcceptInput{RequestID: applied.RequestID}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := svc.Block(ctx, a.ID, b.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ok, err := svc.AreFriends(ctx, a.ID, b.ID)
|
|
if err != nil || !ok {
|
|
t.Fatalf("block keeps friendship %v %v", ok, err)
|
|
}
|
|
view, err := svc.Get(ctx, a.ID, b.ID)
|
|
if err != nil || !view.Blocked || view.Relation != entity.RelationBlock {
|
|
t.Fatalf("blocked view %+v %v", view, err)
|
|
}
|
|
if err := svc.CanSend(ctx, a.ID, b.ID); !errors.Is(err, errs.ErrForbidden) {
|
|
t.Fatalf("blocked send %v", err)
|
|
}
|
|
if _, err := svc.Apply(ctx, a.ID, &ApplyInput{Email: "test02@spark.com"}); !errors.Is(err, errs.ErrForbidden) {
|
|
t.Fatalf("apply blocked %v", err)
|
|
}
|
|
if _, err := svc.Apply(ctx, b.ID, &ApplyInput{Email: "test01@spark.com"}); !errors.Is(err, errs.ErrNotFound) {
|
|
t.Fatalf("apply blocked-by %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPinOrdersList(t *testing.T) {
|
|
svc, users, _ := testFriends(t)
|
|
ctx := t.Context()
|
|
a, b := twoUsers(t, users)
|
|
c, err := users.AuthenticateEmail(ctx, "test03@spark.com", "1", &userentity.LoginDevice{DeviceID: 3, DeviceType: userentity.DeviceIOS})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, peer := range []int64{b.ID, c.ID} {
|
|
applied, err := svc.Apply(ctx, a.ID, &ApplyInput{UID: peer})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := svc.Accept(ctx, peer, AcceptInput{RequestID: applied.RequestID}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := svc.Pin(ctx, a.ID, c.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
list, err := svc.List(ctx, a.ID, 20)
|
|
if err != nil || len(list.Items) != 2 || list.Items[0].UID != c.ID || !list.Items[0].Pinned {
|
|
t.Fatalf("pinned first %+v %v", list, err)
|
|
}
|
|
if err := svc.Unpin(ctx, a.ID, c.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
list, err = svc.List(ctx, a.ID, 20)
|
|
if err != nil || list.Items[0].Pinned {
|
|
t.Fatalf("unpinned %+v %v", list, err)
|
|
}
|
|
}
|
|
|
|
func TestRemarkReportLookup(t *testing.T) {
|
|
svc, users, _ := testFriends(t)
|
|
ctx := t.Context()
|
|
a, b := twoUsers(t, users)
|
|
look, err := svc.Lookup(ctx, a.ID, LookupInput{Q: b.SparkID})
|
|
if err != nil || look.UID != b.ID || look.Relation != entity.RelationNone {
|
|
t.Fatalf("lookup %+v %v", look, err)
|
|
}
|
|
applied, _ := svc.Apply(ctx, a.ID, &ApplyInput{SparkID: b.SparkID})
|
|
if _, err := svc.Accept(ctx, b.ID, AcceptInput{RequestID: applied.RequestID}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := svc.SetRemark(ctx, a.ID, b.ID, RemarkInput{Remark: "boss"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
view, err := svc.Get(ctx, a.ID, b.ID)
|
|
if err != nil || view.Remark != "boss" {
|
|
t.Fatalf("remark %+v %v", view, err)
|
|
}
|
|
if err := svc.Report(ctx, a.ID, b.ID, ReportInput{Reason: entity.ReportSpam, Detail: "ads"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := svc.Apply(ctx, a.ID, &ApplyInput{Email: "test02@spark.com"}); !errors.Is(err, errs.ErrDuplicate) {
|
|
t.Fatalf("already %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLookupQueryClassifies(t *testing.T) {
|
|
svc, users, _ := testFriends(t)
|
|
ctx := t.Context()
|
|
a, b := twoUsers(t, users)
|
|
queries := []string{
|
|
"test02@spark.com",
|
|
strconv.FormatInt(b.ID, 10),
|
|
b.SparkID,
|
|
b.QRCode,
|
|
}
|
|
for _, q := range queries {
|
|
look, err := svc.Lookup(ctx, a.ID, LookupInput{Q: q})
|
|
if err != nil || look.UID != b.ID {
|
|
t.Fatalf("q=%q %+v %v", q, look, err)
|
|
}
|
|
}
|
|
if _, err := svc.Lookup(ctx, a.ID, LookupInput{Q: ""}); !errors.Is(err, errs.ErrInvalidArgument) {
|
|
t.Fatalf("empty %v", err)
|
|
}
|
|
if _, err := svc.Lookup(ctx, a.ID, LookupInput{Q: "nobody_xx"}); !errors.Is(err, errs.ErrNotFound) {
|
|
t.Fatalf("missing %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSystemGreeterWritesMessage(t *testing.T) {
|
|
ids, err := id.New(1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
msgs := msgmodel.NewMemoryRepo()
|
|
chats := convlogic.NewP2PService(convmodel.NewMemoryP2P(), ids)
|
|
g := NewGreeter(chats, msgs, ids, delivermodel.NewMemoryQueue())
|
|
if err := g.BecameFriends(t.Context(), 11, 22); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
convID, err := chats.EnsureP2P(t.Context(), 11, 22)
|
|
if err != nil || convID == 0 {
|
|
t.Fatalf("conv %d %v", convID, err)
|
|
}
|
|
found := false
|
|
for _, msg := range msgs.All() {
|
|
if msg.ConversationID == convID && msg.Type == msgentity.TypeSystem {
|
|
found = true
|
|
payload, err := msg.Payload()
|
|
if err != nil || payload.System == nil || payload.System.Kind != entity.SystemKindBecameFriends {
|
|
t.Fatalf("payload %+v %v", payload, err)
|
|
}
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("missing system message")
|
|
}
|
|
}
|