Files
glchat/internal/server/guild_templates_test.go
T

193 lines
6.5 KiB
Go
Raw Normal View History

package server
import (
"net/http"
"testing"
)
// Тесты шаблонов серверов (AGENT.md 7.3): встроенный шаблон создаёт структуру
// комнат и ролей без сообщений, неизвестный шаблон отклоняется.
func TestGuildTemplatesCatalog(t *testing.T) {
srv, _ := newTestServer(t)
user := registerAndLogin(t, srv, "tpl_user", "tpl@example.com")
rec := doJSON(t, srv, http.MethodGet, "/api/v1/guild-templates", "", user)
if rec.Code != http.StatusOK {
t.Fatalf("каталог шаблонов = %d (%s)", rec.Code, rec.Body.String())
}
payload := decodeResponse[struct {
Templates []struct {
ID string `json:"id"`
Name string `json:"name"`
Roles []string `json:"roles"`
TextCount int `json:"text_channels"`
VoiceCount int `json:"voice_channels"`
Categories []string `json:"categories"`
} `json:"templates"`
}](t, rec)
if len(payload.Templates) != 3 {
t.Fatalf("шаблонов %d, ожидалось 3", len(payload.Templates))
}
found := map[string]bool{}
for _, template := range payload.Templates {
found[template.ID] = true
if len(template.Roles) < 2 {
t.Fatalf("шаблон %s без ролей: %+v", template.ID, template.Roles)
}
}
for _, id := range []string{"empty", "community", "gaming"} {
if !found[id] {
t.Fatalf("шаблон %s отсутствует в каталоге", id)
}
}
}
func TestGuildFromTemplateCreatesStructure(t *testing.T) {
cases := []struct {
template string
wantRoles []string
wantText []string
wantVoice []string
wantCategory []string
}{
{
template: "empty",
wantRoles: []string{"Администратор", "Пользователь"},
wantText: []string{"общий"},
},
{
template: "community",
wantRoles: []string{"Администратор", "Пользователь", "Модератор"},
wantText: []string{"правила", "объявления", "общий", "флудилка"},
wantVoice: []string{"Голосовой"},
wantCategory: []string{"Информация", "Общение"},
},
{
template: "gaming",
wantRoles: []string{"Администратор", "Пользователь", "Игрок"},
wantText: []string{"общий", "поиск-игр"},
wantVoice: []string{"Игровая 1", "Игровая 2"},
wantCategory: []string{"Текстовые", "Голосовые"},
},
}
for _, item := range cases {
t.Run(item.template, func(t *testing.T) {
srv, _ := newTestServer(t)
owner := registerAndLogin(t, srv, "tpl_"+item.template, "tpl-"+item.template+"@example.com")
created := doJSON(t, srv, http.MethodPost, "/api/v1/guilds",
`{"name":"Шаблон","template_id":"`+item.template+`"}`, owner)
if created.Code != http.StatusOK {
t.Fatalf("создание = %d (%s)", created.Code, created.Body.String())
}
guildID := decodeResponse[struct {
Guild struct {
ID string `json:"id"`
} `json:"guild"`
}](t, created).Guild.ID
roles := doJSON(t, srv, http.MethodGet, "/api/v1/guilds/"+guildID+"/roles", "", owner)
roleNames := make([]string, 0, 4)
for _, role := range decodeResponse[struct {
Roles []struct {
Name string `json:"name"`
} `json:"roles"`
}](t, roles).Roles {
roleNames = append(roleNames, role.Name)
}
for _, want := range item.wantRoles {
if !contains(roleNames, want) {
t.Fatalf("роль %q не создана: %v", want, roleNames)
}
}
channels := doJSON(t, srv, http.MethodGet, "/api/v1/guilds/"+guildID+"/channels", "", owner)
list := decodeResponse[struct {
Channels []struct {
Name string `json:"name"`
Type string `json:"type"`
ParentID string `json:"parent_id"`
} `json:"channels"`
}](t, channels)
byType := map[string][]string{}
for _, channel := range list.Channels {
byType[channel.Type] = append(byType[channel.Type], channel.Name)
// Комнаты шаблона лежат внутри своих категорий.
if len(item.wantCategory) > 0 && channel.Type != "category" && channel.ParentID == "" {
t.Fatalf("комната %q не привязана к категории", channel.Name)
}
}
for _, want := range item.wantText {
if !contains(byType["text"], want) {
t.Fatalf("текстовая комната %q не создана: %v", want, byType["text"])
}
}
for _, want := range item.wantVoice {
if !contains(byType["voice"], want) {
t.Fatalf("голосовая комната %q не создана: %v", want, byType["voice"])
}
}
for _, want := range item.wantCategory {
if !contains(byType["category"], want) {
t.Fatalf("категория %q не создана: %v", want, byType["category"])
}
}
})
}
}
func TestGuildWithUnknownTemplateRejected(t *testing.T) {
srv, _ := newTestServer(t)
owner := registerAndLogin(t, srv, "tpl_bad", "tpl-bad@example.com")
rec := doJSON(t, srv, http.MethodPost, "/api/v1/guilds",
`{"name":"Странный","template_id":"нет-такого"}`, owner)
if rec.Code != http.StatusUnprocessableEntity {
t.Fatalf("неизвестный шаблон = %d (%s)", rec.Code, rec.Body.String())
}
}
// contains проверяет наличие строки в списке.
func contains(list []string, value string) bool {
for _, item := range list {
if item == value {
return true
}
}
return false
}
func TestGuildFromTemplateCreatesNoDuplicates(t *testing.T) {
srv, _ := newTestServer(t)
owner := registerAndLogin(t, srv, "tpl_dup", "tpl-dup@example.com")
created := doJSON(t, srv, http.MethodPost, "/api/v1/guilds",
`{"name":"Дубли","template_id":"community"}`, owner)
guildID := decodeResponse[struct {
Guild struct {
ID string `json:"id"`
} `json:"guild"`
}](t, created).Guild.ID
channels := doJSON(t, srv, http.MethodGet, "/api/v1/guilds/"+guildID+"/channels", "", owner)
list := decodeResponse[struct {
Channels []struct {
Name string `json:"name"`
Type string `json:"type"`
} `json:"channels"`
}](t, channels)
seen := map[string]int{}
for _, channel := range list.Channels {
seen[channel.Type+":"+channel.Name]++
}
for key, count := range seen {
if count > 1 {
t.Fatalf("комната %s создана %d раза", key, count)
}
}
// «Сообщество»: 2 категории, 4 текстовые и 1 голосовая.
if len(list.Channels) != 7 {
t.Fatalf("комнат %d, ожидалось 7", len(list.Channels))
}
}