Files
pipu-music/services/api/internal/social/social_integration_test.go
T
mrleo1nid 1a693848f2
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
Redesign social conversations and shared music
2026-10-23 11:27:47 +03:00

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)
}
}