2022-03-17 15:09:22 +00:00
|
|
|
import fsSync from 'fs'
|
|
|
|
import fs from 'fs/promises'
|
|
|
|
import formidable from 'formidable'
|
|
|
|
import { Eltro as t, assert, stub } from 'eltro'
|
|
|
|
import { setTimeout } from 'timers/promises'
|
|
|
|
import { Flaska, JsonHandler, FormidableHandler } from '../flaska.mjs'
|
2021-10-09 00:12:56 +00:00
|
|
|
import Client from './client.mjs'
|
|
|
|
|
|
|
|
const port = 51024
|
2022-03-17 15:09:22 +00:00
|
|
|
const log = {
|
|
|
|
fatal: stub(),
|
|
|
|
error: stub(),
|
|
|
|
warn: stub(),
|
|
|
|
info: stub(),
|
|
|
|
debug: stub(),
|
|
|
|
trace: stub(),
|
|
|
|
log: stub(),
|
|
|
|
}
|
|
|
|
const flaska = new Flaska({ log })
|
2021-10-09 00:12:56 +00:00
|
|
|
const client = new Client(port)
|
|
|
|
|
2022-03-16 10:41:25 +00:00
|
|
|
let reqBody = null
|
2022-03-17 15:09:22 +00:00
|
|
|
let file = null
|
|
|
|
let uploaded = []
|
2022-03-16 10:41:25 +00:00
|
|
|
|
2021-10-09 00:12:56 +00:00
|
|
|
flaska.get('/', function(ctx) {
|
|
|
|
ctx.body = { status: true }
|
|
|
|
})
|
2022-03-17 15:09:22 +00:00
|
|
|
flaska.post('/json', JsonHandler(), function(ctx) {
|
2022-03-16 10:41:25 +00:00
|
|
|
ctx.body = { success: true }
|
2022-03-17 15:09:22 +00:00
|
|
|
reqBody = ctx.req.body
|
2022-03-16 10:41:25 +00:00
|
|
|
})
|
2022-03-17 15:09:22 +00:00
|
|
|
flaska.get('/timeout', function(ctx) {
|
|
|
|
return new Promise(function() {})
|
|
|
|
})
|
|
|
|
flaska.get('/file', function(ctx) {
|
|
|
|
file = fsSync.createReadStream('./test/test.png')
|
|
|
|
ctx.body = file
|
|
|
|
})
|
|
|
|
flaska.post('/file/upload', FormidableHandler(formidable, {
|
|
|
|
uploadDir: './test/upload'
|
|
|
|
}), function(ctx) {
|
|
|
|
uploaded.push(ctx.req.file)
|
|
|
|
ctx.body = ctx.req.file
|
|
|
|
})
|
|
|
|
flaska.get('/file/leak', function(ctx) {
|
|
|
|
file = fsSync.createReadStream('./test/test.png')
|
|
|
|
ctx.body = file
|
|
|
|
|
|
|
|
return new Promise(function() {})
|
|
|
|
})
|
|
|
|
|
|
|
|
function reset() {
|
|
|
|
log.fatal.reset()
|
|
|
|
log.error.reset()
|
|
|
|
log.warn.reset()
|
|
|
|
log.info.reset()
|
|
|
|
log.debug.reset()
|
|
|
|
log.trace.reset()
|
|
|
|
log.log.reset()
|
|
|
|
}
|
2021-10-09 00:12:56 +00:00
|
|
|
|
2022-03-16 10:41:25 +00:00
|
|
|
t.before(function() {
|
|
|
|
return flaska.listenAsync(port)
|
2021-10-09 00:12:56 +00:00
|
|
|
})
|
|
|
|
|
2022-03-16 10:41:25 +00:00
|
|
|
t.describe('/', function() {
|
|
|
|
t.test('should return status true', function() {
|
|
|
|
return client.get('/').then(function(body) {
|
2021-10-09 00:12:56 +00:00
|
|
|
assert.deepEqual(body, { status: true })
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2022-03-17 15:09:22 +00:00
|
|
|
t.describe('/json', function() {
|
2022-03-16 10:41:25 +00:00
|
|
|
t.test('should return success and store body', async function() {
|
2022-03-17 15:09:22 +00:00
|
|
|
const assertBody = { a: '' }
|
|
|
|
for (let i = 0; i < 1010; i++) {
|
|
|
|
assertBody.a += 'aaaaaaaaaa'
|
|
|
|
}
|
2022-03-16 10:41:25 +00:00
|
|
|
reqBody = null
|
2022-03-17 15:09:22 +00:00
|
|
|
let body = await client.post('/json', assertBody)
|
2022-03-16 10:41:25 +00:00
|
|
|
assert.deepEqual(body, { success: true })
|
2022-03-17 15:09:22 +00:00
|
|
|
assert.deepStrictEqual(reqBody, assertBody)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.test('should fail if body is too big', async function() {
|
|
|
|
reset()
|
|
|
|
|
|
|
|
const assertBody = { a: '' }
|
|
|
|
for (let i = 0; i < 10300; i++) {
|
|
|
|
assertBody.a += 'aaaaaaaaaa'
|
|
|
|
}
|
|
|
|
|
|
|
|
let err = await assert.isRejected(client.post('/json', assertBody))
|
|
|
|
|
|
|
|
assert.strictEqual(err.body.status, 413)
|
|
|
|
assert.ok(log.error.called)
|
|
|
|
assert.match(log.error.firstCall[0].message, /10240/)
|
|
|
|
assert.strictEqual(log.error.firstCall[0].status, 413)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.test('should fail if not a valid json', async function() {
|
|
|
|
reset()
|
|
|
|
|
|
|
|
let err = await assert.isRejected(client.customRequest('POST', '/json', 'aaaa'))
|
|
|
|
|
|
|
|
assert.strictEqual(err.body.status, 400)
|
|
|
|
assert.match(err.body.message, /invalid json/i)
|
|
|
|
assert.match(err.body.message, /token a/i)
|
|
|
|
assert.strictEqual(err.body.request, 'aaaa')
|
|
|
|
assert.strictEqual(log.error.callCount, 1)
|
|
|
|
assert.match(log.error.firstCall[0].message, /invalid json/i)
|
|
|
|
assert.match(log.error.firstCall[0].message, /token a/i)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.test('should handle incomplete requests correctly', async function() {
|
|
|
|
reset()
|
|
|
|
|
|
|
|
let req = await client.customRequest('POST', '/json', 'aaaa', { returnRequest: true })
|
|
|
|
|
|
|
|
req.write('part1')
|
|
|
|
|
|
|
|
await setTimeout(20)
|
|
|
|
|
|
|
|
req.destroy()
|
|
|
|
|
|
|
|
await setTimeout(20)
|
|
|
|
|
|
|
|
assert.strictEqual(log.error.callCount, 0)
|
|
|
|
assert.strictEqual(log.info.callCount, 1)
|
|
|
|
assert.strictEqual(log.info.firstCall[0].message, 'aborted')
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
t.describe('/timeout', function() {
|
|
|
|
t.test('server should handle timeout', async function() {
|
|
|
|
reset()
|
|
|
|
|
|
|
|
let err = await assert.isRejected(client.customRequest('GET', '/timeout', JSON.stringify({}), { timeout: 20 }))
|
|
|
|
|
|
|
|
await setTimeout(20)
|
|
|
|
|
|
|
|
assert.match(err.message, /timed out/)
|
|
|
|
assert.notOk(log.error.called)
|
|
|
|
assert.ok(log.info.called)
|
|
|
|
assert.strictEqual(log.info.firstCall[0].message, 'aborted')
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
t.describe('/file', function() {
|
|
|
|
t.test('server should pipe', async function() {
|
|
|
|
log.error.reset()
|
|
|
|
|
|
|
|
let target = fsSync.createWriteStream('./test_tmp.png')
|
|
|
|
|
|
|
|
await client.customRequest('GET', '/file', null, { toPipe: target })
|
|
|
|
|
|
|
|
await setTimeout(20)
|
|
|
|
|
|
|
|
assert.ok(target.closed)
|
|
|
|
assert.ok(file.closed)
|
|
|
|
|
|
|
|
let [statSource, statTarget] = await Promise.all([
|
|
|
|
fs.stat('./test/test.png'),
|
|
|
|
fs.stat('./test_tmp.png'),
|
|
|
|
])
|
|
|
|
|
|
|
|
assert.strictEqual(statSource.size, statTarget.size)
|
|
|
|
})
|
|
|
|
t.test('server should autoclose body file handles on errors', async function() {
|
|
|
|
reset()
|
|
|
|
|
|
|
|
file = null
|
|
|
|
|
|
|
|
let req = await client.customRequest('GET', '/file/leak', null, { returnRequest: true })
|
|
|
|
|
|
|
|
req.end()
|
|
|
|
|
|
|
|
while (!file) {
|
|
|
|
await setTimeout(10)
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.ok(file)
|
|
|
|
assert.notOk(file.closed)
|
|
|
|
|
|
|
|
req.destroy()
|
|
|
|
|
|
|
|
await setTimeout(20)
|
|
|
|
|
|
|
|
while (!file.closed) {
|
|
|
|
await setTimeout(10)
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.strictEqual(log.error.callCount, 0)
|
|
|
|
assert.strictEqual(log.info.callCount, 1)
|
|
|
|
assert.strictEqual(log.info.firstCall[0].message, 'aborted')
|
|
|
|
|
|
|
|
assert.ok(file.closed)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
t.describe('/file/upload', function() {
|
|
|
|
t.test('server should upload file', async function() {
|
|
|
|
let res = await client.upload('/file/upload', './test/test.png')
|
|
|
|
|
|
|
|
let [statSource, statTarget] = await Promise.all([
|
|
|
|
fs.stat('./test/test.png'),
|
|
|
|
fs.stat(res.path),
|
|
|
|
])
|
|
|
|
|
|
|
|
assert.strictEqual(statSource.size, statTarget.size)
|
|
|
|
assert.strictEqual(statSource.size, res.size)
|
2022-03-16 10:41:25 +00:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
t.after(function() {
|
2022-03-17 15:09:22 +00:00
|
|
|
return Promise.all([
|
|
|
|
fs.rm('./test_tmp.png', { force: true }),
|
|
|
|
Promise.all(uploaded.map(file => fs.rm(file.path, { force: true }))),
|
|
|
|
flaska.closeAsync(),
|
|
|
|
])
|
2021-10-09 00:12:56 +00:00
|
|
|
})
|