Skip to content

Commit

Permalink
Helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
flurdy committed May 13, 2024
1 parent 7f44633 commit 07e2000
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 86 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ coverage
.png
/allure-report
/allure-results

.envrc
24 changes: 24 additions & 0 deletions src/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,30 @@ const config = convict({
format: String,
default: 'http://localhost:7337',
env: 'CDP_UPLOADER_BASE_URL'
},
smokeTestBucket: {
doc: 'S3 bucket for test uploads',
format: String,
default: 'cdp-uploader-smoke-test-bucket',
env: 'UPLOADER_SMOKE_TEST_BUCKET'
},
uploadScanInterval: {
doc: 'How long to delay each poll for scan completion',
format: Number,
default: 2000,
env: 'UPLOAD_SCAN_INTERVAL'
},
uploadMaxAttempts: {
doc: 'How many times to poll for scan completion',
format: Number,
default: 30,
env: 'UPLOAD_MAX_ATTEMPTS'
},
uploadScanTimeout: {
doc: 'How long to poll for scan completion',
format: Number,
default: 1000 * 60,
env: 'UPLOAD_SCAN_TIMEOUT'
}
})

Expand Down
38 changes: 38 additions & 0 deletions src/helpers/upload-helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import FormData from 'form-data'
import { readFile } from 'node:fs/promises'

import {
initiateUpload,
uploadFile,
uploadStatus
} from '~/src/helpers/uploader-fetch'

async function createPayload(filename) {
const file = await readFile(`./${filename}`)
const payload = new FormData()
payload.append('field1', 'val1')
payload.append('field2', 'val2')
payload.append('file1', file, filename)
return payload
}

async function findFileDetails(statusUrl) {
const { uploadDetails } = await uploadStatus(statusUrl)
expect(uploadDetails.files).toBeDefined()
expect(uploadDetails.files.length).toEqual(1)
return {
rejectedFiles: uploadDetails.numberOfRejectedFiles,
uploadStatus: uploadDetails.uploadStatus,
fileDetails: uploadDetails?.files[0]
}
}

async function initiateAndUpload(filename, initiatePayload) {
const { uploadId, uploadUrl, statusUrl } =
await initiateUpload(initiatePayload)
const payload = await createPayload(filename)
const { uploadStatusCode, location } = await uploadFile(uploadUrl, payload)
return { uploadId, uploadStatusCode, statusUrl, location }
}

export { createPayload, findFileDetails, initiateAndUpload }
45 changes: 45 additions & 0 deletions src/helpers/uploader-fetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import fetch from 'node-fetch'

import { config } from '~/src/config'

const uploaderBaseUrl = config.get('uploaderBaseUrl')

async function initiateUpload(payload) {
return await fetch(`${uploaderBaseUrl}/initiate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
.then((response) => response.json())
.then((body) => {
return {
uploadId: body.uploadId,
uploadUrl: body.uploadAndScanUrl,
statusUrl: body.statusUrl
}
})
}
async function uploadFile(uploadUrl, payload) {
return await fetch(uploadUrl, {
method: 'POST',
redirect: 'manual',
body: payload
}).then((response) => {
return {
uploadStatusCode: response.status,
location: response.headers.get('location')
}
})
}

async function uploadStatus(statusUrl) {
const response = await fetch(statusUrl, { method: 'GET' })
const payload = await response.json()
return {
uploadDetails: payload
}
}

export { initiateUpload, uploadFile, uploadStatus }
114 changes: 28 additions & 86 deletions src/test/specs/upload-file/upload-journey.test.js
Original file line number Diff line number Diff line change
@@ -1,96 +1,33 @@
import { validate } from 'uuid'
import fetch from 'node-fetch'
import FormData from 'form-data'
import { readFile } from 'node:fs/promises'
import { setTimeout } from 'node:timers/promises'

import { config } from '~/src/config'
import { initiateUpload, uploadStatus } from '~/src/helpers/uploader-fetch'
import {
findFileDetails,
initiateAndUpload
} from '~/src/helpers/upload-helpers'

const uploaderBaseUrl = config.get('uploaderBaseUrl')

const destinationBucket = config.get('smokeTestBucket')
const destinationPath = 'smoke-test'
const scanTimeout = config.get('uploadScanTimeout')
const pollInterval = config.get('uploadScanInterval')
const maxAttempts = config.get('uploadMaxAttempts')
const cleanFilename = 'unicorn-small.jpg'
const virusFilename = 'unicorn-virus.jpg'
const redirectUrl = 'http://httpstat.us/200'

const initiatePayload = {
redirect: redirectUrl,
destinationBucket: 'my-bucket',
destinationPath: 'my-uploads'
}
const scanTimeout = 1000 * 60
const maxAttempts = 20
const pollInterval = 1000
const cleanFilename = 'unicorn-small.jpg'
const virusFilename = 'unicorn-virus.jpg'

async function initiateUpload() {
return await fetch(`${uploaderBaseUrl}/initiate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(initiatePayload)
})
.then((response) => response.json())
.then((body) => {
return {
uploadId: body.uploadId,
uploadUrl: body.uploadAndScanUrl,
statusUrl: body.statusUrl
}
})
}

