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
144 lines
5.3 KiB
Go
144 lines
5.3 KiB
Go
//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)
|
|
}
|
|
}
|