Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 82 additions & 1 deletion app/actions/websocket/burn_on_read.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {removePost} from '@actions/local/post';
import {handleNewPostEvent, handlePostEdited} from '@actions/websocket/posts';
import {PostTypes} from '@constants/post';
import DatabaseManager from '@database/manager';
import {getPostById} from '@queries/servers/post';
import TestHelper from '@test/test_helper';

import {handleBoRPostRevealedEvent} from './burn_on_read';
import {handleBoRPostRevealedEvent, handleBoRPostBurnedEvent} from './burn_on_read';

jest.mock('@actions/websocket/posts');
jest.mock('@queries/servers/post');
jest.mock('@actions/local/post');

const serverUrl = 'burnOnRead.test.com';

Expand All @@ -19,6 +22,7 @@ describe('WebSocket Burn on Read Actions', () => {
const mockedGetPostById = jest.mocked(getPostById);
const mockedHandleNewPostEvent = jest.mocked(handleNewPostEvent);
const mockedHandlePostEdited = jest.mocked(handlePostEdited);
const mockedRemovePost = jest.mocked(removePost);

beforeEach(async () => {
await DatabaseManager.init([serverUrl]);
Expand Down Expand Up @@ -105,4 +109,81 @@ describe('WebSocket Burn on Read Actions', () => {
expect(mockedHandlePostEdited).not.toHaveBeenCalled();
});
});

describe('handleBoRPostBurnedEvent', () => {
const burnOnReadPost = TestHelper.fakePostModel({
id: 'post1',
type: PostTypes.BURN_ON_READ,
});

const regularPost = TestHelper.fakePostModel({
id: 'post2',
type: '',
});

const msg = {
data: {
post_id: 'post1',
},
} as WebSocketMessage;

it('should remove burn-on-read post when it exists locally', async () => {
mockedGetPostById.mockResolvedValue(burnOnReadPost);

const result = await handleBoRPostBurnedEvent(serverUrl, msg);

expect(mockedGetPostById).toHaveBeenCalledWith(expect.any(Object), 'post1');
expect(mockedRemovePost).toHaveBeenCalledWith(serverUrl, burnOnReadPost);
expect(result).toEqual({});
});

it('should not remove post when post does not exist locally', async () => {
mockedGetPostById.mockResolvedValue(undefined);

const result = await handleBoRPostBurnedEvent(serverUrl, msg);

expect(mockedGetPostById).toHaveBeenCalledWith(expect.any(Object), 'post1');
expect(mockedRemovePost).not.toHaveBeenCalled();
expect(result).toBeNull();
});

it('should not remove post when post is not burn-on-read type', async () => {
mockedGetPostById.mockResolvedValue(regularPost);

const result = await handleBoRPostBurnedEvent(serverUrl, msg);

expect(mockedGetPostById).toHaveBeenCalledWith(expect.any(Object), 'post1');
expect(mockedRemovePost).not.toHaveBeenCalled();
expect(result).toBeNull();
});

it('should handle missing server database gracefully', async () => {
const result = await handleBoRPostBurnedEvent('invalid-server-url', msg);

expect(mockedGetPostById).not.toHaveBeenCalled();
expect(mockedRemovePost).not.toHaveBeenCalled();
expect(result).toBeNull();
});

it('should handle missing operator gracefully', async () => {
// Mock a server database without an operator
DatabaseManager.serverDatabases[serverUrl] = {} as any;

const result = await handleBoRPostBurnedEvent(serverUrl, msg);

expect(mockedGetPostById).not.toHaveBeenCalled();
expect(mockedRemovePost).not.toHaveBeenCalled();
expect(result).toBeNull();
});

it('should handle errors gracefully and return error object', async () => {
mockedGetPostById.mockRejectedValue(new Error('Database error'));

const result = await handleBoRPostBurnedEvent(serverUrl, msg);

expect(result).toHaveProperty('error');
expect(result!.error).toBeInstanceOf(Error);
expect(mockedRemovePost).not.toHaveBeenCalled();
});
});
});
28 changes: 28 additions & 0 deletions app/actions/websocket/burn_on_read.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {removePost} from '@actions/local/post';
import {handleNewPostEvent, handlePostEdited} from '@actions/websocket/posts';
import {PostTypes} from '@constants/post';
import DatabaseManager from '@database/manager';
import {getPostById} from '@queries/servers/post';
import {logError} from '@utils/log';
Expand Down Expand Up @@ -34,3 +36,29 @@ export async function handleBoRPostRevealedEvent(serverUrl: string, msg: WebSock
return {error};
}
}

export async function handleBoRPostBurnedEvent(serverUrl: string, msg: WebSocketMessage) {
try {
const postId = msg.data.post_id;
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
if (!operator) {
return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: We have now three possible returns: null, {} and {error}. Any strong reason not to return {} in all the cases where we return null?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}

const {database} = operator;
const post = await getPostById(database, postId);
if (!post) {
return null;
}

if (post.type !== PostTypes.BURN_ON_READ) {
return null;
}

await removePost(serverUrl, post);
return {};
} catch (error) {
logError('handleBoRPostBurnedEvent could not handle websocket event for burned burn-on-read post', error);
return {error};
}
}
5 changes: 4 additions & 1 deletion app/actions/websocket/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import {handleAgentPostUpdate} from '@agents/actions/websocket';

import * as bookmark from '@actions/local/channel_bookmark';
import {handleBoRPostRevealedEvent} from '@actions/websocket/burn_on_read';
import {handleBoRPostBurnedEvent, handleBoRPostRevealedEvent} from '@actions/websocket/burn_on_read';
import * as scheduledPost from '@actions/websocket/scheduled_post';
import * as calls from '@calls/connection/websocket_event_handlers';
import {WebsocketEvents} from '@constants';
Expand Down Expand Up @@ -314,6 +314,9 @@ export async function handleWebSocketEvent(serverUrl: string, msg: WebSocketMess
case WebsocketEvents.BOR_POST_REVEALED:
handleBoRPostRevealedEvent(serverUrl, msg);
break;
case WebsocketEvents.BOR_POST_BURNED:
handleBoRPostBurnedEvent(serverUrl, msg);
break;
}
handlePlaybookEvents(serverUrl, msg);
}
1 change: 1 addition & 0 deletions app/constants/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ const WebsocketEvents = {

// Burn on Read
BOR_POST_REVEALED: 'post_revealed',
BOR_POST_BURNED: 'post_burned',
};

export default WebsocketEvents;