Files

174 lines
7.2 KiB
Go
Raw Permalink Normal View History

package server
import (
"net/http"
"testing"
)
func TestInviteLifecycle(t *testing.T) {
srv, _ := newTestServer(t)
ownerCookie := registerAndLogin(t, srv, "inv_owner", "inv-owner@example.com")
guestCookie := registerAndLogin(t, srv, "inv_guest", "inv-guest@example.com")
// Первый сервер становится главным (в него может войти кто угодно),
// приглашения проверяем на втором — закрытом.
doJSON(t, srv, http.MethodPost, "/api/v1/guilds", `{"name":"Главный"}`, ownerCookie)
created := doJSON(t, srv, http.MethodPost, "/api/v1/guilds", `{"name":"Закрытый"}`, ownerCookie)
guild := decodeResponse[struct {
Guild struct {
ID string `json:"id"`
} `json:"guild"`
}](t, created)
// Посторонний не видит закрытый сервер.
hidden := doJSON(t, srv, http.MethodGet, "/api/v1/guilds/"+guild.Guild.ID, "", guestCookie)
if hidden.Code != http.StatusNotFound {
t.Fatalf("guest guild access = %d, want 404", hidden.Code)
}
// Приглашение с лимитом в одно использование.
inviteRec := doJSON(t, srv, http.MethodPost, "/api/v1/guilds/"+guild.Guild.ID+"/invites",
`{"max_uses":1,"max_age_seconds":3600}`, ownerCookie)
if inviteRec.Code != http.StatusOK {
t.Fatalf("create invite = %d, body = %s", inviteRec.Code, inviteRec.Body.String())
}
invite := decodeResponse[struct {
Invite struct {
Code string `json:"code"`
URL string `json:"url"`
} `json:"invite"`
Guild struct {
Name string `json:"name"`
MemberCount int `json:"member_count"`
IsMember bool `json:"is_member"`
} `json:"guild"`
}](t, inviteRec)
if len(invite.Invite.Code) != 10 {
t.Fatalf("invite code = %q, want 10 символов", invite.Invite.Code)
}
// В ответе на создание IsMember отражает самого создателя (он в сервере).
if invite.Guild.Name != "Закрытый" || !invite.Guild.IsMember {
t.Fatalf("unexpected preview: %+v", invite.Guild)
}
// Предпросмотр доступен гостю и показывает сервер.
preview := doJSON(t, srv, http.MethodGet, "/api/v1/invites/"+invite.Invite.Code, "", guestCookie)
if preview.Code != http.StatusOK {
t.Fatalf("preview = %d, body = %s", preview.Code, preview.Body.String())
}
previewPayload := decodeResponse[struct {
Guild struct {
Name string `json:"name"`
MemberCount int `json:"member_count"`
IsMember bool `json:"is_member"`
} `json:"guild"`
}](t, preview)
if previewPayload.Guild.Name != "Закрытый" || previewPayload.Guild.IsMember || previewPayload.Guild.MemberCount != 1 {
t.Fatalf("guest preview = %+v", previewPayload.Guild)
}
// Принятие: гость становится участником.
accept := doJSON(t, srv, http.MethodPost, "/api/v1/invites/"+invite.Invite.Code, "", guestCookie)
if accept.Code != http.StatusOK {
t.Fatalf("accept invite = %d, body = %s", accept.Code, accept.Body.String())
}
member := doJSON(t, srv, http.MethodGet, "/api/v1/guilds/"+guild.Guild.ID, "", guestCookie)
if member.Code != http.StatusOK {
t.Fatalf("guild after invite = %d, body = %s", member.Code, member.Body.String())
}
// Повторное принятие тем же пользователем не расходует использования.
again := doJSON(t, srv, http.MethodPost, "/api/v1/invites/"+invite.Invite.Code, "", guestCookie)
if again.Code != http.StatusOK {
t.Fatalf("repeat accept = %d, body = %s", again.Code, again.Body.String())
}
// Третьего участника приглашение уже не пускает: лимит исчерпан.
thirdCookie := registerAndLogin(t, srv, "inv_third", "inv-third@example.com")
exhausted := doJSON(t, srv, http.MethodPost, "/api/v1/invites/"+invite.Invite.Code, "", thirdCookie)
if exhausted.Code != http.StatusGone {
t.Fatalf("exhausted invite = %d, want 410", exhausted.Code)
}
// Отзыв приглашения владельцем и проверка списка.
list := doJSON(t, srv, http.MethodGet, "/api/v1/guilds/"+guild.Guild.ID+"/invites", "", ownerCookie)
invites := decodeResponse[struct {
Invites []struct {
Code string `json:"code"`
Uses int `json:"uses"`
} `json:"invites"`
}](t, list)
if len(invites.Invites) != 1 || invites.Invites[0].Uses != 1 {
t.Fatalf("invites = %+v", invites.Invites)
}
revoke := doJSON(t, srv, http.MethodDelete, "/api/v1/invites/"+invite.Invite.Code, "", ownerCookie)
if revoke.Code != http.StatusOK {
t.Fatalf("revoke = %d, body = %s", revoke.Code, revoke.Body.String())
}
afterRevoke := doJSON(t, srv, http.MethodGet, "/api/v1/invites/"+invite.Invite.Code, "", guestCookie)
if afterRevoke.Code != http.StatusNotFound {
t.Fatalf("revoked invite = %d, want 404", afterRevoke.Code)
}
// Обычный участник без CREATE_INVITES приглашение создать не может.
memberInvite := doJSON(t, srv, http.MethodPost, "/api/v1/guilds/"+guild.Guild.ID+"/invites", `{}`, guestCookie)
if memberInvite.Code != http.StatusForbidden {
t.Fatalf("member invite = %d, want 403", memberInvite.Code)
}
}
func TestPublicGuildsCatalog(t *testing.T) {
srv, _ := newTestServer(t)
ownerCookie := registerAndLogin(t, srv, "pub_owner", "pub-owner@example.com")
guestCookie := registerAndLogin(t, srv, "pub_guest", "pub-guest@example.com")
doJSON(t, srv, http.MethodPost, "/api/v1/guilds", `{"name":"Главный"}`, ownerCookie)
created := doJSON(t, srv, http.MethodPost, "/api/v1/guilds", `{"name":"Публичный"}`, ownerCookie)
guild := decodeResponse[struct {
Guild struct {
ID string `json:"id"`
} `json:"guild"`
}](t, created)
// Пока сервер закрыт, в каталоге его нет.
closed := doJSON(t, srv, http.MethodGet, "/api/v1/guilds/public", "", guestCookie)
closedList := decodeResponse[struct {
Guilds []struct {
Name string `json:"name"`
} `json:"guilds"`
}](t, closed)
for _, item := range closedList.Guilds {
if item.Name == "Публичный" {
t.Fatal("closed guild must not be in the public catalog")
}
}
// Делаем сервер публичным: появляется в каталоге и открыт для входа.
if rec := doJSON(t, srv, http.MethodPatch, "/api/v1/guilds/"+guild.Guild.ID,
`{"public":true}`, ownerCookie); rec.Code != http.StatusOK {
t.Fatalf("publish guild = %d, body = %s", rec.Code, rec.Body.String())
}
open := doJSON(t, srv, http.MethodGet, "/api/v1/guilds/public", "", guestCookie)
openList := decodeResponse[struct {
Guilds []struct {
ID string `json:"id"`
Name string `json:"name"`
MemberCount int `json:"member_count"`
} `json:"guilds"`
}](t, open)
found := false
for _, item := range openList.Guilds {
if item.ID == guild.Guild.ID {
found = true
}
}
if !found {
t.Fatalf("public guild is missing from the catalog: %+v", openList.Guilds)
}
join := doJSON(t, srv, http.MethodPost, "/api/v1/guilds/"+guild.Guild.ID+"/join", "", guestCookie)
if join.Code != http.StatusOK {
t.Fatalf("join public guild = %d, body = %s", join.Code, join.Body.String())
}
}