async function createPayload(filename) {
const file = await readFile(`./${filename}`)
const payload = new FormData()
payload.append('field1', 'val1')
payload.append('field2', 'val2')
payload.append('file1', file, filename)
return payload
}

async function uploadFile(uploadUrl, payload) {
return await fetch(uploadUrl, {
method: 'POST',
redirect: 'manual',
body: payload
}).then((response) => {
return {
uploadStatusCode: response.status,
location: response.headers.get('location')
}
})
}

async function uploadStatus(statusUrl) {
const response = await fetch(statusUrl, { method: 'GET' })
const payload = await response.json()
return {
uploadDetails: payload
}
}

async function findFileDetails(statusUrl) {
const { uploadDetails } = await uploadStatus(statusUrl)
expect(uploadDetails.files).toBeDefined()
expect(uploadDetails.files.length).toEqual(1)
return {
rejectedFiles: uploadDetails.numberOfRejectedFiles,
uploadStatus: uploadDetails.uploadStatus,
fileDetails: uploadDetails?.files[0]
}
}

async function initiateAndUpload(filename) {
const { uploadId, uploadUrl, statusUrl } = await initiateUpload()
const payload = await createPayload(filename)
const { uploadStatusCode, location } = await uploadFile(uploadUrl, payload)
expect(uploadStatusCode).toEqual(302)
return { uploadId, statusUrl, location }
destinationBucket,
destinationPath
}

describe('CDP File uploader Smoke Test', () => {
it('should initiate a file upload', async () => {
const { uploadId, uploadUrl, statusUrl } = await initiateUpload()
const { uploadId, uploadUrl, statusUrl } =
await initiateUpload(initiatePayload)
expect(validate(uploadId)).toBeTruthy()
expect(uploadUrl).toMatch(`${uploaderBaseUrl}/upload-and-scan/${uploadId}`)
expect(statusUrl).toMatch(`${uploaderBaseUrl}/status/${uploadId}`)
Expand All @@ -100,8 +37,9 @@ describe('CDP File uploader Smoke Test', () => {

describe('Start file upload journey', () => {
it('should upload a file', async () => {
const { uploadId, statusUrl, location } =
await initiateAndUpload(cleanFilename)
const { uploadId, uploadStatusCode, statusUrl, location } =
await initiateAndUpload(cleanFilename, initiatePayload)
expect(uploadStatusCode).toEqual(302)
expect(location).toEqual(`${redirectUrl}?uploadId=${uploadId}`)
const { uploadStatus, fileDetails } = await findFileDetails(statusUrl)
expect(uploadStatus).toBeDefined()
Expand All @@ -110,14 +48,17 @@ describe('CDP File uploader Smoke Test', () => {
expect(fileDetails.filename).toEqual(cleanFilename)
// Virus scanning may already be complete
expect(['pending', 'ready']).toContain(uploadStatus)
expect(['pending', 'ready']).toContain(fileDetails.fileStatus)
expect(['pending', 'complete']).toContain(fileDetails.fileStatus)
})

describe('Checking file scanning', () => {
jest.setTimeout(scanTimeout)

it('should get scanned as clean', async () => {
const { statusUrl } = await initiateAndUpload(cleanFilename)
const { statusUrl } = await initiateAndUpload(
cleanFilename,
initiatePayload
)
let isUploadReady = false
let attempts = 0
do {
Expand All @@ -139,11 +80,12 @@ describe('CDP File uploader Smoke Test', () => {

// This depends on the test harness being able to serve the virus file
it('should get rejected as infected', async () => {
const { statusUrl } = await initiateAndUpload(virusFilename)
const { statusUrl } = await initiateAndUpload(
virusFilename,
initiatePayload
)
let isUploadReady = false
let attempts = 0
const maxAttempts = 20
const pollInterval = 1000
do {
const { uploadStatus, fileDetails } = await findFileDetails(statusUrl)
if (uploadStatus === 'ready' && fileDetails.fileStatus !== 'pending') {
Expand Down

0 comments on commit 07e2000

Please sign in to comment.