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

Feature/increase test coverage #204

Merged
merged 6 commits into from
Mar 11, 2021
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 index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ declare module "fastify" {
files: (options?: busboy.BusboyConfig) => AsyncIterableIterator<Multipart>

// Disk mode
saveRequestFiles: (options?: busboy.BusboyConfig) => Promise<Array<Multipart>>
saveRequestFiles: (options?: busboy.BusboyConfig & { tmpdir: string }) => Promise<Array<Multipart>>
cleanRequestFiles: () => Promise<void>
tmpUploads: Array<Multipart>
}
Expand Down
9 changes: 3 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,6 @@ function fastifyMultipart (fastify, options, done) {
})
}

let throwFileSizeLimit = true
if (typeof options.throwFileSizeLimit === 'boolean') {
throwFileSizeLimit = options.throwFileSizeLimit
}

const PartsLimitError = createError('FST_PARTS_LIMIT', 'reach parts limit', 413)
const FilesLimitError = createError('FST_FILES_LIMIT', 'reach files limit', 413)
const FieldsLimitError = createError('FST_FIELDS_LIMIT', 'reach fields limit', 413)
Expand Down Expand Up @@ -402,6 +397,7 @@ function fastifyMultipart (fastify, options, done) {
return
}

let throwFileSizeLimit = true
if (typeof opts.throwFileSizeLimit === 'boolean') {
throwFileSizeLimit = opts.throwFileSizeLimit
}
Expand Down Expand Up @@ -466,11 +462,12 @@ function fastifyMultipart (fastify, options, done) {

async function saveRequestFiles (options) {
const requestFiles = []
const tmpdir = (options && options.tmpdir) || os.tmpdir()

const files = await this.files(options)
this.tmpUploads = []
for await (const file of files) {
const filepath = path.join(os.tmpdir(), toID() + path.extname(file.filename))
const filepath = path.join(tmpdir, toID() + path.extname(file.filename))
const target = createWriteStream(filepath)
try {
await pump(file.file, target)
Expand Down
82 changes: 82 additions & 0 deletions test/legacy/multipart.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,88 @@ test('should error if it is not multipart', { skip: process.platform === 'win32'
})
})

test('should error if handler is not a function', { skip: process.platform === 'win32' }, function (t) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you know why these two tests wouldn't work on windows? Also, is there a reason why you're adding tests to this legacy folder? The name suggests these are old tests, I'm assuming new tests should be written outside of this folder perhaps.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I assume it is connected to already existing issue where tests are failing on windows for the legacy api #110.
As per readme the legacy api is not compatible with windows.

The legacy use cases also take part in the test coverage computation. I assume the folder is named legacy to group the tests for the legacy callback api. In this test we are covering the error that happens in the function handleLegacyMultipartApi.

Copy link
Contributor

Choose a reason for hiding this comment

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

Understood, I didn't know there was a legacy API, this explains it.

t.plan(3)

const fastify = Fastify()

t.tearDown(fastify.close.bind(fastify))
fastify.register(multipart)

fastify.post('/', function (req, reply) {
const handler = null

req.multipart(handler, function (err) {
t.ok(err)
reply.code(500).send()
})
})

fastify.listen(0, function () {
// request
const form = new FormData()
const opts = {
protocol: 'http:',
hostname: 'localhost',
port: fastify.server.address().port,
path: '/',
headers: form.getHeaders(),
method: 'POST'
}

const req = http.request(opts, (res) => {
res.resume()
res.on('end', () => {
t.equal(res.statusCode, 500)
t.pass('res ended successfully')
})
})
pump(form, req, function (err) {
t.error(err, 'client pump: no err')
})
})
})

test('should error if callback is not a function', { skip: process.platform === 'win32' }, function (t) {
t.plan(3)

const fastify = Fastify()

t.tearDown(fastify.close.bind(fastify))
fastify.register(multipart)

fastify.post('/', function (req) {
const callback = null
req.multipart(handler, callback)

function handler () {}
})

fastify.listen(0, function () {
// request
const form = new FormData()
const opts = {
protocol: 'http:',
hostname: 'localhost',
port: fastify.server.address().port,
path: '/',
headers: form.getHeaders(),
method: 'POST'
}

const req = http.request(opts, (res) => {
res.resume()
res.on('end', () => {
t.equal(res.statusCode, 500)
t.pass('res ended successfully')
})
})
pump(form, req, function (err) {
t.error(err, 'client pump: no err')
})
})
})

test('should error if it is invalid multipart', { skip: process.platform === 'win32' }, function (t) {
t.plan(5)

Expand Down
99 changes: 99 additions & 0 deletions test/multipart-disk.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const { Readable } = require('readable-stream')
const path = require('path')
const fs = require('fs')
const { access } = require('fs').promises
const rimraf = require('rimraf')
const stream = require('stream')
const EventEmitter = require('events')
const { once } = EventEmitter
Expand Down Expand Up @@ -184,6 +185,104 @@ test('should throw on file limit error', async function (t) {
}
})

test('should throw on file save error', async function (t) {
t.plan(2)

const fastify = Fastify()
t.tearDown(fastify.close.bind(fastify))

fastify.register(require('..'))

fastify.post('/', async function (req, reply) {
t.ok(req.isMultipart())

try {
await req.saveRequestFiles({ tmpdir: 'something' })
reply.code(200).send()
} catch (error) {
reply.code(500).send()
}
})

await fastify.listen(0)

// request
const form = new FormData()
const opts = {
protocol: 'http:',
hostname: 'localhost',
port: fastify.server.address().port,
path: '/',
headers: form.getHeaders(),
method: 'POST'
}
const req = http.request(opts)
const readStream = fs.createReadStream(filePath)
form.append('upload', readStream)

pump(form, req)

try {
const [res] = await once(req, 'response')
t.equal(res.statusCode, 500)
res.resume()
await once(res, 'end')
} catch (error) {
t.error(error, 'request')
}
})

test('should not throw on request files cleanup error', { skip: process.platform === 'win32' }, async function (t) {
Copy link
Contributor

Choose a reason for hiding this comment

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

same question for this test. it doesn't seem to be doing anything that wouldn't work on windows

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason for having this test skip the Windows platform is different.

I think the test is flaky on Windows. It failed on our windows CI with node 12.x but it ran successfully on a Windows 10 laptop with node 12.x. (I can't prove that, you'll have to trust me :) )
My theory is that this could be similar to the Timing Caveat on windows described in node-tap documentation where removal of the tempdir is asynchronous. tapjs/tapjs#677

In the test we are using rimraf which has a known issue that could be causing this: isaacs/rimraf#72

Copy link
Contributor

Choose a reason for hiding this comment

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

Makes sense, thanks for clarifying.

Copy link
Member

Choose a reason for hiding this comment

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

you can remove rimraf. recursive folder removal is now part of Node.js. See https://nodejs.org/api/fs.html#fs_fs_rmdirsync_path_options and others.

Copy link
Contributor

Choose a reason for hiding this comment

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

This module still supports Node 10 though

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, recursive is not supported in Node 10.

t.plan(2)

const fastify = Fastify()
t.tearDown(fastify.close.bind(fastify))

fastify.register(require('..'))

const tmpdir = t.testdir()

fastify.post('/', async function (req, reply) {
t.ok(req.isMultipart())

try {
await req.saveRequestFiles({ tmpdir })
// temp file saved, remove before the onResponse hook
rimraf.sync(tmpdir)
reply.code(200).send()
} catch (error) {
reply.code(500).send()
}
})

await fastify.listen(0)

// request
const form = new FormData()
const opts = {
protocol: 'http:',
hostname: 'localhost',
port: fastify.server.address().port,
path: '/',
headers: form.getHeaders(),
method: 'POST'
}
const req = http.request(opts)
const readStream = fs.createReadStream(filePath)
form.append('upload', readStream)

pump(form, req)

try {
const [res] = await once(req, 'response')
t.equal(res.statusCode, 200)
res.resume()
await once(res, 'end')
} catch (error) {
t.error(error, 'request')
}
})

test('should throw on file limit error, after highWaterMark', async function (t) {
t.plan(5)

Expand Down