Files
speckle-server/packages/server/modules/activitystream/tests/activitySummary.spec.ts
T
Gergő Jedlicska d1d5984e30 gergo/summaryEmails (#979)
* refactor(server emails): email transports module refactor to TypeScript

* refactor(docker-compose deps): move local email server to common dev compose file

* chore(server launch.json): add ts-node script running example

* chore(server deps): add nodemailer types package

* refactor(server activitystream): add strongly typed activity definitions

* feat(server activitystream): add activity repository

* feat(server info): add canonical url on the service level

* feat(server): add static file serving route to server core

* feat(server): add dependencies for periodical email digests

* feat(server activity stream): call the initialization step from the activity stream module

* feat(server activity digest): add WIP weekly email digest implementation

* feat(server digest email): smul upgrades and fixes to the email template and its contents

* just for Fabs to test

* chore(root package.json): remove deleted docker-compose references

* feat(frontend profile): add notification preferences panel

* feat(server digest emails): set prod ready cron tab and timespan

* refactor(server email digest): move templates into the email module

* refactor(server activity digests): refactor to use notifications infrastructure

* test(server activities): add tests and some refactor to activities and notification preferences

* refactor(notification preferences): fix minor issues

* test(server notification preferences test): fix describe nesting

* fix(server activities): add missing action types

* fix(server activities): fix errors after merging main

* test(server activity notifications): add test coverage for activity notifications service

* refactor(server activities): fixing tests and some cleanup

* feat(server cli): add summary notification command to cli

* chore(dev env db versions): upgrade local dev env versions

* chore(server deps): upgrade local dev db to pg 14

* fix(docker-compose): bind maildev to localhost

* process-scoped notifications test queues

* test(activity tests): add  sleep to fix flaky CI

* feat(activity digests): add demo date for digest trigger

* feat(activity digest): add UK timezone trigger date

Co-authored-by: Iain Sproat <68657+iainsproat@users.noreply.github.com>
Co-authored-by: Fabians <fabis94@live.com>
2022-09-09 12:46:57 +02:00

175 lines
5.0 KiB
TypeScript

import { truncateTables } from '@/test/hooks'
import { BasicTestUser, createTestUsers } from '@/test/authHelper'
import { StreamActivity, Users } from '@/modules/core/dbSchema'
import {
createActivitySummary,
sendActivityNotifications
} from '@/modules/activitystream/services/summary'
import { expect } from 'chai'
import { createStream, deleteStream } from '@/modules/core/services/streams'
import { saveActivity } from '@/modules/activitystream/services'
import { ActionTypes, ResourceTypes } from '@/modules/activitystream/helpers/types'
import {
ActivityDigestMessage,
NotificationType,
NotificationTypeMessageMap
} from '@/modules/notifications/helpers/types'
import { sleep } from '@/test/helpers'
const cleanup = async () => {
await truncateTables([StreamActivity.name, Users.name])
}
describe('Activity summary @activity', () => {
const userA: BasicTestUser = {
name: 'd1',
email: 'd.1@speckle.systems',
id: ''
}
before(async () => {
await cleanup()
await createTestUsers([userA])
})
describe('create activity summary', () => {
it('returns null for non existing users', async () => {
const summary = await createActivitySummary(
'notAUserId',
['someStreamIds'],
new Date(),
new Date()
)
expect(summary).to.be.null
})
it('no activity returns empty summary', async () => {
const start = new Date()
const streamIds = await Promise.all(
[{ name: 'foo' }, { name: 'bar' }].map(async (stream) =>
createStream({ ...stream, ownerId: userA.id })
)
)
const summary = await createActivitySummary(
userA.id,
streamIds,
start,
new Date()
)
expect(summary?.streamActivities).to.have.length(0)
})
it('gets activities for the user', async () => {
const start = new Date()
const streamIds = await Promise.all(
[{ name: 'foo' }, { name: 'bar' }].map(async (stream) =>
createStream({ ...stream, ownerId: userA.id })
)
)
await saveActivity({
streamId: streamIds[0],
resourceType: ResourceTypes.Stream,
resourceId: streamIds[0],
actionType: ActionTypes.Stream.Create,
userId: userA.id,
info: {},
message: 'foo'
})
await sleep(100)
const summary = await createActivitySummary(
userA.id,
streamIds,
start,
new Date()
)
expect(summary?.streamActivities).to.have.length(1)
})
it('if stream is deleted, activity summary returns with null as stream value', async () => {
const start = new Date()
const [streamId] = await Promise.all(
[{ name: 'foo' }].map(async (stream) =>
createStream({ ...stream, ownerId: userA.id })
)
)
await saveActivity({
streamId,
resourceType: ResourceTypes.Stream,
resourceId: streamId,
actionType: ActionTypes.Stream.Create,
userId: userA.id,
info: {},
message: 'foo'
})
await deleteStream({ streamId })
const summary = await createActivitySummary(
userA.id,
[streamId],
start,
new Date()
)
expect(summary?.streamActivities).to.have.length(1)
expect(summary?.streamActivities[0].stream).to.be.null
})
})
type NotificationMessage<T extends NotificationType> = {
type: T
params: Omit<NotificationTypeMessageMap[T], 'type'>
}
class FakeNotificationPublisher {
notifications: NotificationMessage<NotificationType>[] = []
async publish<T extends NotificationType>(
type: T,
params: Omit<NotificationTypeMessageMap[T], 'type'>
): Promise<string | number> {
this.notifications.push({ type, params })
return this.notifications.length
}
constructor() {
this.notifications = []
}
}
describe('send activity notifications', () => {
it('sends no notifications if there are no active streams', async () => {
const notificationPublisher = new FakeNotificationPublisher()
await sendActivityNotifications(
new Date(),
new Date(),
notificationPublisher.publish.bind(notificationPublisher),
async () => []
)
expect(notificationPublisher.notifications.length).to.equal(0)
})
it('for each UserStream a notification is sent', async () => {
const userStreams = [
{ userId: 'foo', streamIds: ['bar'] },
{ userId: 'boo', streamIds: ['tic', 'tac', 'toe'] }
]
const notificationPublisher = new FakeNotificationPublisher()
await sendActivityNotifications(
new Date(),
new Date(),
notificationPublisher.publish.bind(notificationPublisher),
async () => userStreams
)
expect(
notificationPublisher.notifications
.map((n) => n.params)
.filter((p): p is ActivityDigestMessage => true)
.map((n) => ({
userId: n.targetUserId,
streamIds: n.data.streamIds
}))
).to.be.deep.equalInAnyOrder(userStreams)
})
})
})