ref: updated tests/prettier/lint

This commit is contained in:
montaghanmy
2023-04-04 13:51:33 +01:00
parent 1458a11e7c
commit d4b054048c
31 changed files with 259 additions and 4323 deletions
@@ -1,305 +0,0 @@
import { describe, expect, it, jest, beforeEach, afterEach } from "@jest/globals";
import {
IncomingMessageQueueMessage,
MessageQueueHandler,
MessageQueueServiceAPI,
MessageQueueServiceProcessor,
} from "../../../../../../src/core/platform/services/message-queue/api";
describe("The MessageQueueServiceProcessor class", () => {
let pubsubService: MessageQueueServiceAPI;
let subscribe;
let publish;
let topic;
let errorTopic;
let outTopic;
beforeEach(() => {
topic = "inputtopic";
errorTopic = "errorTopic";
outTopic = "outTopic";
subscribe = jest.fn();
publish = jest.fn();
pubsubService = {
publish,
subscribe,
} as unknown as MessageQueueServiceAPI;
});
afterEach(() => {
jest.restoreAllMocks();
jest.resetAllMocks();
});
describe("The init function", () => {
it("should not subscribe when topics is not defined", async () => {
subscribe.mockResolvedValue(true);
const processor = new MessageQueueServiceProcessor(
{} as MessageQueueHandler<unknown, unknown>,
pubsubService,
);
await processor.init();
expect(subscribe).not.toHaveBeenCalled;
});
it("should not subscribe when topics.in is not defined", async () => {
subscribe.mockResolvedValue(true);
const processor = new MessageQueueServiceProcessor(
{
topics: {},
} as MessageQueueHandler<unknown, unknown>,
pubsubService,
);
await processor.init();
expect(subscribe).not.toHaveBeenCalled;
});
it("should subscribe when topics.in is defined", async () => {
subscribe.mockResolvedValue(true);
const processor = new MessageQueueServiceProcessor(
{
topics: {
in: topic,
},
} as MessageQueueHandler<unknown, unknown>,
pubsubService,
);
await processor.init();
expect(subscribe).toHaveBeenCalledTimes(1);
expect(subscribe).toHaveBeenCalledWith(topic, expect.any(Function), undefined);
});
});
describe("The function handling incoming messages", () => {
describe("When handler.validate is defined", () => {
it("should not process data when data is not valid", async () => {
const message = {
data: "foo",
} as IncomingMessageQueueMessage<string>;
const validate = jest.fn().mockReturnValue(false);
const process = jest.fn().mockReturnValue(true);
const processor = new MessageQueueServiceProcessor(
{
topics: {
in: topic,
},
validate,
process,
} as unknown as MessageQueueHandler<unknown, unknown>,
pubsubService,
);
await processor.init();
const processMessage = subscribe.mock.calls[0][1];
await processMessage(message);
expect(validate).toBeCalledTimes(1);
expect(validate).toBeCalledWith(message.data);
expect(process).not.toHaveBeenCalled;
});
it("should process data when data is valid", async () => {
const message = {
data: "foo",
} as IncomingMessageQueueMessage<string>;
const validate = jest.fn().mockReturnValue(true);
const process = jest.fn().mockReturnValue(true);
const processor = new MessageQueueServiceProcessor(
{
topics: {
in: topic,
},
validate,
process,
} as unknown as MessageQueueHandler<unknown, unknown>,
pubsubService,
);
await processor.init();
const processMessage = subscribe.mock.calls[0][1];
await processMessage(message);
expect(validate).toBeCalledTimes(1);
expect(validate).toBeCalledWith(message.data);
expect(process).toHaveBeenCalled;
});
});
describe("When processing message", () => {
it("should not publish error when topics.error is not defined", async () => {
const message = {
data: "foo",
} as IncomingMessageQueueMessage<string>;
const process = jest.fn().mockRejectedValue(new Error("I failed to process"));
const processor = new MessageQueueServiceProcessor(
{
topics: {
in: topic,
},
process,
} as unknown as MessageQueueHandler<unknown, unknown>,
pubsubService,
);
await processor.init();
const processMessage = subscribe.mock.calls[0][1];
await processMessage(message);
expect(process).toBeCalledTimes(1);
expect(process).toHaveBeenCalledWith(message.data);
expect(publish).not.toBeCalled;
});
it("should publish error when topics.error is defined", async () => {
const message = {
data: "foo",
} as IncomingMessageQueueMessage<string>;
const process = jest.fn().mockRejectedValue(new Error("I failed to process"));
const processor = new MessageQueueServiceProcessor(
{
topics: {
in: topic,
error: errorTopic,
},
process,
} as unknown as MessageQueueHandler<unknown, unknown>,
pubsubService,
);
await processor.init();
const processMessage = subscribe.mock.calls[0][1];
await processMessage(message);
expect(process).toBeCalledTimes(1);
expect(process).toHaveBeenCalledWith(message.data);
expect(publish).toBeCalledTimes(1);
expect(publish).toBeCalledWith(errorTopic, expect.anything());
});
it("should not publish when processing does not return result", async () => {
const message = {
data: "foo",
} as IncomingMessageQueueMessage<string>;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const process = jest.fn().mockResolvedValue(null);
const processor = new MessageQueueServiceProcessor(
{
topics: {
in: topic,
out: outTopic,
error: errorTopic,
},
process,
} as unknown as MessageQueueHandler<unknown, unknown>,
pubsubService,
);
await processor.init();
const processMessage = subscribe.mock.calls[0][1];
await processMessage(message);
expect(process).toBeCalledTimes(1);
expect(process).toHaveBeenCalledWith(message.data);
expect(publish).not.toBeCalled;
});
it("should not publish when out topic is not defined", async () => {
const message = {
data: "foo",
} as IncomingMessageQueueMessage<string>;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const process = jest.fn().mockResolvedValue(true);
const processor = new MessageQueueServiceProcessor(
{
topics: {
in: topic,
error: errorTopic,
},
process,
} as unknown as MessageQueueHandler<unknown, unknown>,
pubsubService,
);
await processor.init();
const processMessage = subscribe.mock.calls[0][1];
await processMessage(message);
expect(process).toBeCalledTimes(1);
expect(process).toHaveBeenCalledWith(message.data);
expect(publish).not.toBeCalled;
});
it("should publish when out topic is defined and process returns result", async () => {
const result = "processing result";
const message = {
data: "foo",
} as IncomingMessageQueueMessage<string>;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const process = jest.fn().mockResolvedValue(result);
const processor = new MessageQueueServiceProcessor(
{
topics: {
in: topic,
error: errorTopic,
out: outTopic,
},
process,
} as unknown as MessageQueueHandler<unknown, unknown>,
pubsubService,
);
await processor.init();
const processMessage = subscribe.mock.calls[0][1];
await processMessage(message);
expect(process).toBeCalledTimes(1);
expect(process).toHaveBeenCalledWith(message.data);
expect(publish).toBeCalledTimes(1);
expect(publish).toBeCalledWith(
outTopic,
expect.objectContaining({
data: result,
}),
);
});
it("should publish error when result can not be published", async () => {
publish.mockRejectedValue(new Error("I failed to publish"));
const result = "processing result";
const message = {
data: "foo",
} as IncomingMessageQueueMessage<string>;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const process = jest.fn().mockResolvedValue(result);
const processor = new MessageQueueServiceProcessor(
{
topics: {
in: topic,
error: errorTopic,
out: outTopic,
},
process,
} as unknown as MessageQueueHandler<unknown, unknown>,
pubsubService,
);
await processor.init();
const processMessage = subscribe.mock.calls[0][1];
await processMessage(message);
expect(process).toBeCalledTimes(1);
expect(process).toHaveBeenCalledWith(message.data);
expect(publish).toBeCalledTimes(2);
expect(publish.mock.calls[0][0]).toEqual(outTopic);
expect(publish.mock.calls[1][0]).toEqual(errorTopic);
});
});
});
});
@@ -1,504 +0,0 @@
import { afterEach, beforeEach, describe, expect, it, jest } from "@jest/globals";
import {
ListResult,
OperationType,
SaveResult,
} from "../../../../../../../../src/core/platform/framework/api/crud-service";
import { ChannelMemberNotificationLevel } from "../../../../../../../../src/services/channels/types";
import { MessageNotification } from "../../../../../../../../src/services/messages/types";
import {
ChannelMemberNotificationPreference,
ChannelThreadUsers,
} from "../../../../../../../../src/services/notifications/entities";
import { ChannelType } from "../../../../../../../../src/utils/types";
import gr from "../../../../../../../../src/services/global-resolver";
import { NewChannelMessageProcessor } from "../../../../../../../../src/services/notifications/services/engine/processors/new-channel-message";
describe("The NewChannelMessageProcessor class", () => {
let channel_id, company_id, workspace_id, thread_id;
let service: any;
let processor: NewChannelMessageProcessor;
let getUsersInThread;
let getChannelPreferencesForUsers;
beforeEach(() => {
channel_id = "channel_id";
company_id = "company_id";
workspace_id = "workspace_id";
getUsersInThread = jest.fn();
getChannelPreferencesForUsers = jest.fn();
setPreferences();
setUsersInThread();
service = {
channelThreads: {
bulkSave: jest
.fn()
.mockResolvedValue(
new SaveResult<ChannelThreadUsers[]>("thread", [], OperationType.CREATE) as never,
),
getUsersInThread,
},
channelPreferences: {
getChannelPreferencesForUsers,
},
};
gr.services = {
notifications: service,
} as any;
processor = new NewChannelMessageProcessor();
});
afterEach(() => {
jest.restoreAllMocks();
jest.resetAllMocks();
});
function getMessage(newThread: boolean = false): MessageNotification {
const message = {
company_id,
workspace_id,
channel_id,
id: "id",
thread_id: "thread_id",
sender: "sender",
mentions: {
users: [],
},
} as MessageNotification;
if (newThread) {
// message is new thread when thread_id is not defined
message.thread_id = undefined;
}
return message;
}
function setPreferences(preferences: ChannelMemberNotificationPreference[] = []): void {
getChannelPreferencesForUsers.mockResolvedValue(
new ListResult<ChannelMemberNotificationPreference>("preferences", preferences),
);
}
function setUsersInThread(users: string[] = []) {
getUsersInThread.mockResolvedValue(
new ListResult<ChannelThreadUsers>(
"users",
users.map(
user_id =>
({
company_id,
channel_id,
workspace_id,
thread_id,
user_id,
} as ChannelThreadUsers),
),
),
);
}
describe("The process method", () => {
describe("When message is a new thread", () => {
function setNewThreadPreferences() {
setPreferences([
{
channel_id,
company_id,
user_id: "1",
last_read: Date.now(),
preferences: ChannelMemberNotificationLevel.ALL,
},
{
channel_id,
company_id,
user_id: "2",
last_read: Date.now(),
preferences: ChannelMemberNotificationLevel.MENTIONS,
},
{
channel_id,
company_id,
user_id: "3",
last_read: Date.now(),
preferences: ChannelMemberNotificationLevel.ME,
},
{
channel_id,
company_id,
user_id: "4",
last_read: Date.now(),
preferences: ChannelMemberNotificationLevel.NONE,
},
]);
}
it("should return undefined when there is no one to notify", async done => {
const message = getMessage(true);
const result = await processor.process(message);
expect(service.channelThreads.bulkSave).toBeCalledTimes(1);
expect(service.channelThreads.bulkSave).toBeCalledWith(
[
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.id,
user_id: message.sender,
},
],
undefined,
);
expect(service.channelThreads.getUsersInThread).not.toBeCalled;
expect(service.channelPreferences.getChannelPreferencesForUsers).toBeCalledTimes(1);
expect(service.channelPreferences.getChannelPreferencesForUsers).toBeCalledWith({
company_id: message.company_id,
channel_id: message.channel_id,
});
expect(result).toBeUndefined();
done();
});
it("without mentions, should get the preferences for all channel members and return only the ones who want to be notified", async done => {
const message = getMessage(true);
setNewThreadPreferences();
const result = await processor.process(message);
expect(service.channelThreads.bulkSave).toBeCalledTimes(1);
expect(service.channelThreads.bulkSave).toBeCalledWith(
[
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.id,
user_id: message.sender,
},
],
undefined,
);
expect(service.channelThreads.getUsersInThread).not.toBeCalled;
expect(service.channelPreferences.getChannelPreferencesForUsers).toBeCalledTimes(1);
expect(service.channelPreferences.getChannelPreferencesForUsers).toBeCalledWith({
company_id: message.company_id,
channel_id: message.channel_id,
});
expect(result.mentions.users).toEqual(["1"]);
done();
});
it("with @all mention, should get the preferences for all channel members and return only the ones who want to be notified", async done => {
const message = getMessage(true);
message.mentions.specials = ["all"];
setNewThreadPreferences();
const result = await processor.process(message);
expect(service.channelThreads.bulkSave).toBeCalledTimes(1);
expect(service.channelThreads.bulkSave).toBeCalledWith(
[
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.id,
user_id: message.sender,
},
],
undefined,
);
expect(service.channelThreads.getUsersInThread).not.toBeCalled;
expect(service.channelPreferences.getChannelPreferencesForUsers).toBeCalledTimes(1);
expect(service.channelPreferences.getChannelPreferencesForUsers).toBeCalledWith({
company_id: message.company_id,
channel_id: message.channel_id,
});
expect(result.mentions.users).toEqual(["1", "2"]);
done();
});
it("with @here mentions, should get the preferences for all channel members and return only the ones who want to be notified", async done => {
const message = getMessage(true);
message.mentions.specials = ["here"];
setNewThreadPreferences();
const result = await processor.process(message);
expect(service.channelThreads.bulkSave).toBeCalledTimes(1);
expect(service.channelThreads.bulkSave).toBeCalledWith(
[
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.id,
user_id: message.sender,
},
],
undefined,
);
expect(service.channelThreads.getUsersInThread).not.toBeCalled;
expect(service.channelPreferences.getChannelPreferencesForUsers).toBeCalledTimes(1);
expect(service.channelPreferences.getChannelPreferencesForUsers).toBeCalledWith({
company_id: message.company_id,
channel_id: message.channel_id,
});
expect(result.mentions.users).toEqual(["1", "2"]);
done();
});
it("with @user mentions, should get the preferences for all channel members and return only the ones who want to be notified", async done => {
const message = getMessage(true);
message.mentions.users = ["1", "2", "3", "4"];
setNewThreadPreferences();
const result = await processor.process(message);
expect(service.channelThreads.bulkSave).toBeCalledTimes(1);
expect(service.channelThreads.bulkSave).toBeCalledWith(
[
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.id,
user_id: message.sender,
},
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.id,
user_id: "1",
},
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.id,
user_id: "2",
},
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.id,
user_id: "3",
},
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.id,
user_id: "4",
},
],
undefined,
);
expect(service.channelThreads.getUsersInThread).not.toBeCalled;
expect(service.channelPreferences.getChannelPreferencesForUsers).toBeCalledTimes(1);
expect(service.channelPreferences.getChannelPreferencesForUsers).toBeCalledWith({
company_id: message.company_id,
channel_id: message.channel_id,
});
expect(result.mentions.users).toEqual(["1", "2", "3"]);
done();
});
it("When message is a direct message", async done => {
const message = getMessage(true);
message.workspace_id = ChannelType.DIRECT;
message.mentions.users = ["1", "2", "3", "4"];
setNewThreadPreferences();
const result = await processor.process(message);
expect(service.channelThreads.bulkSave).toBeCalledTimes(1);
expect(service.channelThreads.bulkSave).toBeCalledWith(
[
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.id,
user_id: message.sender,
},
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.id,
user_id: "1",
},
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.id,
user_id: "2",
},
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.id,
user_id: "3",
},
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.id,
user_id: "4",
},
],
undefined,
);
expect(service.channelThreads.getUsersInThread).not.toBeCalled;
expect(service.channelPreferences.getChannelPreferencesForUsers).toBeCalledTimes(1);
expect(service.channelPreferences.getChannelPreferencesForUsers).toBeCalledWith({
company_id: message.company_id,
channel_id: message.channel_id,
});
expect(result.mentions.users).toEqual(["1", "2", "3"]);
done();
});
});
describe("When message is a response to a thread", () => {
function setThreadResponsePreferences() {
setPreferences([
{
channel_id,
company_id,
user_id: "1",
last_read: Date.now(),
preferences: ChannelMemberNotificationLevel.ALL,
},
{
channel_id,
company_id,
user_id: "2",
last_read: Date.now(),
preferences: ChannelMemberNotificationLevel.MENTIONS,
},
{
channel_id,
company_id,
user_id: "3",
last_read: Date.now(),
preferences: ChannelMemberNotificationLevel.ME,
},
{
channel_id,
company_id,
user_id: "4",
last_read: Date.now(),
preferences: ChannelMemberNotificationLevel.NONE,
},
]);
}
it("should return undefined when there is no one to notify", async done => {
const message = getMessage();
setUsersInThread(["1", "2", "3", "4"]);
const result = await processor.process(message);
expect(service.channelThreads.bulkSave).toBeCalledTimes(1);
expect(service.channelThreads.bulkSave).toBeCalledWith(
[
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.thread_id,
user_id: message.sender,
},
],
undefined,
);
expect(service.channelThreads.getUsersInThread).toBeCalled;
expect(service.channelPreferences.getChannelPreferencesForUsers).toBeCalledTimes(1);
expect(result).toBeUndefined();
done();
});
it("without mentions, should get the preferences for all members involved and return only the ones with preference !== NONE", async done => {
const message = getMessage();
setUsersInThread(["1", "2", "3", "4"]);
setThreadResponsePreferences();
const result = await processor.process(message);
expect(service.channelThreads.bulkSave).toBeCalledTimes(1);
expect(service.channelThreads.bulkSave).toBeCalledWith(
[
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.thread_id,
user_id: message.sender,
},
],
undefined,
);
expect(service.channelThreads.getUsersInThread).toBeCalled;
expect(service.channelPreferences.getChannelPreferencesForUsers).toBeCalledTimes(1);
expect(result.mentions.users).toEqual(["1", "2", "3"]);
done();
});
it("with @user mentions, should get the preferences for all members involved and return only the ones who want to be notified", async done => {
const message = getMessage();
message.mentions.users = ["1", "2", "3", "4"];
setUsersInThread(["1", "2", "3", "4"]);
setThreadResponsePreferences();
const result = await processor.process(message);
expect(service.channelThreads.bulkSave).toBeCalledTimes(1);
expect(service.channelThreads.bulkSave).toBeCalledWith(
[
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.thread_id,
user_id: message.sender,
},
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.thread_id,
user_id: "1",
},
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.thread_id,
user_id: "2",
},
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.thread_id,
user_id: "3",
},
{
company_id: message.company_id,
channel_id: message.channel_id,
thread_id: message.thread_id,
user_id: "4",
},
],
undefined,
);
expect(service.channelThreads.getUsersInThread).toBeCalled;
expect(service.channelPreferences.getChannelPreferencesForUsers).toBeCalledTimes(1);
expect(result.mentions.users).toEqual(["1", "2", "3"]);
done();
});
});
});
});
@@ -1,244 +0,0 @@
import "reflect-metadata";
import { afterEach, beforeEach, describe, expect, it, jest } from "@jest/globals";
import {
ListResult,
OperationType,
SaveResult,
} from "../../../../../../../../src/core/platform/framework/api/crud-service";
import { MessageQueueServiceAPI } from "../../../../../../../../src/core/platform/services/message-queue/api";
import { ChannelMemberNotificationLevel } from "../../../../../../../../src/services/channels/types";
import {
ChannelMemberNotificationPreference,
ChannelThreadUsers,
} from "../../../../../../../../src/services/notifications/entities";
import {
TYPE as UserNotificationBadgeType,
UserNotificationBadge,
} from "../../../../../../../../src/services/notifications/entities/user-notification-badges";
import { MentionNotification } from "../../../../../../../../src/services/notifications/types";
import { uniqueId } from "lodash";
import { PushNotificationToUsersMessageProcessor } from "../../../../../../../../src/services/notifications/services/engine/processors/push-to-users";
import gr from "../../../../../../../../src/services/global-resolver";
describe("The PushNotificationToUsersMessageProcessor class", () => {
let channel_id, company_id, workspace_id, thread_id;
let pubsubService: MessageQueueServiceAPI;
let processor: PushNotificationToUsersMessageProcessor;
let getUsersInThread;
let getChannelPreferencesForUsers;
let saveBadge;
beforeEach(async () => {
channel_id = "channel_id";
company_id = "company_id";
workspace_id = "workspace_id";
thread_id: "thread_id";
getUsersInThread = jest.fn();
getChannelPreferencesForUsers = jest.fn();
saveBadge = jest.fn();
setPreferences();
setUsersInThread();
pubsubService = {
publish: jest.fn(),
} as unknown as MessageQueueServiceAPI;
//
const service = {
badges: {
save: saveBadge,
},
channelPreferences: {
getChannelPreferencesForUsers,
},
};
gr.platformServices = {
messageQueue: pubsubService,
} as any;
gr.services = {
notifications: service,
} as any;
processor = new PushNotificationToUsersMessageProcessor();
});
afterEach(() => {
jest.restoreAllMocks();
jest.resetAllMocks();
});
function getMessage(newThread: boolean = false): MentionNotification {
const message = {
company_id,
workspace_id,
channel_id,
message_id: "id",
creation_date: Date.now(),
thread_id,
mentions: {
users: [],
},
} as MentionNotification;
if (newThread) {
// message is new thread when thread_id is not defined
message.thread_id = undefined;
}
return message;
}
function setPreferences(preferences: ChannelMemberNotificationPreference[] = []): void {
getChannelPreferencesForUsers.mockResolvedValue(
new ListResult<ChannelMemberNotificationPreference>("preferences", preferences),
);
}
function setBadgeResults(badges: UserNotificationBadge[] = []): void {
badges.forEach(badge =>
saveBadge.mockResolvedValueOnce(
new SaveResult(UserNotificationBadgeType, badge, OperationType.CREATE),
),
);
}
function setUsersInThread(users: string[] = []) {
getUsersInThread.mockResolvedValue(
new ListResult<ChannelThreadUsers>(
"users",
users.map(
user_id =>
({
company_id,
channel_id,
workspace_id,
thread_id,
user_id,
} as ChannelThreadUsers),
),
),
);
}
describe("The process method", () => {
it("will fail when channel_id is not defined", async () => {
const message = getMessage();
delete message.channel_id;
await expect(processor.process(message)).rejects.toThrowError("Missing required fields");
});
it("will fail when company_id is not defined", async () => {
const message = getMessage();
delete message.company_id;
await expect(processor.process(message)).rejects.toThrowError("Missing required fields");
});
it("will fail when workspace_id is not defined", async () => {
const message = getMessage();
delete message.workspace_id;
await expect(processor.process(message)).rejects.toThrowError("Missing required fields");
});
it("will fail when creation_date is not defined", async () => {
const message = getMessage();
delete message.creation_date;
await expect(processor.process(message)).rejects.toThrowError("Missing required fields");
});
it("will do nothing when mentions is not defined", async done => {
const message = getMessage();
delete message.mentions;
await processor.process(message);
expect(gr.services.notifications.channelPreferences.getChannelPreferencesForUsers).not
.toBeCalled;
done();
});
it("will do nothing when mentions.users is not defined", async done => {
const message = getMessage();
message.mentions = { users: undefined };
await processor.process(message);
expect(gr.services.notifications.channelPreferences.getChannelPreferencesForUsers).not
.toBeCalled;
done();
});
it("will do nothing when mentions.users is empty", async done => {
const message = getMessage();
message.mentions = { users: [] };
await processor.process(message);
expect(gr.services.notifications.channelPreferences.getChannelPreferencesForUsers).not
.toBeCalled;
done();
});
it("will keep users who did not read the channel yet", async done => {
const message = getMessage();
setPreferences([
{
channel_id,
company_id,
last_read: Date.now(),
user_id: "1",
preferences: ChannelMemberNotificationLevel.ALL,
},
{
channel_id,
company_id,
last_read: Date.now(),
user_id: "3",
preferences: ChannelMemberNotificationLevel.ALL,
},
]);
setBadgeResults([
{
channel_id,
company_id,
thread_id,
user_id: "1",
workspace_id,
message_id: message.message_id,
id: uniqueId(),
},
{
channel_id,
company_id,
thread_id,
user_id: "3",
workspace_id,
message_id: message.message_id,
id: uniqueId(),
},
]);
const lessThan = message.creation_date;
const users = ["1", "2", "3", "4"];
const channel = {
channel_id,
company_id,
};
message.mentions = { users };
await processor.process(message);
expect(getChannelPreferencesForUsers).toBeCalledWith(channel, users, { lessThan }, undefined);
expect(gr.services.notifications.badges.save).toBeCalledTimes(2);
expect(pubsubService.publish).toBeCalledTimes(2);
done();
});
});
});
@@ -1,176 +0,0 @@
import { describe, expect, it, jest, beforeEach, afterEach, afterAll } from "@jest/globals";
import { generateVideoPreview } from "../../../../../../../src/services/previews/services/files/processing/video";
import ffmpeg, { ffprobe } from "fluent-ffmpeg";
import { cleanFiles, getTmpFile } from "../../../../../../../src/utils/files";
import fs from "fs";
jest.mock("fluent-ffmpeg");
jest.mock("../../../../../../../src/utils/files");
const ffmpegMock = {
screenshot: jest.fn().mockReturnValue({
on: function (e, cb) {
cb();
return this;
},
}),
};
beforeEach(() => {
jest.spyOn(fs, "statSync").mockReturnValue({ size: 1 } as any);
(ffmpeg as any).mockImplementation(() => ffmpegMock);
(cleanFiles as any).mockImplementation(() => jest.fn());
(getTmpFile as any).mockImplementation(() => "/tmp/file");
(ffprobe as any).mockImplementation((i, cb) => {
cb(null, {
streams: [
{
width: 320,
height: 240,
},
],
});
});
});
afterEach(() => {
jest.clearAllMocks();
});
afterAll(() => {
jest.restoreAllMocks();
});
describe("the generateVideoPreview function", () => {
it("should return a promise", () => {
const result = generateVideoPreview([]);
expect(result).toBeInstanceOf(Promise);
});
it("should attempt to create a screenshot using ffmpeg", async () => {
await generateVideoPreview(["/foo/bar"]);
expect(ffmpeg as any).toBeCalledWith("/foo/bar");
});
it("should call ffmpeg screenshot method with the original video dimentions", async () => {
(getTmpFile as any).mockImplementation(() => "/tmp/some-random-file");
(ffprobe as any).mockImplementation((i, cb) => {
cb(null, {
streams: [
{
width: 500,
height: 500,
},
],
});
});
await generateVideoPreview(["/foo/bar"]);
expect(ffmpegMock.screenshot).toBeCalledWith({
count: 1,
filename: "some-random-file.jpg",
folder: "/tmp",
timemarks: ["0"],
size: "500x500",
});
});
it("should attempt to generate a temporary file", async () => {
await generateVideoPreview(["/foo/bar"]);
expect(getTmpFile).toBeCalled();
});
it("should attempt to clean up the temporary file in case of errors", async () => {
const expectedError = new Error("failed to generate video preview: Error: foo");
(ffmpeg as any).mockImplementation(() => {
throw new Error("foo");
});
(ffprobe as any).mockImplementation((i, cb) => {
cb(null, {
streams: [
{
width: 500,
height: 500,
},
],
});
});
await expect(generateVideoPreview(["/foo/bar"])).rejects.toThrow(expectedError);
expect(cleanFiles).toBeCalledWith(["/tmp/file.jpg"]);
});
it("should return the thumbnail information", async () => {
const result = await generateVideoPreview(["/foo/bar"]);
expect(result).toEqual([
{
width: 320,
height: 240,
type: "image/jpg",
size: 1,
path: "/tmp/file.jpg",
},
]);
});
it("should generate thumbnails for multiple files", async () => {
const result = await generateVideoPreview(["/foo/bar", "/foo/baz", "/foo/tar"]);
expect(result).toHaveLength(3);
});
it("should use ffprobe to get the video dimensions", async () => {
await generateVideoPreview(["/foo/bar"]);
expect(ffprobe).toBeCalledWith("/foo/bar", expect.any(Function));
});
it("should use 1080p as the maximum video dimensions", async () => {
(ffprobe as any).mockImplementation((i, cb) => {
cb(null, {
streams: [
{
width: 5000,
height: 5000,
},
],
});
});
const result = await generateVideoPreview(["/foo/bar"]);
expect(result).toEqual([
{
width: 1920,
height: 1080,
type: "image/jpg",
size: 1,
path: "/tmp/file.jpg",
},
]);
});
it("should use 320x240 as the minimum video dimensions", async () => {
(ffprobe as any).mockImplementation((i, cb) => {
cb(null, {
streams: [
{
width: 320,
height: 180,
},
],
});
});
const result = await generateVideoPreview(["/foo/bar"]);
expect(result).toEqual([
{
width: 320,
height: 240,
type: "image/jpg",
size: 1,
path: "/tmp/file.jpg",
},
]);
});
});
@@ -1,61 +0,0 @@
import { describe, expect, it, jest, beforeEach, afterEach, afterAll } from "@jest/globals";
import { generateImageUrlPreview } from "../../../../../../../src/services/previews/services/links/processing/image";
import getFavicons from "get-website-favicon";
import imageProbe from "probe-image-size";
jest.mock("get-website-favicon");
jest.mock("probe-image-size");
beforeEach(() => {
(imageProbe as any).mockImplementation(() => ({
width: 320,
height: 240,
}));
(getFavicons as any).mockImplementation(() => ({
icons: [
{
src: "http://foo.bar/favicon.ico",
},
],
}));
});
afterEach(() => {
jest.clearAllMocks();
});
afterAll(() => {
jest.restoreAllMocks();
});
describe("the generateImageUrlPreview function", () => {
it("should return a promise", () => {
const result = generateImageUrlPreview("http://foo.bar");
expect(result).toBeInstanceOf(Promise);
});
it("should generate a preview for a given image url", async () => {
const result = await generateImageUrlPreview("https://foo.bar/image.jpg");
expect(result).toEqual({
title: "image.jpg", // title should be the file name
domain: "foo.bar",
favicon: "http://foo.bar/favicon.ico",
url: "https://foo.bar/image.jpg",
img_height: 240,
img_width: 320,
description: null,
img: "https://foo.bar/image.jpg",
});
});
it("should return nothing in case of error", async () => {
(imageProbe as any).mockImplementation(() => {
throw new Error("failed to probe image");
});
const result = await generateImageUrlPreview("https://foo.bar/image.jpg");
expect(result).toBeUndefined();
});
});
@@ -1,186 +0,0 @@
import { describe, expect, it, jest, beforeEach, afterEach, afterAll } from "@jest/globals";
import { generateLinkPreview } from "../../../../../../../src/services/previews/services/links/processing/link";
import { parser } from "html-metadata-parser";
import getFavicons from "get-website-favicon";
import imageProbe from "probe-image-size";
jest.mock("html-metadata-parser");
jest.mock("get-website-favicon");
jest.mock("probe-image-size");
beforeEach(() => {
(imageProbe as any).mockImplementation(() => ({
width: 320,
height: 240,
}));
(getFavicons as any).mockImplementation(() => ({
icons: [
{
src: "http://foo.bar/favicon.ico",
},
],
}));
(parser as any).mockImplementation(() => ({
og: {
title: "Foo",
description: "Bar",
image: "http://foo.bar/image.jpg",
},
meta: {
title: "Foo",
description: "Bar",
image: "http://foo.bar/image1.jpg",
},
images: ["http://foo.bar/image2.jpg"],
}));
});
afterEach(() => {
jest.clearAllMocks();
});
afterAll(() => {
jest.restoreAllMocks();
});
describe("the generateLinkPreview service", () => {
it("should return a promise", () => {
const result = generateLinkPreview("http://foo.bar");
expect(result).toBeInstanceOf(Promise);
});
it("should return a promise that resolves to a preview", async () => {
const result = await generateLinkPreview("https://foo.bar");
expect(result).toEqual({
title: "Foo",
description: "Bar",
img: "http://foo.bar/image.jpg",
favicon: "http://foo.bar/favicon.ico",
img_width: 320,
img_height: 240,
domain: "foo.bar",
url: "https://foo.bar",
});
});
it("should return a promise that resolves to undefined if no previews are found", async () => {
(parser as any).mockImplementation(() => {
throw new Error("failed to parse");
});
(getFavicons as any).mockImplementation(() => []);
(imageProbe as any).mockImplementation(() => ({}));
const result = await generateLinkPreview("https://foo.bar");
expect(result).toBeUndefined();
});
it("should use og information as first choice", async () => {
(parser as any).mockImplementation(() => ({
og: {
title: "test",
description: "test",
image: "http://foo.bar/test.jpg",
},
meta: {
title: "test2",
description: "test2",
image: "http://foo.bar/test2.jpg",
},
images: ["http://foo.bar/test3.jpg"],
}));
const result = await generateLinkPreview("https://foo.bar");
expect(result).toEqual({
title: "test",
description: "test",
img: "http://foo.bar/test.jpg",
favicon: "http://foo.bar/favicon.ico",
img_width: 320,
img_height: 240,
domain: "foo.bar",
url: "https://foo.bar",
});
});
it("should use meta information as second choice", async () => {
(parser as any).mockImplementation(() => ({
meta: {
title: "test2",
description: "test2",
image: "http://foo.bar/test2.jpg",
},
images: [],
}));
const result = await generateLinkPreview("https://foo.bar");
expect(result).toEqual({
title: "test2",
description: "test2",
img: "http://foo.bar/test2.jpg",
favicon: "http://foo.bar/favicon.ico",
img_width: 320,
img_height: 240,
domain: "foo.bar",
url: "https://foo.bar",
});
});
it("should use the first image found in the url when none are present in the og or meta information", async () => {
(parser as any).mockImplementation(() => ({
og: {
title: "test",
description: "test",
},
meta: {
title: "test2",
description: "test2",
},
images: ["http://foo.bar/test3.jpg", "http://foo.bar/test4.jpg"],
}));
const result = await generateLinkPreview("https://foo.bar");
expect(result).toEqual({
title: "test",
description: "test",
img: "http://foo.bar/test3.jpg",
favicon: "http://foo.bar/favicon.ico",
img_width: 320,
img_height: 240,
domain: "foo.bar",
url: "https://foo.bar",
});
});
it("shouldn't attempt to probe for image size when none are found", async () => {
(parser as any).mockImplementation(() => ({
og: {
title: "test",
description: "test",
},
meta: {
title: "test2",
description: "test2",
},
images: [],
}));
await generateLinkPreview("https://foo.bar");
expect(imageProbe).not.toHaveBeenCalled();
});
it("should strip www from the domain", async () => {
const result = await generateLinkPreview("https://www.foo.bar");
expect(result).toEqual({
title: "Foo",
description: "Bar",
img: "http://foo.bar/image.jpg",
favicon: "http://foo.bar/favicon.ico",
img_width: 320,
img_height: 240,
domain: "foo.bar",
url: "https://www.foo.bar",
});
});
});
@@ -1,109 +0,0 @@
import {
describe,
expect,
it,
jest,
beforeEach,
afterEach,
afterAll,
beforeAll,
} from "@jest/globals";
import { LinkPreviewProcessService } from "../../../../../../../src/services/previews/services/links/processing/service";
import { generateLinkPreview } from "../../../../../../../src/services/previews/services/links/processing/link";
import { generateImageUrlPreview } from "../../../../../../../src/services/previews/services/links/processing/image";
import axios from "axios";
jest.mock("axios");
jest.mock("../../../../../../../src/services/previews/services/links/processing/link");
jest.mock("../../../../../../../src/services/previews/services/links/processing/image");
let service: LinkPreviewProcessService;
beforeEach(async () => {
(axios as any).mockImplementation(() => ({
headers: {
"content-type": "text/html",
},
}));
(generateLinkPreview as any).mockImplementation(() => ({
title: "Foo",
description: "Bar",
img: "http://foo.bar/image.jpg",
favicon: "http://foo.bar/favicon.ico",
}));
(generateImageUrlPreview as any).mockImplementation(() => ({
title: "image.jpg",
description: null,
img: "http://foo.bar/image.jpg",
favicon: "http://foo.bar/favicon.ico",
}));
});
afterEach(() => {
jest.clearAllMocks();
});
afterAll(() => {
jest.restoreAllMocks();
});
beforeAll(async () => {
service = await new LinkPreviewProcessService().init();
});
describe("the LinkPreviewProcessService service", () => {
it("should return a promise", () => {
const result = service.generatePreviews(["http://foo.bar"]);
expect(result).toBeInstanceOf(Promise);
});
it("should call generateLinkPreview when it's a html page", async () => {
(axios as any).mockImplementation(() => ({
headers: {
"content-type": "text/html",
},
}));
await service.generatePreviews(["http://foo.bar"]);
expect(generateLinkPreview).toHaveBeenCalled();
});
it("should call generateImageUrlPreview when it's an image", async () => {
(axios as any).mockImplementation(() => ({
headers: {
"content-type": "image/jpeg",
},
}));
await service.generatePreviews(["http://foo.bar/image.jpg"]);
expect(generateImageUrlPreview).toHaveBeenCalled();
});
it("should skip if the url content is not supported", async () => {
(axios as any).mockImplementation(() => ({
headers: {
"content-type": "application/json",
},
}));
await service.generatePreviews(["http://foo.bar"]);
expect(generateLinkPreview).not.toHaveBeenCalled();
expect(generateImageUrlPreview).not.toHaveBeenCalled();
});
// should filter null values
it("should filter null values", async () => {
(axios as any).mockImplementation(() => ({
headers: {
"content-type": "text/html",
},
}));
(generateLinkPreview as any).mockImplementation(() => null);
const result = await service.generatePreviews(["http://foo.bar", "http://foo.xyz"]);
expect(result).toEqual([]);
});
});