235 lines
7.2 KiB
Go
235 lines
7.2 KiB
Go
package logic
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"spark-server/internal/infrastructure/id"
|
|
friendlogic "spark-server/internal/module/friend/logic"
|
|
friendmodel "spark-server/internal/module/friend/model"
|
|
"spark-server/internal/module/moment/entity"
|
|
momentmodel "spark-server/internal/module/moment/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"
|
|
"spark-server/internal/pkg/reqctx"
|
|
)
|
|
|
|
type memObj struct {
|
|
deleted []string
|
|
}
|
|
|
|
func (m *memObj) Delete(_ context.Context, key string) error {
|
|
m.deleted = append(m.deleted, key)
|
|
return nil
|
|
}
|
|
|
|
func testMoment(t *testing.T) (*Service, *friendlogic.Service, *userlogic.Service) {
|
|
t.Helper()
|
|
ids, err := id.New(1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
users := logictest.Users(ids, nil)
|
|
friends := friendlogic.NewService(friendmodel.NewMemoryRepo(), users, ids, nil)
|
|
svc := NewService(momentmodel.NewMemoryRepo(), friends, users, &memObj{}, ids)
|
|
return svc, friends, users
|
|
}
|
|
|
|
func twoAccounts(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 becomeFriends(t *testing.T, friends *friendlogic.Service, a, b *userentity.User) {
|
|
t.Helper()
|
|
ctx := t.Context()
|
|
applied, err := friends.Apply(ctx, a.ID, &friendlogic.ApplyInput{Email: "test02@spark.com"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := friends.Accept(ctx, b.ID, friendlogic.AcceptInput{RequestID: applied.RequestID}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func ctxUser(t *testing.T, uid int64, ip string) context.Context {
|
|
t.Helper()
|
|
return reqctx.With(t.Context(), &reqctx.RequestContext{UserID: uid, Language: "zh-CN", RealIP: ip})
|
|
}
|
|
|
|
func TestPublishFeedPrivacyAndDelete(t *testing.T) {
|
|
svc, friends, users := testMoment(t)
|
|
a, b := twoAccounts(t, users)
|
|
becomeFriends(t, friends, a, b)
|
|
ctxA := ctxUser(t, a.ID, "10.0.0.1")
|
|
ctxB := ctxUser(t, b.ID, "10.0.0.2")
|
|
pub, err := svc.Publish(ctxA, a.ID, &PublishInput{Text: "visible"})
|
|
if err != nil || pub.Text != "visible" || pub.Kind != entity.KindText {
|
|
t.Fatalf("%+v %v", pub, err)
|
|
}
|
|
feed, err := svc.Feed(ctxB, b.ID, 0, 20)
|
|
if err != nil || len(feed.Items) != 1 || feed.Items[0].ID != pub.ID {
|
|
t.Fatalf("feed %+v %v", feed, err)
|
|
}
|
|
priv, err := svc.Publish(ctxA, a.ID, &PublishInput{Text: "secret", Visibility: entity.VisibilityPrivate})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
feed, err = svc.Feed(ctxB, b.ID, 0, 20)
|
|
if err != nil || len(feed.Items) != 1 {
|
|
t.Fatalf("private leaked %+v %v", feed, err)
|
|
}
|
|
if _, err := svc.Get(ctxB, b.ID, priv.ID); !errors.Is(err, errs.ErrNotFound) {
|
|
t.Fatalf("private get %v", err)
|
|
}
|
|
album, err := svc.Album(ctxB, b.ID, a.ID, 0, 20)
|
|
if err != nil || len(album.Items) != 1 {
|
|
t.Fatalf("album %+v %v", album, err)
|
|
}
|
|
if err := svc.Delete(ctxA, a.ID, pub.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
feed, err = svc.Feed(ctxB, b.ID, 0, 20)
|
|
if err != nil || len(feed.Items) != 0 {
|
|
t.Fatalf("deleted %+v %v", feed, err)
|
|
}
|
|
}
|
|
|
|
func TestLikeCommentNoticeAndHide(t *testing.T) {
|
|
svc, friends, users := testMoment(t)
|
|
a, b := twoAccounts(t, users)
|
|
becomeFriends(t, friends, a, b)
|
|
ctxA := ctxUser(t, a.ID, "10.0.0.1")
|
|
ctxB := ctxUser(t, b.ID, "10.0.0.2")
|
|
post, err := svc.Publish(ctxA, a.ID, &PublishInput{Text: "hi"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
liked, err := svc.Like(ctxB, b.ID, post.ID)
|
|
if err != nil || !liked.Liked || liked.LikeCount != 1 {
|
|
t.Fatalf("like %+v %v", liked, err)
|
|
}
|
|
notices, err := svc.ListNotices(ctxA, a.ID, 0, 20)
|
|
if err != nil || len(notices.Items) != 1 || notices.Items[0].Kind != entity.NoticeLike {
|
|
t.Fatalf("notice %+v %v", notices, err)
|
|
}
|
|
commented, err := svc.Comment(ctxB, b.ID, post.ID, &CommentInput{Text: "nice"})
|
|
if err != nil || commented.CommentCount != 1 {
|
|
t.Fatalf("comment %+v %v", commented, err)
|
|
}
|
|
unread, err := svc.UnreadFeed(ctxB, b.ID)
|
|
if err != nil || !unread.HasNew || unread.LatestID != post.ID {
|
|
t.Fatalf("unread %+v %v", unread, err)
|
|
}
|
|
if err := svc.MarkFeedRead(ctxB, b.ID, post.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
unread, err = svc.UnreadFeed(ctxB, b.ID)
|
|
if err != nil || unread.HasNew {
|
|
t.Fatalf("after read %+v %v", unread, err)
|
|
}
|
|
if _, err := svc.PutHide(ctxB, b.ID, a.ID, &HideInput{HideHim: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
feed, err := svc.Feed(ctxB, b.ID, 0, 20)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, item := range feed.Items {
|
|
if item.ID == post.ID {
|
|
t.Fatal("hide him failed")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestUnlikeCommentSettingsAndPurge(t *testing.T) {
|
|
svc, friends, users := testMoment(t)
|
|
a, b := twoAccounts(t, users)
|
|
becomeFriends(t, friends, a, b)
|
|
ctxA := ctxUser(t, a.ID, "10.0.0.1")
|
|
ctxB := ctxUser(t, b.ID, "10.0.0.2")
|
|
post, err := svc.Publish(ctxA, a.ID, &PublishInput{Text: "hi"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := svc.Like(ctxB, b.ID, post.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
unliked, err := svc.Unlike(ctxB, b.ID, post.ID)
|
|
if err != nil || unliked.Liked || unliked.LikeCount != 0 {
|
|
t.Fatalf("unlike %+v %v", unliked, err)
|
|
}
|
|
commented, err := svc.Comment(ctxB, b.ID, post.ID, &CommentInput{Text: "nice"})
|
|
if err != nil || commented.CommentCount != 1 {
|
|
t.Fatalf("comment %+v %v", commented, err)
|
|
}
|
|
cid := commented.Comments[0].ID
|
|
if _, err := svc.DeleteComment(ctxA, a.ID, post.ID, cid); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := svc.Get(ctxA, a.ID, post.ID)
|
|
if err != nil || got.CommentCount != 0 {
|
|
t.Fatalf("deleted comment %+v %v", got, err)
|
|
}
|
|
days := entity.Visible30Days
|
|
st, err := svc.PatchSettings(ctxA, a.ID, &PatchSettingsInput{VisibleDays: &days})
|
|
if err != nil || st.VisibleDays != entity.Visible30Days {
|
|
t.Fatalf("settings %+v %v", st, err)
|
|
}
|
|
if _, err := svc.PutHide(ctxB, b.ID, a.ID, &HideInput{HideHim: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := svc.DeleteHide(ctxB, b.ID, a.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := svc.ReadNotices(ctxA, a.ID, &NoticeReadInput{Cursor: 0}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := svc.PurgeUser(ctxA, a.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := svc.Get(ctxA, a.ID, post.ID); !errors.Is(err, errs.ErrNotFound) {
|
|
t.Fatalf("purged get %v", err)
|
|
}
|
|
}
|
|
|
|
func TestMomentImageKeyRejectedIfChatPrefix(t *testing.T) {
|
|
svc, _, users := testMoment(t)
|
|
a, _ := twoAccounts(t, users)
|
|
ctx := ctxUser(t, a.ID, "10.0.0.1")
|
|
_, err := svc.Publish(ctx, a.ID, &PublishInput{
|
|
Images: []*entity.Image{{Key: "chat/image/20260918/xxxx.jpg"}},
|
|
})
|
|
if err == nil {
|
|
t.Fatal("accepted chat key")
|
|
}
|
|
}
|
|
|
|
func TestContentRoundTrip(t *testing.T) {
|
|
raw, err := (&entity.Content{Images: []*entity.Image{{Key: "perm/moment/image/20260918/abcdefghijklmnop.jpg"}}}).Marshal()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := entity.UnmarshalContent(raw)
|
|
if err != nil || got.Kind() != entity.KindImage {
|
|
t.Fatalf("%s %v %v", raw, got, err)
|
|
}
|
|
if !json.Valid(raw) || strings.Contains(string(raw), "uid") {
|
|
t.Fatalf("json %s", raw)
|
|
}
|
|
}
|