feat(server): статус комнаты в снапшоте READY
Шапка комнаты показывает статус (описание) рядом с названием, но в снапшоте этого поля не было: клиент получал описание только REST-запросом списка комнат, поэтому статус появлялся с задержкой и не приходил в событиях комнат (AGENT.md 7.5, 11.6). - `ReadyChannel.Description` + заполнение в `channelPayload` — статус приходит и в READY, и в `CHANNEL_CREATE`/`CHANNEL_UPDATE` (те же payload'ы, что у REST); - Go-тест `TestReadyCarriesChannelStatus`: описание и медленный режим доезжают до снапшота. Клиентская часть (парсер и шапка) идёт следующим коммитом.
This commit is contained in:
@@ -132,6 +132,9 @@ type ReadyChannel struct {
|
|||||||
Slowmode int `json:"slowmode_seconds,omitempty"`
|
Slowmode int `json:"slowmode_seconds,omitempty"`
|
||||||
CanSend bool `json:"can_send"`
|
CanSend bool `json:"can_send"`
|
||||||
CanConnect bool `json:"can_connect"`
|
CanConnect bool `json:"can_connect"`
|
||||||
|
// Description — статус комнаты: шапка показывает его рядом с названием
|
||||||
|
// (AGENT.md 7.5, 11.6), поэтому он приходит и в READY, и в событиях комнат.
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
// BackgroundFileID — фон комнаты: приходит и в READY, и в CHANNEL_UPDATE
|
// BackgroundFileID — фон комнаты: приходит и в READY, и в CHANNEL_UPDATE
|
||||||
// (AGENT.md 7.5), иначе после перезагрузки фон терялся бы до REST-запроса.
|
// (AGENT.md 7.5), иначе после перезагрузки фон терялся бы до REST-запроса.
|
||||||
BackgroundFileID string `json:"background_file_id,omitempty"`
|
BackgroundFileID string `json:"background_file_id,omitempty"`
|
||||||
|
|||||||
@@ -274,6 +274,7 @@ func channelPayload(channel store.Channel, canSend, canConnect bool) ReadyChanne
|
|||||||
ID: formatID(channel.ID),
|
ID: formatID(channel.ID),
|
||||||
Type: string(channel.Type),
|
Type: string(channel.Type),
|
||||||
Name: channel.Name,
|
Name: channel.Name,
|
||||||
|
Description: channel.Description,
|
||||||
Position: channel.Position,
|
Position: channel.Position,
|
||||||
UserLimit: channel.UserLimit,
|
UserLimit: channel.UserLimit,
|
||||||
Slowmode: channel.SlowmodeSeconds,
|
Slowmode: channel.SlowmodeSeconds,
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestReadyCarriesChannelStatus проверяет, что снапшот несёт статус комнаты —
|
||||||
|
// описание и медленный режим: шапка показывает их рядом с названием, включая
|
||||||
|
// чужой сервер у инстанс-админа (AGENT.md 7.5, 11.6).
|
||||||
|
func TestReadyCarriesChannelStatus(t *testing.T) {
|
||||||
|
srv, _ := newTestServer(t)
|
||||||
|
httpServer := httptest.NewServer(srv.Handler())
|
||||||
|
t.Cleanup(httpServer.Close)
|
||||||
|
|
||||||
|
ownerCookie := registerAndLogin(t, srv, "status_owner", "status-owner@example.com")
|
||||||
|
created := doJSON(t, srv, http.MethodPost, "/api/v1/guilds", `{"name":"Статусы"}`, ownerCookie)
|
||||||
|
guild := decodeResponse[struct {
|
||||||
|
Guild struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
} `json:"guild"`
|
||||||
|
}](t, created)
|
||||||
|
|
||||||
|
channels := doJSON(t, srv, http.MethodGet, "/api/v1/guilds/"+guild.Guild.ID+"/channels", "", ownerCookie)
|
||||||
|
channel := decodeResponse[struct {
|
||||||
|
Channels []struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
} `json:"channels"`
|
||||||
|
}](t, channels)
|
||||||
|
if len(channel.Channels) == 0 {
|
||||||
|
t.Fatal("у нового сервера нет комнат")
|
||||||
|
}
|
||||||
|
patched := doJSON(t, srv, http.MethodPatch,
|
||||||
|
"/api/v1/guilds/"+guild.Guild.ID+"/channels/"+channel.Channels[0].ID,
|
||||||
|
`{"description":"Статус комнаты","slowmode_seconds":5}`, ownerCookie)
|
||||||
|
if patched.Code != http.StatusOK {
|
||||||
|
t.Fatalf("описание комнаты = %d, body = %s", patched.Code, patched.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
client := dialGateway(t, httpServer, ownerCookie)
|
||||||
|
var snapshot struct {
|
||||||
|
Guilds []struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Channels []struct {
|
||||||
|
Description string `json:"description"`
|
||||||
|
Slowmode int `json:"slowmode_seconds"`
|
||||||
|
} `json:"channels"`
|
||||||
|
} `json:"guilds"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(client.ready.D, &snapshot); err != nil {
|
||||||
|
t.Fatalf("decode READY: %v", err)
|
||||||
|
}
|
||||||
|
described := false
|
||||||
|
for _, item := range snapshot.Guilds {
|
||||||
|
if item.ID != guild.Guild.ID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, ch := range item.Channels {
|
||||||
|
if ch.Description == "Статус комнаты" && ch.Slowmode == 5 {
|
||||||
|
described = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !described {
|
||||||
|
t.Fatalf("READY не несёт статус комнаты: %+v", snapshot.Guilds)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,8 @@ import (
|
|||||||
type realtimeClient struct {
|
type realtimeClient struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
conn *websocket.Conn
|
conn *websocket.Conn
|
||||||
|
// ready — тело кадра READY: тесты снапшота читают из него списки серверов.
|
||||||
|
ready realtimeFrame
|
||||||
}
|
}
|
||||||
|
|
||||||
// dialGateway подключается к /gateway и проходит IDENTIFY пустым токеном:
|
// dialGateway подключается к /gateway и проходит IDENTIFY пустым токеном:
|
||||||
@@ -47,6 +49,7 @@ func dialGateway(t *testing.T, server *httptest.Server, cookie *http.Cookie) *re
|
|||||||
if ready.T != "READY" {
|
if ready.T != "READY" {
|
||||||
t.Fatalf("frame = %q, want READY", ready.T)
|
t.Fatalf("frame = %q, want READY", ready.T)
|
||||||
}
|
}
|
||||||
|
client.ready = ready
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user