Files
Toju/toju-app/src/app/store/messages/messages-incoming.handlers.spec.ts
2026-05-23 15:28:40 +02:00

128 lines
3.8 KiB
TypeScript

import { defaultIfEmpty, firstValueFrom } from 'rxjs';
import { type Message } from '../../shared-kernel';
import { dispatchIncomingMessage } from './messages-incoming.handlers';
function createMessage(overrides: Partial<Message> = {}): Message {
return {
id: 'message-1',
roomId: 'room-1',
senderId: 'user-1',
senderName: 'User 1',
content: 'hello',
timestamp: 1,
reactions: [],
isDeleted: false,
...overrides
};
}
function createContext(overrides: Record<string, unknown> = {}) {
return {
db: {
getMessages: vi.fn()
},
webrtc: {
sendToPeer: vi.fn()
},
attachments: {},
debugging: {},
currentUser: null,
currentRoom: null,
savedRooms: [],
...overrides
} as const;
}
describe('dispatchIncomingMessage room-scoped sync', () => {
it('requests sync for event room even when another room is viewed', async () => {
const getMessages = vi.fn(async (roomId: string) => roomId === 'room-b'
? [createMessage({ roomId: 'room-b', timestamp: 10, editedAt: 10 })]
: [createMessage({ roomId: 'room-a', timestamp: 100, editedAt: 100 })]);
const sendToPeer = vi.fn();
const context = createContext({
db: { getMessages },
webrtc: { sendToPeer },
currentRoom: { id: 'room-a' },
savedRooms: [{ id: 'room-b' }]
});
await firstValueFrom(
dispatchIncomingMessage(
{
type: 'chat-sync-summary',
roomId: 'room-b',
fromPeerId: 'peer-1',
count: 2,
lastUpdated: 20
} as never,
context as never
).pipe(defaultIfEmpty(null))
);
expect(getMessages).toHaveBeenCalledWith('room-b', expect.any(Number), 0);
expect(sendToPeer).toHaveBeenCalledWith('peer-1', {
type: 'chat-sync-request',
roomId: 'room-b'
});
});
it('sends full sync for requested room even when another room is viewed', async () => {
const roomBMessageOne = createMessage({ id: 'message-b1', roomId: 'room-b', timestamp: 5 });
const roomBMessageTwo = createMessage({ id: 'message-b2', roomId: 'room-b', timestamp: 15 });
const roomBMessages = [roomBMessageOne, roomBMessageTwo];
const getMessages = vi.fn(async (roomId: string) => roomId === 'room-b'
? roomBMessages
: [createMessage({ id: 'message-a1', roomId: 'room-a', timestamp: 200 })]);
const sendToPeer = vi.fn();
const context = createContext({
db: { getMessages },
webrtc: { sendToPeer },
currentRoom: { id: 'room-a' },
savedRooms: [{ id: 'room-b' }]
});
await firstValueFrom(
dispatchIncomingMessage(
{
type: 'chat-sync-request',
roomId: 'room-b',
fromPeerId: 'peer-2'
} as never,
context as never
).pipe(defaultIfEmpty(null))
);
expect(getMessages).toHaveBeenCalledWith('room-b', expect.any(Number), 0);
expect(sendToPeer).toHaveBeenCalledWith('peer-2', {
type: 'chat-sync-batch',
roomId: 'room-b',
messages: roomBMessages
});
});
it('ignores chat messages for rooms that are not saved or currently viewed', async () => {
const saveMessage = vi.fn(async () => undefined);
const rememberMessageRoom = vi.fn();
const context = createContext({
db: { saveMessage },
attachments: { rememberMessageRoom },
currentRoom: { id: 'room-a' },
savedRooms: [{ id: 'room-a' }]
});
const action = await firstValueFrom(
dispatchIncomingMessage(
{
type: 'chat-message',
message: createMessage({ roomId: 'room-b' })
} as never,
context as never
).pipe(defaultIfEmpty(null))
);
expect(action).toBeNull();
expect(saveMessage).not.toHaveBeenCalled();
expect(rememberMessageRoom).not.toHaveBeenCalled();
});
});