Skip to content

Commit

Permalink
test(adminsubmissionservice): unit tests for download methods (#2129)
Browse files Browse the repository at this point in the history
* test(adminsubmissionsservice): adds tests for download related methoids

* test(adminsubmissionsservice): adds new test case for when sdk returns null
  • Loading branch information
seaerchin authored Jun 10, 2021
1 parent c2fd684 commit 9dcdaf1
Showing 1 changed file with 144 additions and 11 deletions.
155 changes: 144 additions & 11 deletions src/public/services/__tests__/AdminSubmissionsService.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import { decode } from '@stablelib/base64'
import axios from 'axios'
import JSZip from 'jszip'
import { mocked } from 'ts-jest/utils'

import { SubmissionMetadataList } from 'src/types'

import {
countFormSubmissions,
getEncryptedResponse,
getSubmissionMetadataById,
getSubmissionsMetadataByPage,
} from '../AdminSubmissionsService'
import * as AdminSubmissionService from '../AdminSubmissionsService'
import * as formsSdk from '../FormSgSdkService'
import { ADMIN_FORM_ENDPOINT } from '../UpdateFormService'

jest.mock('axios')
const MockAxios = mocked(axios, true)

jest.mock('../FormSgSdkService')

const mockFormSgSdk = mocked(formsSdk.FormSgSdk, true)

jest.mock('@stablelib/base64')
const mockDecodeBase64 = mocked(decode)

describe('AdminSubmissionsService', () => {
describe('countFormSubmissions', () => {
const MOCK_FORM_ID = 'mock–form-id'
Expand All @@ -25,7 +30,7 @@ describe('AdminSubmissionsService', () => {
MockAxios.get.mockResolvedValueOnce({ data: 123 })

// Act
const actual = await countFormSubmissions({
const actual = await AdminSubmissionService.countFormSubmissions({
formId: MOCK_FORM_ID,
dates: { startDate: MOCK_START_DATE, endDate: MOCK_END_DATE },
})
Expand All @@ -48,7 +53,7 @@ describe('AdminSubmissionsService', () => {
MockAxios.get.mockResolvedValueOnce({ data: 123 })

// Act
const actual = await countFormSubmissions({
const actual = await AdminSubmissionService.countFormSubmissions({
formId: MOCK_FORM_ID,
})

Expand Down Expand Up @@ -79,7 +84,7 @@ describe('AdminSubmissionsService', () => {
MockAxios.get.mockResolvedValueOnce({ data: MOCK_RESPONSE })

// Act
const actual = await getSubmissionsMetadataByPage({
const actual = await AdminSubmissionService.getSubmissionsMetadataByPage({
formId: MOCK_FORM_ID,
pageNum: MOCK_PAGE_NUM,
})
Expand Down Expand Up @@ -116,7 +121,7 @@ describe('AdminSubmissionsService', () => {
MockAxios.get.mockResolvedValueOnce({ data: MOCK_RESPONSE })

// Act
const actual = await getSubmissionMetadataById({
const actual = await AdminSubmissionService.getSubmissionMetadataById({
formId: MOCK_FORM_ID,
submissionId: MOCK_SUBMISSION_ID,
})
Expand Down Expand Up @@ -150,7 +155,7 @@ describe('AdminSubmissionsService', () => {
MockAxios.get.mockResolvedValueOnce({ data: MOCK_RESPONSE })

// Act
const actual = await getEncryptedResponse({
const actual = await AdminSubmissionService.getEncryptedResponse({
formId: MOCK_FORM_ID,
submissionId: MOCK_SUBMISSION_ID,
})
Expand All @@ -162,4 +167,132 @@ describe('AdminSubmissionsService', () => {
)
})
})

describe('downloadAndDecryptAttachment', () => {
const MOCK_URL = 'www.decryptme.com'
const MOCK_SECRET_KEY = 'this is a secret'
const MOCK_PUBLIC_KEY = 'some public key'
const MOCK_NONCE = 'use me once and throw away pls'
const MOCK_BINARY =
'01101001 00100000 01100001 01101101 00100000 01100001 00100000 01101000 01110101 01101101 01100001 01101110'

it('should decrypt successfully when there is data', async () => {
// Arrange
mockFormSgSdk.crypto.decryptFile.mockReturnValueOnce('great decryption')
const MOCK_ENCRYPTED_ATTACHMENT = {
encryptedFile: {
submissionPublicKey: MOCK_PUBLIC_KEY,
nonce: MOCK_NONCE,
binary: MOCK_BINARY,
},
}
const MOCK_ARRAY = Uint8Array.from([1, 2, 3, 4])
MockAxios.get.mockResolvedValueOnce({ data: MOCK_ENCRYPTED_ATTACHMENT })
mockDecodeBase64.mockReturnValueOnce(MOCK_ARRAY)

// Act
const actual = await AdminSubmissionService.downloadAndDecryptAttachment(
MOCK_URL,
MOCK_SECRET_KEY,
)

// Arrange
expect(mockDecodeBase64).toHaveBeenCalledWith(MOCK_BINARY)
expect(mockFormSgSdk.crypto.decryptFile).toHaveBeenCalledWith(
MOCK_SECRET_KEY,
MOCK_ENCRYPTED_ATTACHMENT.encryptedFile,
)
expect(actual).toBe('great decryption')
})

it('should return null when the sdk returns null', async () => {
// Arrange
mockFormSgSdk.crypto.decryptFile.mockResolvedValueOnce(null)
const MOCK_ENCRYPTED_ATTACHMENT = {
encryptedFile: {
submissionPublicKey: MOCK_PUBLIC_KEY,
nonce: MOCK_NONCE,
binary: MOCK_BINARY,
},
}
const MOCK_ARRAY = Uint8Array.from([1, 2, 3, 4])
MockAxios.get.mockResolvedValueOnce({ data: MOCK_ENCRYPTED_ATTACHMENT })
mockDecodeBase64.mockReturnValueOnce(MOCK_ARRAY)

// Act
const actual = await AdminSubmissionService.downloadAndDecryptAttachment(
MOCK_URL,
MOCK_SECRET_KEY,
)

// Arrange
expect(mockDecodeBase64).toHaveBeenCalledWith(MOCK_BINARY)
expect(mockFormSgSdk.crypto.decryptFile).toHaveBeenCalledWith(
MOCK_SECRET_KEY,
MOCK_ENCRYPTED_ATTACHMENT.encryptedFile,
)
expect(actual).toBe(null)
})
})

describe('downloadAndDecryptAttachmentsAsZip', () => {
const MOCK_DOWNLOAD_URLS = new Map([
[1, { url: 'something', filename: '1' }],
[2, { url: 'something', filename: '1' }],
])
const MOCK_SECRET_KEY = 'keep this secret'
const MOCK_ARRAY = Uint8Array.from([1, 2, 3])

it('should return the zipfile when downloads are successful', async () => {
// Arrange
const decryptSpy = jest.spyOn(
AdminSubmissionService,
'downloadAndDecryptAttachment',
)
decryptSpy.mockResolvedValue(MOCK_ARRAY)
const MOCK_ZIP = new JSZip()
const zipPromises = Array.from(MOCK_DOWNLOAD_URLS).map(
async ([questionNum, { filename }]) => {
const fileName = `Question ${questionNum} - ${filename}`
return MOCK_ZIP.file(fileName, MOCK_ARRAY)
},
)
const expected = await Promise.all(zipPromises).then(() => {
return MOCK_ZIP.generateAsync({ type: 'blob' })
})

// Act
const actual =
await AdminSubmissionService.downloadAndDecryptAttachmentsAsZip(
MOCK_DOWNLOAD_URLS,
MOCK_SECRET_KEY,
)

// Assert
// Check that function is called with correct params for each element in the map
MOCK_DOWNLOAD_URLS.forEach(({ url }) => {
expect(decryptSpy).toHaveBeenCalledWith(url, MOCK_SECRET_KEY)
})
expect(actual).toEqual(expected)
})

it('should reject when any single download failed', async () => {
// Arrange
const decryptSpy = jest.spyOn(
AdminSubmissionService,
'downloadAndDecryptAttachment',
)
// NOTE: 2 urls but only 1 single rejection
decryptSpy.mockRejectedValueOnce('oh no i failed')

// Act
const actual = AdminSubmissionService.downloadAndDecryptAttachmentsAsZip(
MOCK_DOWNLOAD_URLS,
MOCK_SECRET_KEY,
)

// Assert
await expect(actual).rejects.toEqual('oh no i failed')
})
})
})

0 comments on commit 9dcdaf1

Please sign in to comment.