Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: add generic notification handler tests #572

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
- run: npm ci
- run: npm run dev:services
- run: . .env.test && npx jest
- run: . .env.test && npx jest --runInBand
- run: docker compose down

gatekeep:
Expand Down
239 changes: 239 additions & 0 deletions src/integration/NotificationOnEditHandler.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
import express from "express"
import request from "supertest"

import { NotificationOnEditHandler } from "@middleware/notificationOnEditHandler"

import UserSessionData from "@classes/UserSessionData"

import {
Notification,
Repo,
Reviewer,
ReviewMeta,
ReviewRequest,
ReviewRequestView,
Site,
SiteMember,
User,
Whitelist,
} from "@database/models"
import { generateRouterForUserWithSite } from "@fixtures/app"
import {
mockEmail,
mockIsomerUserId,
mockSiteName,
} from "@fixtures/sessionData"
import { GitHubService } from "@services/db/GitHubService"
import * as ReviewApi from "@services/db/review"
import { ConfigYmlService } from "@services/fileServices/YmlFileServices/ConfigYmlService"
import { getUsersService, notificationsService } from "@services/identity"
import CollaboratorsService from "@services/identity/CollaboratorsService"
import IsomerAdminsService from "@services/identity/IsomerAdminsService"
import SitesService from "@services/identity/SitesService"
import ReviewRequestService from "@services/review/ReviewRequestService"
import { sequelize } from "@tests/database"

const mockSiteId = "1"
const mockSiteMemberId = "1"

const mockGithubService = {
getPullRequest: jest.fn(),
getComments: jest.fn(),
}
const usersService = getUsersService(sequelize)
const reviewRequestService = new ReviewRequestService(
(mockGithubService as unknown) as typeof ReviewApi,
User,
ReviewRequest,
Reviewer,
ReviewMeta,
ReviewRequestView
)
const sitesService = new SitesService({
siteRepository: Site,
gitHubService: (mockGithubService as unknown) as GitHubService,
configYmlService: (jest.fn() as unknown) as ConfigYmlService,
usersService,
isomerAdminsService: (jest.fn() as unknown) as IsomerAdminsService,
reviewRequestService,
})
const collaboratorsService = new CollaboratorsService({
siteRepository: Site,
siteMemberRepository: SiteMember,
sitesService,
usersService,
whitelist: Whitelist,
})

const notificationsHandler = new NotificationOnEditHandler({
reviewRequestService,
collaboratorsService,
sitesService,
notificationsService,
})

// Set up express with defaults and use the router under test
const router = express()
const dummySubrouter = express()
dummySubrouter.get("/:siteName/test", async (req, res, next) =>
// Dummy subrouter
next()
)
router.use(dummySubrouter)

// We handle the test slightly differently - jest interprets the end of the test as when the response is sent,
// but we normally create a notification after this response, due to the position of the middleware
// the solution to get tests working is to send a response only after the notification middleware
router.use(async (req, res, next) => {
// Inserts notification handler after all other subrouters
// Needs to be awaited so jest doesn't exit prematurely upon receiving response status
await notificationsHandler.createNotification(req as any, res as any, next)
Copy link
Contributor

@kishore03109 kishore03109 Jan 7, 2023

Choose a reason for hiding this comment

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

nit: await has no effect
edit: await/async doesnt work, express doesn’t natively handle promises, and so it doesn’t support async / await
more info here

Copy link
Contributor Author

@alexanderleegs alexanderleegs Jan 11, 2023

Choose a reason for hiding this comment

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

Hmm it seems to be required in the test, probably because jest seems to conclude the test once the status code is received - I've left a comment explaining this in d6c1b3e

res.status(200).send(200)
})
const userSessionData = new UserSessionData({
isomerUserId: mockIsomerUserId,
email: mockEmail,
})
const app = generateRouterForUserWithSite(router, userSessionData, mockSiteName)

