|
|
|
|
@@ -1,14 +1,11 @@
|
|
|
|
|
import { randomUUID } from 'node:crypto';
|
|
|
|
|
import { mock } from 'node:test';
|
|
|
|
|
|
|
|
|
|
import ava, { TestFn } from 'ava';
|
|
|
|
|
import test from 'ava';
|
|
|
|
|
|
|
|
|
|
import { createModule } from '../../../__tests__/create-module';
|
|
|
|
|
import { Mockers } from '../../../__tests__/mocks';
|
|
|
|
|
import {
|
|
|
|
|
createTestingModule,
|
|
|
|
|
type TestingModule,
|
|
|
|
|
} from '../../../__tests__/utils';
|
|
|
|
|
import { NotificationNotFound } from '../../../base';
|
|
|
|
|
import { Due, NotificationNotFound } from '../../../base';
|
|
|
|
|
import {
|
|
|
|
|
DocMode,
|
|
|
|
|
MentionNotificationBody,
|
|
|
|
|
@@ -18,33 +15,33 @@ import {
|
|
|
|
|
Workspace,
|
|
|
|
|
WorkspaceMemberStatus,
|
|
|
|
|
} from '../../../models';
|
|
|
|
|
import { DocReader } from '../../doc';
|
|
|
|
|
import { DocStorageModule } from '../../doc';
|
|
|
|
|
import { FeatureModule } from '../../features';
|
|
|
|
|
import { MailModule } from '../../mail';
|
|
|
|
|
import { PermissionModule } from '../../permission';
|
|
|
|
|
import { StorageModule } from '../../storage';
|
|
|
|
|
import { NotificationModule } from '..';
|
|
|
|
|
import { NotificationService } from '../service';
|
|
|
|
|
|
|
|
|
|
interface Context {
|
|
|
|
|
module: TestingModule;
|
|
|
|
|
notificationService: NotificationService;
|
|
|
|
|
models: Models;
|
|
|
|
|
docReader: DocReader;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const test = ava as TestFn<Context>;
|
|
|
|
|
|
|
|
|
|
test.before(async t => {
|
|
|
|
|
const module = await createTestingModule();
|
|
|
|
|
t.context.module = module;
|
|
|
|
|
t.context.notificationService = module.get(NotificationService);
|
|
|
|
|
t.context.models = module.get(Models);
|
|
|
|
|
t.context.docReader = module.get(DocReader);
|
|
|
|
|
const module = await createModule({
|
|
|
|
|
imports: [
|
|
|
|
|
FeatureModule,
|
|
|
|
|
PermissionModule,
|
|
|
|
|
DocStorageModule,
|
|
|
|
|
StorageModule,
|
|
|
|
|
MailModule,
|
|
|
|
|
NotificationModule,
|
|
|
|
|
],
|
|
|
|
|
providers: [NotificationService],
|
|
|
|
|
});
|
|
|
|
|
const notificationService = module.get(NotificationService);
|
|
|
|
|
const models = module.get(Models);
|
|
|
|
|
|
|
|
|
|
let owner: User;
|
|
|
|
|
let member: User;
|
|
|
|
|
let workspace: Workspace;
|
|
|
|
|
|
|
|
|
|
test.beforeEach(async t => {
|
|
|
|
|
const { module } = t.context;
|
|
|
|
|
await module.initTestingDB();
|
|
|
|
|
test.beforeEach(async () => {
|
|
|
|
|
owner = await module.create(Mockers.User);
|
|
|
|
|
member = await module.create(Mockers.User);
|
|
|
|
|
workspace = await module.create(Mockers.Workspace, {
|
|
|
|
|
@@ -61,13 +58,13 @@ test.afterEach.always(() => {
|
|
|
|
|
mock.timers.reset();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test.after.always(async t => {
|
|
|
|
|
await t.context.module.close();
|
|
|
|
|
test.after.always(async () => {
|
|
|
|
|
await module.close();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should create invitation notification and email', async t => {
|
|
|
|
|
const { notificationService } = t.context;
|
|
|
|
|
const inviteId = randomUUID();
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createInvitation({
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -76,25 +73,28 @@ test('should create invitation notification and email', async t => {
|
|
|
|
|
inviteId,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.truthy(notification);
|
|
|
|
|
t.is(notification!.type, NotificationType.Invitation);
|
|
|
|
|
t.is(notification!.userId, member.id);
|
|
|
|
|
t.is(notification!.body.workspaceId, workspace.id);
|
|
|
|
|
t.is(notification!.body.createdByUserId, owner.id);
|
|
|
|
|
t.is(notification!.body.inviteId, inviteId);
|
|
|
|
|
|
|
|
|
|
// should send invitation email
|
|
|
|
|
const invitationMail = t.context.module.mails.last('MemberInvitation');
|
|
|
|
|
t.is(invitationMail.to, member.email);
|
|
|
|
|
const invitationMail = module.queue.last('notification.sendMail');
|
|
|
|
|
t.is(invitationMail.payload.to, member.email);
|
|
|
|
|
t.is(invitationMail.payload.name, 'MemberInvitation');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should not send invitation email if user setting is not to receive invitation email', async t => {
|
|
|
|
|
const { notificationService, module } = t.context;
|
|
|
|
|
const inviteId = randomUUID();
|
|
|
|
|
await module.create(Mockers.UserSettings, {
|
|
|
|
|
userId: member.id,
|
|
|
|
|
receiveInvitationEmail: false,
|
|
|
|
|
});
|
|
|
|
|
const invitationMailCount = module.mails.count('MemberInvitation');
|
|
|
|
|
const invitationMailCount = module.queue.count('notification.sendMail');
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createInvitation({
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -103,17 +103,19 @@ test('should not send invitation email if user setting is not to receive invitat
|
|
|
|
|
inviteId,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.truthy(notification);
|
|
|
|
|
|
|
|
|
|
// no new invitation email should be sent
|
|
|
|
|
t.is(t.context.module.mails.count('MemberInvitation'), invitationMailCount);
|
|
|
|
|
t.is(module.queue.count('notification.sendMail'), invitationMailCount);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should not create invitation notification if user is already a member', async t => {
|
|
|
|
|
const { notificationService, module } = t.context;
|
|
|
|
|
const { id: inviteId } = await module.create(Mockers.WorkspaceUser, {
|
|
|
|
|
workspaceId: workspace.id,
|
|
|
|
|
userId: member.id,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createInvitation({
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -122,15 +124,16 @@ test('should not create invitation notification if user is already a member', as
|
|
|
|
|
inviteId,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.is(notification, undefined);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should create invitation accepted notification and email', async t => {
|
|
|
|
|
const { notificationService, module } = t.context;
|
|
|
|
|
const { id: inviteId } = await module.create(Mockers.WorkspaceUser, {
|
|
|
|
|
workspaceId: workspace.id,
|
|
|
|
|
userId: member.id,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createInvitationAccepted({
|
|
|
|
|
userId: owner.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -139,6 +142,7 @@ test('should create invitation accepted notification and email', async t => {
|
|
|
|
|
inviteId,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.truthy(notification);
|
|
|
|
|
t.is(notification!.type, NotificationType.InvitationAccepted);
|
|
|
|
|
t.is(notification!.userId, owner.id);
|
|
|
|
|
@@ -147,12 +151,12 @@ test('should create invitation accepted notification and email', async t => {
|
|
|
|
|
t.is(notification!.body.inviteId, inviteId);
|
|
|
|
|
|
|
|
|
|
// should send email
|
|
|
|
|
const invitationAcceptedMail = module.mails.last('MemberAccepted');
|
|
|
|
|
t.is(invitationAcceptedMail.to, owner.email);
|
|
|
|
|
const invitationAcceptedMail = module.queue.last('notification.sendMail');
|
|
|
|
|
t.is(invitationAcceptedMail.payload.to, owner.email);
|
|
|
|
|
t.is(invitationAcceptedMail.payload.name, 'MemberAccepted');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should not send invitation accepted email if user settings is not receive invitation email', async t => {
|
|
|
|
|
const { notificationService, module } = t.context;
|
|
|
|
|
const { id: inviteId } = await module.create(Mockers.WorkspaceUser, {
|
|
|
|
|
workspaceId: workspace.id,
|
|
|
|
|
userId: member.id,
|
|
|
|
|
@@ -162,8 +166,10 @@ test('should not send invitation accepted email if user settings is not receive
|
|
|
|
|
userId: owner.id,
|
|
|
|
|
receiveInvitationEmail: false,
|
|
|
|
|
});
|
|
|
|
|
const invitationAcceptedMailCount =
|
|
|
|
|
t.context.module.mails.count('MemberAccepted');
|
|
|
|
|
const invitationAcceptedMailCount = module.queue.count(
|
|
|
|
|
'notification.sendMail'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createInvitationAccepted({
|
|
|
|
|
userId: owner.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -172,17 +178,19 @@ test('should not send invitation accepted email if user settings is not receive
|
|
|
|
|
inviteId,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.truthy(notification);
|
|
|
|
|
|
|
|
|
|
// no new invitation accepted email should be sent
|
|
|
|
|
t.is(
|
|
|
|
|
t.context.module.mails.count('MemberAccepted'),
|
|
|
|
|
module.queue.count('notification.sendMail'),
|
|
|
|
|
invitationAcceptedMailCount
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should not create invitation accepted notification if user is not an active member', async t => {
|
|
|
|
|
const { notificationService } = t.context;
|
|
|
|
|
const inviteId = randomUUID();
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createInvitationAccepted({
|
|
|
|
|
userId: owner.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -191,12 +199,13 @@ test('should not create invitation accepted notification if user is not an activ
|
|
|
|
|
inviteId,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.is(notification, undefined);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should create invitation blocked notification', async t => {
|
|
|
|
|
const { notificationService } = t.context;
|
|
|
|
|
const inviteId = randomUUID();
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createInvitationBlocked({
|
|
|
|
|
userId: owner.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -205,6 +214,7 @@ test('should create invitation blocked notification', async t => {
|
|
|
|
|
inviteId,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.truthy(notification);
|
|
|
|
|
t.is(notification!.type, NotificationType.InvitationBlocked);
|
|
|
|
|
t.is(notification!.userId, owner.id);
|
|
|
|
|
@@ -214,8 +224,8 @@ test('should create invitation blocked notification', async t => {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should create invitation rejected notification', async t => {
|
|
|
|
|
const { notificationService } = t.context;
|
|
|
|
|
const inviteId = randomUUID();
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createInvitationRejected({
|
|
|
|
|
userId: owner.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -224,6 +234,7 @@ test('should create invitation rejected notification', async t => {
|
|
|
|
|
inviteId,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.truthy(notification);
|
|
|
|
|
t.is(notification!.type, NotificationType.InvitationRejected);
|
|
|
|
|
t.is(notification!.userId, owner.id);
|
|
|
|
|
@@ -233,8 +244,8 @@ test('should create invitation rejected notification', async t => {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should create invitation review request notification if user is not an active member', async t => {
|
|
|
|
|
const { notificationService, module } = t.context;
|
|
|
|
|
const inviteId = randomUUID();
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createInvitationReviewRequest({
|
|
|
|
|
userId: owner.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -243,6 +254,7 @@ test('should create invitation review request notification if user is not an act
|
|
|
|
|
inviteId,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.truthy(notification);
|
|
|
|
|
t.is(notification!.type, NotificationType.InvitationReviewRequest);
|
|
|
|
|
t.is(notification!.userId, owner.id);
|
|
|
|
|
@@ -251,18 +263,19 @@ test('should create invitation review request notification if user is not an act
|
|
|
|
|
t.is(notification!.body.inviteId, inviteId);
|
|
|
|
|
|
|
|
|
|
// should send email
|
|
|
|
|
const invitationReviewRequestMail = module.mails.last(
|
|
|
|
|
'LinkInvitationReviewRequest'
|
|
|
|
|
const invitationReviewRequestMail = module.queue.last(
|
|
|
|
|
'notification.sendMail'
|
|
|
|
|
);
|
|
|
|
|
t.is(invitationReviewRequestMail.to, owner.email);
|
|
|
|
|
t.is(invitationReviewRequestMail.payload.to, owner.email);
|
|
|
|
|
t.is(invitationReviewRequestMail.payload.name, 'LinkInvitationReviewRequest');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should not create invitation review request notification if user is an active member', async t => {
|
|
|
|
|
const { notificationService, module } = t.context;
|
|
|
|
|
const { id: inviteId } = await module.create(Mockers.WorkspaceUser, {
|
|
|
|
|
workspaceId: workspace.id,
|
|
|
|
|
userId: member.id,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createInvitationReviewRequest({
|
|
|
|
|
userId: owner.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -271,15 +284,16 @@ test('should not create invitation review request notification if user is an act
|
|
|
|
|
inviteId,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.is(notification, undefined);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should create invitation review approved notification if user is an active member', async t => {
|
|
|
|
|
const { notificationService, module } = t.context;
|
|
|
|
|
const { id: inviteId } = await module.create(Mockers.WorkspaceUser, {
|
|
|
|
|
workspaceId: workspace.id,
|
|
|
|
|
userId: member.id,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createInvitationReviewApproved(
|
|
|
|
|
{
|
|
|
|
|
userId: member.id,
|
|
|
|
|
@@ -290,6 +304,7 @@ test('should create invitation review approved notification if user is an active
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
t.truthy(notification);
|
|
|
|
|
t.is(notification!.type, NotificationType.InvitationReviewApproved);
|
|
|
|
|
t.is(notification!.userId, member.id);
|
|
|
|
|
@@ -298,19 +313,20 @@ test('should create invitation review approved notification if user is an active
|
|
|
|
|
t.is(notification!.body.inviteId, inviteId);
|
|
|
|
|
|
|
|
|
|
// should send email
|
|
|
|
|
const invitationReviewApprovedMail = t.context.module.mails.last(
|
|
|
|
|
'LinkInvitationApprove'
|
|
|
|
|
const invitationReviewApprovedMail = module.queue.last(
|
|
|
|
|
'notification.sendMail'
|
|
|
|
|
);
|
|
|
|
|
t.is(invitationReviewApprovedMail.to, member.email);
|
|
|
|
|
t.is(invitationReviewApprovedMail.payload.to, member.email);
|
|
|
|
|
t.is(invitationReviewApprovedMail.payload.name, 'LinkInvitationApprove');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should not create invitation review approved notification if user is not an active member', async t => {
|
|
|
|
|
const { notificationService, module } = t.context;
|
|
|
|
|
const { id: inviteId } = await module.create(Mockers.WorkspaceUser, {
|
|
|
|
|
workspaceId: workspace.id,
|
|
|
|
|
userId: member.id,
|
|
|
|
|
status: WorkspaceMemberStatus.Pending,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createInvitationReviewApproved(
|
|
|
|
|
{
|
|
|
|
|
userId: member.id,
|
|
|
|
|
@@ -321,11 +337,11 @@ test('should not create invitation review approved notification if user is not a
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
t.is(notification, undefined);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should create invitation review declined notification if user is not an active member', async t => {
|
|
|
|
|
const { notificationService, module } = t.context;
|
|
|
|
|
const notification = await notificationService.createInvitationReviewDeclined(
|
|
|
|
|
{
|
|
|
|
|
userId: member.id,
|
|
|
|
|
@@ -335,6 +351,7 @@ test('should create invitation review declined notification if user is not an ac
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
t.truthy(notification);
|
|
|
|
|
t.is(notification!.type, NotificationType.InvitationReviewDeclined);
|
|
|
|
|
t.is(notification!.userId, member.id);
|
|
|
|
|
@@ -342,18 +359,19 @@ test('should create invitation review declined notification if user is not an ac
|
|
|
|
|
t.is(notification!.body.createdByUserId, owner.id);
|
|
|
|
|
|
|
|
|
|
// should send email
|
|
|
|
|
const invitationReviewDeclinedMail = module.mails.last(
|
|
|
|
|
'LinkInvitationDecline'
|
|
|
|
|
const invitationReviewDeclinedMail = module.queue.last(
|
|
|
|
|
'notification.sendMail'
|
|
|
|
|
);
|
|
|
|
|
t.is(invitationReviewDeclinedMail.to, member.email);
|
|
|
|
|
t.is(invitationReviewDeclinedMail.payload.to, member.email);
|
|
|
|
|
t.is(invitationReviewDeclinedMail.payload.name, 'LinkInvitationDecline');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should not create invitation review declined notification if user is an active member', async t => {
|
|
|
|
|
const { notificationService, module } = t.context;
|
|
|
|
|
await module.create(Mockers.WorkspaceUser, {
|
|
|
|
|
workspaceId: workspace.id,
|
|
|
|
|
userId: member.id,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createInvitationReviewDeclined(
|
|
|
|
|
{
|
|
|
|
|
userId: owner.id,
|
|
|
|
|
@@ -363,11 +381,11 @@ test('should not create invitation review declined notification if user is an ac
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
t.is(notification, undefined);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should clean expired notifications', async t => {
|
|
|
|
|
const { notificationService } = t.context;
|
|
|
|
|
await notificationService.createInvitation({
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -376,29 +394,35 @@ test('should clean expired notifications', async t => {
|
|
|
|
|
inviteId: randomUUID(),
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let count = await notificationService.countByUserId(member.id);
|
|
|
|
|
t.is(count, 1);
|
|
|
|
|
|
|
|
|
|
// wait for 100 days
|
|
|
|
|
mock.timers.enable({
|
|
|
|
|
apis: ['Date'],
|
|
|
|
|
now: Date.now() + 1000 * 60 * 60 * 24 * 100,
|
|
|
|
|
now: Due.after('100d'),
|
|
|
|
|
});
|
|
|
|
|
await t.context.models.notification.cleanExpiredNotifications();
|
|
|
|
|
|
|
|
|
|
await models.notification.cleanExpiredNotifications();
|
|
|
|
|
|
|
|
|
|
count = await notificationService.countByUserId(member.id);
|
|
|
|
|
t.is(count, 1);
|
|
|
|
|
|
|
|
|
|
mock.timers.reset();
|
|
|
|
|
// wait for 1 year
|
|
|
|
|
mock.timers.enable({
|
|
|
|
|
apis: ['Date'],
|
|
|
|
|
now: Date.now() + 1000 * 60 * 60 * 24 * 365,
|
|
|
|
|
now: Due.after('1y'),
|
|
|
|
|
});
|
|
|
|
|
await t.context.models.notification.cleanExpiredNotifications();
|
|
|
|
|
|
|
|
|
|
await models.notification.cleanExpiredNotifications();
|
|
|
|
|
|
|
|
|
|
count = await notificationService.countByUserId(member.id);
|
|
|
|
|
t.is(count, 0);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should mark notification as read', async t => {
|
|
|
|
|
const { notificationService } = t.context;
|
|
|
|
|
const notification = await notificationService.createInvitation({
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -407,22 +431,20 @@ test('should mark notification as read', async t => {
|
|
|
|
|
inviteId: randomUUID(),
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await notificationService.markAsRead(member.id, notification!.id);
|
|
|
|
|
const updatedNotification = await t.context.models.notification.get(
|
|
|
|
|
notification!.id
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const updatedNotification = await models.notification.get(notification!.id);
|
|
|
|
|
t.is(updatedNotification!.read, true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should throw error on mark notification as read if notification is not found', async t => {
|
|
|
|
|
const { notificationService } = t.context;
|
|
|
|
|
await t.throwsAsync(notificationService.markAsRead(member.id, randomUUID()), {
|
|
|
|
|
instanceOf: NotificationNotFound,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should throw error on mark notification as read if notification user is not the same', async t => {
|
|
|
|
|
const { notificationService, module } = t.context;
|
|
|
|
|
const notification = await notificationService.createInvitation({
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -431,7 +453,9 @@ test('should throw error on mark notification as read if notification user is no
|
|
|
|
|
inviteId: randomUUID(),
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const otherUser = await module.create(Mockers.User);
|
|
|
|
|
|
|
|
|
|
await t.throwsAsync(
|
|
|
|
|
notificationService.markAsRead(otherUser.id, notification!.id),
|
|
|
|
|
{
|
|
|
|
|
@@ -441,8 +465,8 @@ test('should throw error on mark notification as read if notification user is no
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should use latest doc title in mention notification', async t => {
|
|
|
|
|
const { notificationService, models } = t.context;
|
|
|
|
|
const docId = randomUUID();
|
|
|
|
|
|
|
|
|
|
await notificationService.createMention({
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -456,6 +480,7 @@ test('should use latest doc title in mention notification', async t => {
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const mentionNotification = await notificationService.createMention({
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -469,7 +494,9 @@ test('should use latest doc title in mention notification', async t => {
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.truthy(mentionNotification);
|
|
|
|
|
|
|
|
|
|
mock.method(models.doc, 'findMetas', async () => [
|
|
|
|
|
{
|
|
|
|
|
title: 'doc-title-2-updated',
|
|
|
|
|
@@ -478,7 +505,9 @@ test('should use latest doc title in mention notification', async t => {
|
|
|
|
|
title: 'doc-title-1-updated',
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const notifications = await notificationService.findManyByUserId(member.id);
|
|
|
|
|
|
|
|
|
|
t.is(notifications.length, 2);
|
|
|
|
|
const mention = notifications[0];
|
|
|
|
|
t.is(mention.body.workspace!.id, workspace.id);
|
|
|
|
|
@@ -498,8 +527,8 @@ test('should use latest doc title in mention notification', async t => {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should raw doc title in mention notification if no doc found', async t => {
|
|
|
|
|
const { notificationService, models } = t.context;
|
|
|
|
|
const docId = randomUUID();
|
|
|
|
|
|
|
|
|
|
await notificationService.createMention({
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -526,7 +555,9 @@ test('should raw doc title in mention notification if no doc found', async t =>
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mock.method(models.doc, 'findMetas', async () => [null, null]);
|
|
|
|
|
|
|
|
|
|
const notifications = await notificationService.findManyByUserId(member.id);
|
|
|
|
|
t.is(notifications.length, 2);
|
|
|
|
|
const mention = notifications[0];
|
|
|
|
|
@@ -545,8 +576,8 @@ test('should raw doc title in mention notification if no doc found', async t =>
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should send mention email by user setting', async t => {
|
|
|
|
|
const { notificationService, module } = t.context;
|
|
|
|
|
const docId = randomUUID();
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createMention({
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -560,17 +591,21 @@ test('should send mention email by user setting', async t => {
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.truthy(notification);
|
|
|
|
|
|
|
|
|
|
// should send mention email
|
|
|
|
|
const mentionMail = module.mails.last('Mention');
|
|
|
|
|
t.is(mentionMail.to, member.email);
|
|
|
|
|
const mentionMail = module.queue.last('notification.sendMail');
|
|
|
|
|
t.is(mentionMail.payload.to, member.email);
|
|
|
|
|
t.is(mentionMail.payload.name, 'Mention');
|
|
|
|
|
|
|
|
|
|
// update user setting to not receive mention email
|
|
|
|
|
const mentionMailCount = module.mails.count('Mention');
|
|
|
|
|
const mentionMailCount = module.queue.count('notification.sendMail');
|
|
|
|
|
await module.create(Mockers.UserSettings, {
|
|
|
|
|
userId: member.id,
|
|
|
|
|
receiveMentionEmail: false,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await notificationService.createMention({
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -584,12 +619,12 @@ test('should send mention email by user setting', async t => {
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// should not send mention email
|
|
|
|
|
t.is(module.mails.count('Mention'), mentionMailCount);
|
|
|
|
|
t.is(module.queue.count('notification.sendMail'), mentionMailCount);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should send mention email with use client doc title if server doc title is empty', async t => {
|
|
|
|
|
const { notificationService, module } = t.context;
|
|
|
|
|
const docId = randomUUID();
|
|
|
|
|
await module.create(Mockers.DocMeta, {
|
|
|
|
|
workspaceId: workspace.id,
|
|
|
|
|
@@ -597,6 +632,7 @@ test('should send mention email with use client doc title if server doc title is
|
|
|
|
|
// mock empty title
|
|
|
|
|
title: '',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createMention({
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
@@ -610,8 +646,115 @@ test('should send mention email with use client doc title if server doc title is
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.truthy(notification);
|
|
|
|
|
const mentionMail = module.mails.last('Mention');
|
|
|
|
|
t.is(mentionMail.to, member.email);
|
|
|
|
|
t.is(mentionMail.props.doc.title, 'doc-title-1');
|
|
|
|
|
|
|
|
|
|
const mentionMail = module.queue.last('notification.sendMail');
|
|
|
|
|
t.is(mentionMail.payload.to, member.email);
|
|
|
|
|
t.is(mentionMail.payload.name, 'Mention');
|
|
|
|
|
// @ts-expect-error - payload is not typed
|
|
|
|
|
t.is(mentionMail.payload.props.doc.title, 'doc-title-1');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should send comment notification and email', async t => {
|
|
|
|
|
const docId = randomUUID();
|
|
|
|
|
const commentId = randomUUID();
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createComment({
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
workspaceId: workspace.id,
|
|
|
|
|
createdByUserId: owner.id,
|
|
|
|
|
doc: {
|
|
|
|
|
id: docId,
|
|
|
|
|
title: 'doc-title-1',
|
|
|
|
|
mode: DocMode.page,
|
|
|
|
|
},
|
|
|
|
|
commentId,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.truthy(notification);
|
|
|
|
|
|
|
|
|
|
const commentMail = module.queue.last('notification.sendMail');
|
|
|
|
|
t.is(commentMail.payload.to, member.email);
|
|
|
|
|
t.is(commentMail.payload.name, 'Comment');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should send comment mention notification and email', async t => {
|
|
|
|
|
const docId = randomUUID();
|
|
|
|
|
const commentId = randomUUID();
|
|
|
|
|
const replyId = randomUUID();
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createComment(
|
|
|
|
|
{
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
workspaceId: workspace.id,
|
|
|
|
|
createdByUserId: owner.id,
|
|
|
|
|
doc: {
|
|
|
|
|
id: docId,
|
|
|
|
|
title: 'doc-title-1',
|
|
|
|
|
mode: DocMode.page,
|
|
|
|
|
},
|
|
|
|
|
commentId,
|
|
|
|
|
replyId,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
true
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
t.truthy(notification);
|
|
|
|
|
|
|
|
|
|
const commentMentionMail = module.queue.last('notification.sendMail');
|
|
|
|
|
t.is(commentMentionMail.payload.to, member.email);
|
|
|
|
|
t.is(commentMentionMail.payload.name, 'CommentMention');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should send comment email by user setting', async t => {
|
|
|
|
|
const docId = randomUUID();
|
|
|
|
|
|
|
|
|
|
const notification = await notificationService.createComment({
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
workspaceId: workspace.id,
|
|
|
|
|
createdByUserId: owner.id,
|
|
|
|
|
doc: {
|
|
|
|
|
id: docId,
|
|
|
|
|
title: 'doc-title-1',
|
|
|
|
|
mode: DocMode.page,
|
|
|
|
|
},
|
|
|
|
|
commentId: randomUUID(),
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.truthy(notification);
|
|
|
|
|
|
|
|
|
|
const commentMail = module.queue.last('notification.sendMail');
|
|
|
|
|
t.is(commentMail.payload.to, member.email);
|
|
|
|
|
t.is(commentMail.payload.name, 'Comment');
|
|
|
|
|
|
|
|
|
|
// update user setting to not receive comment email
|
|
|
|
|
const commentMailCount = module.queue.count('notification.sendMail');
|
|
|
|
|
await module.create(Mockers.UserSettings, {
|
|
|
|
|
userId: member.id,
|
|
|
|
|
receiveCommentEmail: false,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await notificationService.createComment({
|
|
|
|
|
userId: member.id,
|
|
|
|
|
body: {
|
|
|
|
|
workspaceId: workspace.id,
|
|
|
|
|
createdByUserId: owner.id,
|
|
|
|
|
doc: {
|
|
|
|
|
id: docId,
|
|
|
|
|
title: 'doc-title-2',
|
|
|
|
|
mode: DocMode.page,
|
|
|
|
|
},
|
|
|
|
|
commentId: randomUUID(),
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// should not send comment email
|
|
|
|
|
t.is(module.queue.count('notification.sendMail'), commentMailCount);
|
|
|
|
|
});
|
|
|
|
|
|