feat(voice): журнал обработанных вебхуков LiveKit
This commit is contained in:
+42
-2
@@ -13,6 +13,7 @@ import { useMessagesStore } from '@/stores/messages';
|
||||
import { useSessionStore } from '@/stores/session';
|
||||
import { useUiStore } from '@/stores/ui';
|
||||
import { useVoiceStore } from '@/stores/voice';
|
||||
import { useVoiceConnectionStore } from '@/stores/voiceConnection';
|
||||
import type { User } from '@/api/types';
|
||||
|
||||
/** Ответ-заглушка с JSON-телом (как у настоящего API). */
|
||||
@@ -32,7 +33,8 @@ export interface FetchRoute {
|
||||
/** Подстрока URL, по которой выбирается обработчик. */
|
||||
match: string;
|
||||
method?: string;
|
||||
response: () => Response | Promise<Response>;
|
||||
/** Ответ на запрос: обработчик видит url, метод и разобранное тело. */
|
||||
response: (request: RecordedRequest) => Response | Promise<Response>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,7 +56,7 @@ export function installFetch(routes: FetchRoute[]) {
|
||||
if (!url.includes(route.match)) {
|
||||
continue;
|
||||
}
|
||||
return Promise.resolve(route.response());
|
||||
return Promise.resolve(route.response({ url, method, body: parseRequestBody(init?.body) }));
|
||||
}
|
||||
return Promise.resolve(apiError('not_found', 404));
|
||||
});
|
||||
@@ -62,6 +64,18 @@ export function installFetch(routes: FetchRoute[]) {
|
||||
return fetchMock;
|
||||
}
|
||||
|
||||
/** Тело запроса как JSON: у multipart и пустого тела его нет. */
|
||||
function parseRequestBody(body: BodyInit | null | undefined): unknown {
|
||||
if (typeof body !== 'string') {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(body) as unknown;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/** Мок fetch, который падает на уровне сети (сервер недоступен). */
|
||||
export function installFailingFetch() {
|
||||
const fetchMock = vi.fn(() => Promise.reject(new TypeError('Failed to fetch')));
|
||||
@@ -136,6 +150,8 @@ export function resetStores(): void {
|
||||
useSessionStore.getState().reset();
|
||||
useMessagesStore.getState().reset();
|
||||
useVoiceStore.getState().reset();
|
||||
// Голосовая сессия могла остаться от прошлого теста: гасим её без запросов.
|
||||
useVoiceConnectionStore.getState().reset();
|
||||
useFriendsStore.getState().reset();
|
||||
resetUiStore();
|
||||
// `reset` закрывает и общее соединение шлюза: иначе колбэки прошлого теста
|
||||
@@ -205,6 +221,18 @@ interface SnapshotGuildInput {
|
||||
my_nickname?: string;
|
||||
/** Кастомные эмодзи сервера (AGENT.md 7.12). */
|
||||
emojis?: { id: string; name: string; file_id: string; animated?: boolean; token?: string }[];
|
||||
/** Кто сидит в голосовых комнатах (AGENT.md 7.14). */
|
||||
voice_states?: {
|
||||
user_id: string;
|
||||
channel_id: string;
|
||||
self_mute?: boolean;
|
||||
self_deaf?: boolean;
|
||||
server_mute?: boolean;
|
||||
server_deaf?: boolean;
|
||||
camera?: boolean;
|
||||
screen?: boolean;
|
||||
joined_at?: string;
|
||||
}[];
|
||||
channels?: {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -269,6 +297,18 @@ export function makeReadySnapshot(
|
||||
animated: emoji.animated ?? false,
|
||||
token: emoji.token ?? `<:${emoji.name}:${emoji.file_id}>`,
|
||||
})),
|
||||
voice_states: (guild.voice_states ?? []).map((state, index) => ({
|
||||
user_id: state.user_id,
|
||||
channel_id: state.channel_id,
|
||||
guild_id: guild.id,
|
||||
self_mute: state.self_mute ?? false,
|
||||
self_deaf: state.self_deaf ?? false,
|
||||
server_mute: state.server_mute ?? false,
|
||||
server_deaf: state.server_deaf ?? false,
|
||||
camera: state.camera ?? false,
|
||||
screen: state.screen ?? false,
|
||||
joined_at: state.joined_at ?? `2026-09-19T10:0${String(index)}:00Z`,
|
||||
})),
|
||||
})),
|
||||
dm_channels: dmChannels.map((channel) => ({
|
||||
id: channel.id,
|
||||
|
||||
Reference in New Issue
Block a user