ref: updated tests/prettier/lint
This commit is contained in:
-305
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
-504
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
-244
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
-176
@@ -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",
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
-61
@@ -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();
|
||||
});
|
||||
});
|
||||
-186
@@ -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",
|
||||
});
|
||||
});
|
||||
});
|
||||
-109
@@ -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([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user