Files
ds-galo/backend/internal/sse/hub_test.go
T
iu e82f45647c 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) и
  применяет события к стору альбомов; страница альбома уходит при потере доступа
2026-10-15 23:25:07 +03:00

48 lines
1.2 KiB
Go

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