fix(web): свежий звук сервера виден в настройках и при ошибке списка

`GUILD_SOUNDS_UPDATE` приносит набор звуков целиком, но раздел «Звуки» читает
REST-кэш: клиент инвалидировал запрос вместо того, чтобы положить в кэш сам
payload события. Под нагрузкой повторный `GET /guilds/{id}/sounds` попадает
под лимит частоты (429), а запрос списка идёт с `retry: 0` — список оставался
старым, и новый звук не появлялся в разделе. Так падал пункт 5 матрицы 11.6 в
полной прогонке (build/e2e-all-run8.log, run9): в логах стенда видно
`POST .../sounds 200`, следом `GET .../sounds 200` (первый звук) и
`GET .../sounds 429` (второй) — без единой повторной попытки.

Теперь событие пишет список в кэш запроса, а ошибка перезапроса не прячет уже
полученный список: раздел показывает актуальный набор из снапшота.

Тест: web/tests/soundSettings.test.tsx — звук из `GUILD_SOUNDS_UPDATE`
появляется, когда список по REST отвечает 429.
This commit is contained in:
2026-09-26 15:06:26 +03:00
parent ad7b3d21bb
commit 4175b1436e
3 changed files with 43 additions and 6 deletions
+30
View File
@@ -3,8 +3,11 @@ import { fireEvent, screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import {
apiError,
installFetch,
installGatewaySocket,
json,
makeReadySnapshot,
makeUser,
renderApp,
requestUrl,
@@ -157,6 +160,33 @@ describe('настройки сервера: секция «Звуки»', () =>
expect(within(dialog).queryByTestId('guild-sounds-counter')).toBeNull();
});
it('показывает звук из GUILD_SOUNDS_UPDATE, даже если список по REST не отдался', async () => {
// Под нагрузкой ручка списка отвечает 429 (AGENT.md 8.6), а раздел должен
// обновляться снапшотом из события шлюза, а не повтором запроса (7.13).
const gateway = installGatewaySocket(
makeReadySnapshot([{ id: 'g-1', name: 'Сервер', my_permissions: ['MANAGE_SOUNDS'] }]),
);
installFetch(
routes({
extra: [
{
match: '/api/v1/guilds/g-1/sounds',
response: () => apiError('ratelimit.hit', 429),
},
],
}),
);
renderApp('/settings/servers/g-1');
const dialog = await screen.findByRole('dialog', { name: 'Настройки' });
await gateway.greet();
const fresh: GuildSound = { id: 's-3', name: 'fresh', file_id: '920', kind: 'soundboard' };
gateway.dispatch('GUILD_SOUNDS_UPDATE', { guild_id: 'g-1', sounds: [airhorn, fresh] });
expect(await within(dialog).findByTestId('guild-sound-s-1')).toHaveTextContent('airhorn');
expect(within(dialog).getByTestId('guild-sound-s-3')).toHaveTextContent('fresh');
});
it('загружает звук палитры: kind в URL, событие и имя в multipart', async () => {
const { dialog, fetchMock } = await openSoundsSection();
await userEvent.click(within(dialog).getByTestId('guild-sounds-tab-ui'));