describe("Notifications Router", () => {
const mockAdditionalUserId = "2"
const mockAdditionalSiteId = "2"
const mockAdditionalSiteMemberId = "2"
const mockAnotherSiteMemberId = "3"

beforeAll(async () => {
// Mock github service return
mockGithubService.getPullRequest.mockResolvedValue({
title: "title",
body: "body",
changed_files: [],
created_at: new Date(),
})

// We need to force the relevant tables to start from a clean slate
// Otherwise, some tests may fail due to the auto-incrementing IDs
// not starting from 1
await User.sync({ force: true })
await Site.sync({ force: true })
await Repo.sync({ force: true })
await SiteMember.sync({ force: true })
await Notification.sync({ force: true })
await ReviewMeta.sync({ force: true })
await ReviewRequest.sync({ force: true })

// Set up User and Site table entries
await User.create({
id: mockIsomerUserId,
})
await User.create({
id: mockAdditionalUserId,
})
await Site.create({
id: mockSiteId,
name: mockSiteName,
apiTokenName: "token",
jobStatus: "READY",
siteStatus: "LAUNCHED",
creatorId: mockIsomerUserId,
})
await SiteMember.create({
userId: mockIsomerUserId,
siteId: mockSiteId,
role: "ADMIN",
id: mockSiteMemberId,
})
await Repo.create({
name: mockSiteName,
url: "url",
siteId: mockSiteId,
})
await SiteMember.create({
userId: mockAdditionalUserId,
siteId: mockSiteId,
role: "ADMIN",
id: mockAdditionalSiteMemberId,
})
await Site.create({
id: mockAdditionalSiteId,
name: "mockSite2",
apiTokenName: "token",
jobStatus: "READY",
siteStatus: "LAUNCHED",
creatorId: mockIsomerUserId,
})
await SiteMember.create({
userId: mockIsomerUserId,
siteId: mockAdditionalSiteId,
role: "ADMIN",
id: mockAnotherSiteMemberId,
})
await Repo.create({
name: `${mockSiteName}2`,
url: "url",
siteId: mockAdditionalSiteId,
})
})

afterAll(async () => {
await SiteMember.sync({ force: true })
await Site.sync({ force: true })
await User.sync({ force: true })
await Repo.sync({ force: true })
})

describe("createNotification handler", () => {
afterEach(async () => {
// Clean up so that different tests using
// the same notifications don't interfere with each other
await Notification.sync({ force: true })
await ReviewMeta.sync({ force: true })
await ReviewRequest.sync({ force: true })
})
it("should create a new notification when called", async () => {
// Arrange
await ReviewRequest.create({
id: 1,
requestorId: mockIsomerUserId,
siteId: mockSiteId,
reviewStatus: "OPEN",
})
await ReviewMeta.create({
reviewId: 1,
pullRequestNumber: 1,
reviewLink: "test",
})
mockGithubService.getComments.mockResolvedValueOnce([])

// Act
await request(app).get(`/${mockSiteName}/test`)

// Assert
// Notification should be sent to all site members other than the creator
expect(
(
await Notification.findAll({
where: {
userId: mockAdditionalUserId,
siteId: mockSiteId,
siteMemberId: mockAdditionalSiteMemberId,
firstReadTime: null,
},
})
).length
).toEqual(1)
expect(
(
await Notification.findAll({
where: {
userId: mockIsomerUserId,
siteId: mockSiteId,
siteMemberId: mockSiteMemberId,
firstReadTime: null,
},
})
).length
).toEqual(0)
})
})
})
12 changes: 11 additions & 1 deletion src/integration/Notifications.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { ConfigYmlService } from "@root/services/fileServices/YmlFileServices/Co
import CollaboratorsService from "@root/services/identity/CollaboratorsService"
import SitesService from "@root/services/identity/SitesService"
import ReviewRequestService from "@root/services/review/ReviewRequestService"
import * as ReviewApi from "@services/db/review"
import {
getIdentityAuthService,
getUsersService,
Expand All @@ -58,7 +59,7 @@ const identityAuthService = getIdentityAuthService(gitHubService)
const usersService = getUsersService(sequelize)
const configYmlService = new ConfigYmlService({ gitHubService })
const reviewRequestService = new ReviewRequestService(
gitHubService,
(gitHubService as unknown) as typeof ReviewApi,
User,
ReviewRequest,
Reviewer,
Expand Down Expand Up @@ -109,6 +110,15 @@ describe("Notifications Router", () => {
const MOCK_ANOTHER_SITE_MEMBER_ID = "3"

beforeAll(async () => {
// We need to force the relevant tables to start from a clean slate
// Otherwise, some tests may fail due to the auto-incrementing IDs
// not starting from 1
await User.sync({ force: true })
await Site.sync({ force: true })
await Repo.sync({ force: true })
await SiteMember.sync({ force: true })
await Notification.sync({ force: true })

// Set up User and Site table entries
await User.create({
id: mockIsomerUserId,
Expand Down
11 changes: 8 additions & 3 deletions src/integration/Reviews.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { SitesRouter as _SitesRouter } from "@routes/v2/authenticated/sites"

import {
IsomerAdmin,
Notification,
Repo,
Reviewer,
ReviewMeta,
Expand Down Expand Up @@ -70,8 +71,9 @@ import {
import { ReviewRequestStatus } from "@root/constants"
import { ReviewRequestDto } from "@root/types/dto/review"
import { GitHubService } from "@services/db/GitHubService"
import * as ReviewApi from "@services/db/review"
import { ConfigYmlService } from "@services/fileServices/YmlFileServices/ConfigYmlService"
import { getUsersService } from "@services/identity"
import { getUsersService, notificationsService } from "@services/identity"
import CollaboratorsService from "@services/identity/CollaboratorsService"
import IsomerAdminsService from "@services/identity/IsomerAdminsService"
import SitesService from "@services/identity/SitesService"
Expand All @@ -83,7 +85,7 @@ const configYmlService = new ConfigYmlService({ gitHubService })
const usersService = getUsersService(sequelize)
const isomerAdminsService = new IsomerAdminsService({ repository: IsomerAdmin })
const reviewRequestService = new ReviewRequestService(
gitHubService,
(gitHubService as unknown) as typeof ReviewApi,
User,
ReviewRequest,
Reviewer,
Expand All @@ -110,7 +112,8 @@ const ReviewsRouter = new _ReviewsRouter(
reviewRequestService,
usersService,
sitesService,
collaboratorsService
collaboratorsService,
notificationsService
)
const reviewsSubrouter = ReviewsRouter.getRouter()
const subrouter = express()
Expand All @@ -136,6 +139,8 @@ describe("Review Requests Integration Tests", () => {
await Site.sync({ force: true })
await Repo.sync({ force: true })
await SiteMember.sync({ force: true })
await Notification.sync({ force: true })
await ReviewMeta.sync({ force: true })

await User.create(MOCK_USER_DBENTRY_ONE)
await User.create(MOCK_USER_DBENTRY_TWO)
Expand Down
Loading