Skip to content

Commit

Permalink
Feat: add generic notification handler tests (#572)
Browse files Browse the repository at this point in the history
* Feat: add generic notification handler tests

* Fix: router initialisation and cleanup

* fix: update github actions to use runInBand

* Fix: update tests

* Fix: reset database tables before integration tests

* Chore: modify imports

* Nit: test comment spelling

Co-authored-by: Kishore <[email protected]>

* Nit: comments and change var names

Co-authored-by: Kishore <[email protected]>
  • Loading branch information
alexanderleegs and kishore03109 authored Jan 17, 2023
1 parent c61ae69 commit c90055f
Show file tree
Hide file tree
Showing 5 changed files with 306 additions and 8 deletions.
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)
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

0 comments on commit c90055f

Please sign in to comment.