ci / api / api (push) Successful in 19m12s
ci / ts / ts (packages + web) (push) Successful in 2m44s
ci / module boundaries / boundaries (push) Successful in 18m23s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 59s
ci / db / db (push) Failing after 18s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m18s
167 lines
6.5 KiB
Go
167 lines
6.5 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.NotNil(t, inbox.People[0].LastMessage)
|
|
require.Equal(t, m.Id, inbox.People[0].LastMessage.Id)
|
|
require.Equal(t, "Привет", inbox.People[0].LastMessage.Text)
|
|
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, s.updateMessage(ctx, a, b, second.Id, gen.UpdateChatMessageJSONRequestBody{Action: "delete"}))
|
|
inbox, e = s.inbox(ctx, b)
|
|
require.NoError(t, e)
|
|
require.True(t, inbox.People[0].LastMessage.Deleted)
|
|
require.Empty(t, inbox.People[0].LastMessage.Text)
|
|
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.Nil(t, blocked.People[0].LastMessage)
|
|
}
|
|
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 = s.send(ctx, a, c, gen.SendDirectMessage{Id: uuid.New(), Text: "After the music", AttachmentKind: "none"})
|
|
require.NoError(t, e)
|
|
music, e := s.filteredMessages(ctx, c, a, nil, "", true)
|
|
require.NoError(t, e)
|
|
require.Len(t, music, 1)
|
|
require.Equal(t, attachment.Id, music[0].Id)
|
|
require.False(t, music[0].Read, "browsing shared music must not acknowledge messages")
|
|
music, e = s.filteredMessages(ctx, c, a, &attachment.Seq, "", true)
|
|
require.NoError(t, e)
|
|
require.Empty(t, music)
|
|
require.NoError(t, s.updateMessage(ctx, a, c, attachment.Id, gen.UpdateChatMessageJSONRequestBody{Action: "delete"}))
|
|
music, e = s.filteredMessages(ctx, c, a, nil, "", true)
|
|
require.NoError(t, e)
|
|
require.Empty(t, music, "deleted music must not appear in shared attachments")
|
|
_, 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)
|
|
}
|
|
}
|