feat(realtime): доставлять клиентам события жизненного цикла альбома и изменения состава фото
Публиковать album.created/updated/deleted и изменения состава фото в личный поток SSE, а также photo.created при загрузке, чтобы списки альбомов, счётчики и открытые страницы альбома обновлялись без перезагрузки. - members.Repository получил Audience (владелец + участники) для дешёвого веера - albums.Service рассылает события в личные потоки получателей, перечитывая альбом через GetByID и attachCover, чтобы payload совпадал с GET /api/albums - members.Service публикует при принятии инвайта, смене роли и исключении - photos.Service публикует photo.created при загрузке и album.updated при добавлении/копировании/перемещении/восстановлении/корзине/окончательном удалении - photos_count теперь считает фото вне корзины - web-клиент подписывается на /api/sse/me один раз на уровне layout'а (app) и применяет события к стору альбомов; страница альбома уходит при потере доступа
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package sse
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Publishing to a user with no subscription must be a no-op: it must neither
|
||||
// panic nor register an empty subscription entry.
|
||||
func TestPublishUserWithoutSubscriberIsNoop(t *testing.T) {
|
||||
h := NewHub(4)
|
||||
h.PublishUser("nobody", "album.created", map[string]string{"id": "a1"})
|
||||
|
||||
h.mu.RLock()
|
||||
_, created := h.users["nobody"]
|
||||
h.mu.RUnlock()
|
||||
if created {
|
||||
t.Fatalf("PublishUser created a subscription entry for an absent user")
|
||||
}
|
||||
}
|
||||
|
||||
// The personal and per-album channels are independent: a personal event must
|
||||
// not leak into the album channel.
|
||||
func TestPublishUserDoesNotReachAlbumChannel(t *testing.T) {
|
||||
h := NewHub(4)
|
||||
albumSub := h.SubscribeAlbum("a1")
|
||||
defer albumSub.Close()
|
||||
userSub := h.SubscribeUser("u1")
|
||||
defer userSub.Close()
|
||||
|
||||
h.PublishUser("u1", "album.created", map[string]string{"id": "a1"})
|
||||
|
||||
select {
|
||||
case ev := <-albumSub.Events():
|
||||
t.Fatalf("album channel received personal event %q", ev.Type)
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
}
|
||||
|
||||
select {
|
||||
case ev := <-userSub.Events():
|
||||
if ev.Type != "album.created" {
|
||||
t.Fatalf("user event = %q, want album.created", ev.Type)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("personal subscriber did not receive the event")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user