feat(social): add friends, private chats, inbox and music sharing
ci / module boundaries / boundaries (push) Successful in 7s
ci / api / api (push) Successful in 1m9s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m8s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m23s
ci / db / db (push) Successful in 2m29s
ci / ts / ts (packages + web) (push) Successful in 2m55s
ci / module boundaries / boundaries (push) Successful in 7s
ci / api / api (push) Successful in 1m9s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m8s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m23s
ci / db / db (push) Successful in 2m29s
ci / ts / ts (packages + web) (push) Successful in 2m55s
This commit is contained in:
1 parent
f1528de948
commit
0a05bfe2cd
43 files changed
+2828
-389
No files matched your search
@@ -0,0 +1,143 @@
|
||||
//go:build integration
|
||||
|
||||
package social
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
"os"
|
||||
"pipu/api/internal/api/gen"
|
||||
"pipu/api/internal/platform/db"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSocialIsolationAndLifecycle(t *testing.T) {
|
||||
url := os.Getenv("TEST_DATABASE_URL")
|
||||
if url == "" {
|
||||
t.Skip("TEST_DATABASE_URL is not set")
|
||||
}
|
||||
ctx := context.Background()
|
||||
pool, e := db.Connect(ctx, url)
|
||||
require.NoError(t, e)
|
||||
defer pool.Close()
|
||||
require.NoError(t, db.Migrate(ctx, pool))
|
||||
s := &Store{pool: pool}
|
||||
a, b, c := uuid.New(), uuid.New(), uuid.New()
|
||||
for _, id := range []uuid.UUID{a, b, c} {
|
||||
_, e = pool.Exec(ctx, `INSERT INTO users(id,username,password_hash) VALUES($1,$2,'x')`, id, id.String())
|
||||
require.NoError(t, e)
|
||||
}
|
||||
defer pool.Exec(ctx, `DELETE FROM users WHERE id=ANY($1)`, []uuid.UUID{a, b, c})
|
||||
action := func(user, peer uuid.UUID, op string) error {
|
||||
return s.action(ctx, user, gen.SocialAction{Action: gen.SocialActionAction(op), TargetId: peer})
|
||||
}
|
||||
require.NoError(t, action(a, b, "request"))
|
||||
require.NoError(t, action(a, b, "request"))
|
||||
parallel(t, func() error { return action(a, b, "request") })
|
||||
inbox, e := s.inbox(ctx, b)
|
||||
require.NoError(t, e)
|
||||
require.Len(t, inbox.Notifications, 1)
|
||||
require.Equal(t, 1, inbox.Unread)
|
||||
require.EqualValues(t, "incoming", inbox.People[0].Relationship)
|
||||
require.Error(t, action(a, b, "accept"))
|
||||
require.Error(t, action(c, b, "accept"))
|
||||
_, e = s.send(ctx, a, b, gen.SendDirectMessage{Id: uuid.New(), Text: "before acceptance", AttachmentKind: "none"})
|
||||
require.Error(t, e)
|
||||
require.NoError(t, action(b, a, "accept"))
|
||||
require.NoError(t, action(b, a, "accept"))
|
||||
inbox, e = s.inbox(ctx, a)
|
||||
require.NoError(t, e)
|
||||
require.Len(t, inbox.Notifications, 1)
|
||||
require.EqualValues(t, "friend", inbox.People[0].Relationship)
|
||||
body := gen.SendDirectMessage{Id: uuid.New(), Text: "Привет", AttachmentKind: "none"}
|
||||
m, e := s.send(ctx, a, b, body)
|
||||
require.NoError(t, e)
|
||||
again, e := s.send(ctx, a, b, body)
|
||||
require.NoError(t, e)
|
||||
require.Equal(t, m.Seq, again.Seq)
|
||||
parallel(t, func() error { _, err := s.send(ctx, a, b, body); return err })
|
||||
body.Text = "changed"
|
||||
_, e = s.send(ctx, a, b, body)
|
||||
require.Error(t, e)
|
||||
_, e = s.messages(ctx, c, b, nil)
|
||||
require.Error(t, e)
|
||||
inbox, e = s.inbox(ctx, b)
|
||||
require.NoError(t, e)
|
||||
require.Equal(t, 1, inbox.Unread)
|
||||
// Another user cannot acknowledge this user's notification.
|
||||
require.NoError(t, s.action(ctx, c, gen.SocialAction{Action: "read_notification", TargetId: inbox.Notifications[0].Id}))
|
||||
inbox, e = s.inbox(ctx, b)
|
||||
require.NoError(t, e)
|
||||
require.Equal(t, 1, inbox.Unread)
|
||||
second, e := s.send(ctx, a, b, gen.SendDirectMessage{Id: uuid.New(), Text: "Later", AttachmentKind: "none"})
|
||||
require.NoError(t, e)
|
||||
require.NoError(t, s.action(ctx, b, gen.SocialAction{Action: "read_chat", TargetId: a, UpToSeq: &m.Seq}))
|
||||
history, e := s.messages(ctx, a, b, nil)
|
||||
require.NoError(t, e)
|
||||
require.Len(t, history, 2)
|
||||
require.True(t, history[0].Read)
|
||||
require.False(t, history[1].Read)
|
||||
before, e := s.messages(ctx, b, a, &second.Seq)
|
||||
require.NoError(t, e)
|
||||
require.Len(t, before, 1)
|
||||
require.NoError(t, action(b, a, "block"))
|
||||
require.NoError(t, action(a, b, "block"))
|
||||
for _, id := range []uuid.UUID{a, b} {
|
||||
blocked, err := s.inbox(ctx, id)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, blocked.People, 1)
|
||||
require.EqualValues(t, "blocked", blocked.People[0].Relationship)
|
||||
}
|
||||
require.NoError(t, action(a, b, "unblock"))
|
||||
_, e = s.send(ctx, a, b, gen.SendDirectMessage{Id: uuid.New(), Text: "blocked", AttachmentKind: "none"})
|
||||
require.Error(t, e)
|
||||
_, e = s.messages(ctx, b, a, nil)
|
||||
require.Error(t, e)
|
||||
require.Error(t, action(a, b, "request"))
|
||||
found, e := s.people(ctx, a, b.String())
|
||||
require.NoError(t, e)
|
||||
require.Empty(t, found)
|
||||
require.NoError(t, action(b, a, "unblock"))
|
||||
require.Error(t, action(a, b, "accept"))
|
||||
require.NoError(t, action(a, b, "request"))
|
||||
require.NoError(t, action(b, a, "accept"))
|
||||
require.NoError(t, action(a, b, "remove"))
|
||||
_, e = s.messages(ctx, b, a, nil)
|
||||
require.Error(t, e)
|
||||
// An attachment must reference available catalog music or an active playlist share.
|
||||
require.NoError(t, action(a, c, "request"))
|
||||
require.NoError(t, action(c, a, "accept"))
|
||||
missing := uuid.New()
|
||||
_, e = s.send(ctx, a, c, gen.SendDirectMessage{Id: uuid.New(), AttachmentKind: "playlist", AttachmentId: &missing})
|
||||
require.Error(t, e)
|
||||
playlist, share := uuid.New(), uuid.New()
|
||||
_, e = pool.Exec(ctx, `INSERT INTO playlists(id,user_id,name,share_id) VALUES($1,$2,'Music',$3)`, playlist, a, share)
|
||||
require.NoError(t, e)
|
||||
attachment, e := s.send(ctx, a, c, gen.SendDirectMessage{Id: uuid.New(), AttachmentKind: "playlist", AttachmentId: &share})
|
||||
require.NoError(t, e)
|
||||
require.Equal(t, &share, attachment.AttachmentId)
|
||||
_, e = pool.Exec(ctx, `UPDATE playlists SET share_id=NULL WHERE id=$1`, playlist)
|
||||
require.NoError(t, e)
|
||||
_, e = s.send(ctx, a, c, gen.SendDirectMessage{Id: uuid.New(), AttachmentKind: "playlist", AttachmentId: &share})
|
||||
require.Error(t, e)
|
||||
found, e = s.people(ctx, a, "%_")
|
||||
require.NoError(t, e)
|
||||
require.Empty(t, found)
|
||||
}
|
||||
|
||||
func parallel(t *testing.T, operation func() error) {
|
||||
t.Helper()
|
||||
var wg sync.WaitGroup
|
||||
errors := make(chan error, 8)
|
||||
for range 8 {
|
||||
wg.Add(1)
|
||||
go func() { defer wg.Done(); errors <- operation() }()
|
||||
}
|
||||
wg.Wait()
|
||||
close(errors)
|
||||
for err := range errors {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user