959 lines
30 KiB
JavaScript
959 lines
30 KiB
JavaScript
import fs from 'fs/promises'
|
|
import { Eltro as t, assert, stub } from 'eltro'
|
|
import { HttpError } from 'flaska'
|
|
import { createContext } from '../helper.server.mjs'
|
|
|
|
import MediaRoutes from '../../api/media/routes.mjs'
|
|
|
|
t.before(function() {
|
|
return Promise.all([
|
|
fs.readdir('./public/development').then(files => {
|
|
return Promise.all(files.map(function(file) {
|
|
if (file !== '.gitkeep') {
|
|
return fs.unlink(`./public/development/${file}`).catch(function() {})
|
|
}
|
|
return Promise.resolve()
|
|
}))
|
|
}),
|
|
])
|
|
})
|
|
|
|
t.describe('#filesCacheGet', function() {
|
|
t.test('should return all files in public folder', async function() {
|
|
const routes = new MediaRoutes()
|
|
|
|
await routes.init()
|
|
|
|
assert.ok(routes.filesCacheGet('development'))
|
|
assert.ok(Array.isArray(routes.filesCacheGet('development')))
|
|
assert.ok(routes.filesCacheGet('existing'))
|
|
assert.ok(Array.isArray(routes.filesCacheGet('existing')))
|
|
assert.ok(routes.filesCacheGet('nonexisting'))
|
|
assert.ok(Array.isArray(routes.filesCacheGet('nonexisting')))
|
|
|
|
assert.strictEqual(routes.filesCacheGet('development').length, 1)
|
|
assert.strictEqual(routes.filesCacheGet('existing').length, 2)
|
|
assert.strictEqual(routes.filesCacheGet('nonexisting').length, 0)
|
|
|
|
assert.strictEqual(routes.filesCacheGet('development')[0].filename, '.gitkeep')
|
|
assert.strictEqual(routes.filesCacheGet('development')[0].size, 0)
|
|
assert.strictEqual(routes.filesCacheGet('existing')[0].filename, '20220105_101610_test1.jpg')
|
|
assert.strictEqual(routes.filesCacheGet('existing')[0].size, 15079)
|
|
assert.strictEqual(routes.filesCacheGet('existing')[1].filename, '20220105_101610_test2.png')
|
|
assert.strictEqual(routes.filesCacheGet('existing')[1].size, 31705)
|
|
})
|
|
|
|
t.test('should always sort result', async function() {
|
|
const routes = new MediaRoutes()
|
|
|
|
await routes.init()
|
|
|
|
let files = routes.filesCacheGet('existing')
|
|
|
|
assert.strictEqual(files[0].filename, '20220105_101610_test1.jpg')
|
|
assert.strictEqual(files[0].size, 15079)
|
|
assert.strictEqual(files[1].filename, '20220105_101610_test2.png')
|
|
assert.strictEqual(files[1].size, 31705)
|
|
|
|
routes.siteCache.get('existing').push({ filename: '0000.png', size: 0 })
|
|
|
|
files = routes.filesCacheGet('existing')
|
|
assert.strictEqual(files[0].filename, '0000.png')
|
|
assert.strictEqual(files[0].size, 0)
|
|
assert.strictEqual(files[1].filename, '20220105_101610_test1.jpg')
|
|
assert.strictEqual(files[1].size, 15079)
|
|
assert.strictEqual(files[2].filename, '20220105_101610_test2.png')
|
|
assert.strictEqual(files[2].size, 31705)
|
|
})
|
|
})
|
|
|
|
t.describe('#listFiles()', function() {
|
|
const stubVerify = stub()
|
|
const stubGetCache = stub()
|
|
|
|
const routes = new MediaRoutes({
|
|
security: { verifyToken: stubVerify },
|
|
})
|
|
routes.filesCacheGet = stubGetCache
|
|
|
|
function reset() {
|
|
stubVerify.reset()
|
|
stubGetCache.reset()
|
|
}
|
|
|
|
t.test('should call security correctly', async function() {
|
|
reset()
|
|
|
|
let ctx = createContext()
|
|
const assertError = new Error('temp')
|
|
stubVerify.rejects(assertError)
|
|
|
|
let err = await assert.isRejected(routes.listFiles(ctx))
|
|
|
|
assert.ok(stubVerify.called)
|
|
assert.strictEqual(err, assertError)
|
|
assert.strictEqual(stubVerify.firstCall[0], ctx)
|
|
})
|
|
|
|
t.test('should call filesCacheGet and return results', async function() {
|
|
reset()
|
|
|
|
let ctx = createContext()
|
|
const assertSiteName = 'benshapiro'
|
|
const assertResult = { a: 1 }
|
|
stubVerify.resolves(assertSiteName)
|
|
stubGetCache.returns(assertResult)
|
|
|
|
await routes.listFiles(ctx)
|
|
|
|
assert.ok(stubGetCache.called)
|
|
assert.strictEqual(stubGetCache.firstCall[0], assertSiteName)
|
|
assert.strictEqual(ctx.body, assertResult)
|
|
})
|
|
})
|
|
|
|
t.describe('#listPublicFiles()', function() {
|
|
const stubSitePublic = stub()
|
|
const stubGetCache = stub()
|
|
|
|
const routes = new MediaRoutes({
|
|
security: { throwIfNotPublic: stubSitePublic },
|
|
})
|
|
routes.filesCacheGet = stubGetCache
|
|
|
|
function reset() {
|
|
stubSitePublic.reset()
|
|
stubGetCache.reset()
|
|
}
|
|
|
|
t.test('should call security correctly', async function() {
|
|
reset()
|
|
|
|
let ctx = createContext()
|
|
const assertError = new Error('temp')
|
|
const assertSite = 'astalavista'
|
|
stubSitePublic.throws(assertError)
|
|
ctx.params.site = assertSite
|
|
|
|
let err = await assert.isRejected(routes.listPublicFiles(ctx))
|
|
|
|
assert.ok(stubSitePublic.called)
|
|
assert.strictEqual(err, assertError)
|
|
assert.strictEqual(stubSitePublic.firstCall[0], assertSite)
|
|
})
|
|
|
|
t.test('should call filesCacheGet and return results', async function() {
|
|
reset()
|
|
|
|
let ctx = createContext()
|
|
const assertSiteName = 'benshapiro'
|
|
const assertResult = { a: 1 }
|
|
ctx.params.site = assertSiteName
|
|
stubGetCache.returns(assertResult)
|
|
|
|
await routes.listPublicFiles(ctx)
|
|
|
|
assert.ok(stubGetCache.called)
|
|
assert.strictEqual(stubGetCache.firstCall[0], assertSiteName)
|
|
assert.strictEqual(ctx.body, assertResult)
|
|
})
|
|
})
|
|
|
|
t.describe('#filesCacheAdd', function() {
|
|
t.test('should auto-create array of site and add file', async function() {
|
|
const routes = new MediaRoutes()
|
|
const assertName = 'asdf.png'
|
|
const assertSize = 1234
|
|
|
|
await routes.init()
|
|
|
|
assert.ok(routes.filesCacheGet('nonexisting'))
|
|
assert.ok(Array.isArray(routes.filesCacheGet('nonexisting')))
|
|
assert.strictEqual(routes.filesCacheGet('nonexisting').length, 0)
|
|
|
|
routes.filesCacheAdd('nonexisting', assertName, assertSize)
|
|
|
|
assert.strictEqual(routes.filesCacheGet('nonexisting').length, 1)
|
|
assert.strictEqual(routes.filesCacheGet('nonexisting')[0].filename, assertName)
|
|
assert.strictEqual(routes.filesCacheGet('nonexisting')[0].size, assertSize)
|
|
})
|
|
|
|
t.test('should not add but update size if file already exists', function() {
|
|
const routes = new MediaRoutes()
|
|
const assertName = 'asdf.png'
|
|
const assertSize = 1234
|
|
const assertNewSize = 5678
|
|
|
|
assert.ok(routes.filesCacheGet('nonexisting'))
|
|
assert.ok(Array.isArray(routes.filesCacheGet('nonexisting')))
|
|
assert.strictEqual(routes.filesCacheGet('nonexisting').length, 0)
|
|
|
|
routes.filesCacheAdd('nonexisting', assertName, assertSize)
|
|
|
|
assert.strictEqual(routes.filesCacheGet('nonexisting').length, 1)
|
|
assert.strictEqual(routes.filesCacheGet('nonexisting')[0].filename, assertName)
|
|
assert.strictEqual(routes.filesCacheGet('nonexisting')[0].size, assertSize)
|
|
|
|
routes.filesCacheAdd('nonexisting', assertName, assertNewSize)
|
|
|
|
assert.strictEqual(routes.filesCacheGet('nonexisting').length, 1)
|
|
assert.strictEqual(routes.filesCacheGet('nonexisting')[0].filename, assertName)
|
|
assert.strictEqual(routes.filesCacheGet('nonexisting')[0].size, assertNewSize)
|
|
})
|
|
})
|
|
|
|
t.describe('#filesCacheRemove', function() {
|
|
t.test('should default not do anything if nothing is found', async function() {
|
|
const routes = new MediaRoutes()
|
|
|
|
await routes.init()
|
|
|
|
assert.ok(routes.filesCacheGet('existing'))
|
|
assert.ok(Array.isArray(routes.filesCacheGet('existing')))
|
|
assert.strictEqual(routes.filesCacheGet('existing').length, 2)
|
|
|
|
routes.filesCacheRemove('nonexisting', 'bla.text')
|
|
routes.filesCacheRemove('nonexisting', 'herp.derp')
|
|
routes.filesCacheRemove('existing', 'bla.text')
|
|
routes.filesCacheRemove('existing', 'herp.derp')
|
|
|
|
assert.ok(routes.filesCacheGet('existing'))
|
|
assert.ok(Array.isArray(routes.filesCacheGet('existing')))
|
|
assert.strictEqual(routes.filesCacheGet('existing').length, 2)
|
|
})
|
|
|
|
t.test('should otherwise remove entries that match filename', async function() {
|
|
const routes = new MediaRoutes()
|
|
|
|
await routes.init()
|
|
|
|
assert.ok(routes.filesCacheGet('existing'))
|
|
assert.ok(Array.isArray(routes.filesCacheGet('existing')))
|
|
assert.strictEqual(routes.filesCacheGet('existing').length, 2)
|
|
|
|
routes.filesCacheRemove('existing', '20220105_101610_test1.jpg')
|
|
|
|
assert.ok(routes.filesCacheGet('existing'))
|
|
assert.ok(Array.isArray(routes.filesCacheGet('existing')))
|
|
assert.strictEqual(routes.filesCacheGet('existing').length, 1)
|
|
assert.strictEqual(routes.filesCacheGet('existing')[0].filename, '20220105_101610_test2.png')
|
|
})
|
|
})
|
|
|
|
t.describe('#uploadNoPrefix', function() {
|
|
const stubVerify = stub()
|
|
const stubUpload = stub()
|
|
const stubStat = stub()
|
|
|
|
const routes = new MediaRoutes({
|
|
security: {
|
|
verifyToken: stubVerify,
|
|
verifyBody: stub(),
|
|
},
|
|
formidable: { uploadFile: stubUpload, },
|
|
fs: { stat: stubStat },
|
|
})
|
|
|
|
function reset() {
|
|
stubVerify.reset()
|
|
stubUpload.reset()
|
|
stubStat.reset()
|
|
}
|
|
|
|
t.test('should call upload correctly', async function() {
|
|
reset()
|
|
|
|
let ctx = createContext()
|
|
const assertSiteName = 'benshapiro'
|
|
const assertError = new Error('hello')
|
|
stubVerify.resolves(assertSiteName)
|
|
stubUpload.rejects(assertError)
|
|
|
|
let err = await assert.isRejected(routes.uploadNoPrefix(ctx))
|
|
|
|
assert.ok(stubUpload.called)
|
|
assert.strictEqual(err, assertError)
|
|
assert.strictEqual(stubUpload.firstCall[0], ctx)
|
|
assert.strictEqual(stubUpload.firstCall[1], assertSiteName)
|
|
assert.strictEqual(stubUpload.firstCall[2], true)
|
|
})
|
|
})
|
|
|
|
t.describe('#resizeExisting', function() {
|
|
const stubVerifyToken = stub()
|
|
const stubVerifyBody = stub()
|
|
const stubSharp = stub()
|
|
const stubSharpResize = stub()
|
|
const stubSharpBlur = stub()
|
|
const stubSharpTrim = stub()
|
|
const stubSharpExtend = stub()
|
|
const stubSharpFlatten = stub()
|
|
const stubSharpRotate = stub()
|
|
const stubSharpToFile = stub()
|
|
const stubSharpJpeg = stub()
|
|
const stubSharpPng = stub()
|
|
const stubSharpToBuffer = stub()
|
|
const stubStat = stub()
|
|
|
|
const routes = new MediaRoutes({
|
|
security: {
|
|
verifyToken: stubVerifyToken,
|
|
verifyBody: stubVerifyBody,
|
|
},
|
|
fs: { stat: stubStat },
|
|
sharp: stubSharp,
|
|
})
|
|
|
|
function reset() {
|
|
stubVerifyToken.reset()
|
|
stubVerifyBody.reset()
|
|
let def = {
|
|
toFile: stubSharpToFile,
|
|
jpeg: stubSharpJpeg,
|
|
png: stubSharpPng,
|
|
resize: stubSharpResize,
|
|
trim: stubSharpTrim,
|
|
extend: stubSharpExtend,
|
|
flatten: stubSharpFlatten,
|
|
blur: stubSharpBlur,
|
|
rotate: stubSharpRotate,
|
|
toBuffer: stubSharpToBuffer,
|
|
}
|
|
stubStat.reset()
|
|
stubStat.resolves({ size: 0 })
|
|
stubSharp.reset()
|
|
stubSharp.returns(def)
|
|
for (let key in def) {
|
|
def[key].reset()
|
|
def[key].returns(def)
|
|
}
|
|
stubSharpToFile.resolves()
|
|
stubSharpToBuffer.resolves()
|
|
routes.siteCache.clear()
|
|
}
|
|
|
|
t.test('should call security verifyToken correctly', async function() {
|
|
reset()
|
|
|
|
let ctx = createContext({ req: { body: { } } })
|
|
const assertError = new Error('temp')
|
|
stubVerifyToken.rejects(assertError)
|
|
|
|
let err = await assert.isRejected(routes.resizeExisting(ctx))
|
|
|
|
assert.ok(stubVerifyToken.called)
|
|
assert.strictEqual(err, assertError)
|
|
assert.strictEqual(stubVerifyToken.firstCall[0], ctx)
|
|
})
|
|
|
|
t.test('should call security verifyBody correctly', async function() {
|
|
reset()
|
|
|
|
let ctx = createContext({ req: { body: { }}})
|
|
const assertError = new Error('temp')
|
|
stubVerifyBody.throws(assertError)
|
|
|
|
let err = await assert.isRejected(routes.resizeExisting(ctx))
|
|
|
|
assert.strictEqual(err, assertError)
|
|
assert.ok(stubVerifyBody.called)
|
|
assert.strictEqual(stubVerifyBody.firstCall[0], ctx)
|
|
})
|
|
|
|
t.test('should call sharp correctly if items are specified', async function() {
|
|
reset()
|
|
const assertKey = 'asdf'
|
|
const assertJpeg = { a: 1 }
|
|
const assertFilename = 'asdfsafd'
|
|
const assertSite = 'mario'
|
|
stubVerifyToken.resolves(assertSite)
|
|
|
|
let ctx = createContext({ req: { body: {
|
|
[assertKey]: {
|
|
format: 'jpeg',
|
|
}
|
|
}}})
|
|
ctx.params.filename = assertFilename + '.png'
|
|
ctx.req.body[assertKey].jpeg = assertJpeg
|
|
|
|
await routes.resizeExisting(ctx)
|
|
|
|
assert.ok(stubSharp.called)
|
|
assert.match(stubSharp.firstCall[0], new RegExp(`\/${assertSite}\/${assertFilename}\.png`))
|
|
assert.ok(stubSharpRotate.called)
|
|
assert.ok(stubSharpJpeg.called)
|
|
assert.strictEqual(stubSharpJpeg.firstCall[0], assertJpeg)
|
|
assert.ok(stubSharpToFile.called)
|
|
assert.match(stubSharpToFile.firstCall[0], new RegExp(`\/${assertSite}\/${assertFilename}_${assertKey}\.jpg`))
|
|
assert.notOk(stubSharpPng.called)
|
|
assert.notOk(stubSharpResize.called)
|
|
assert.notOk(ctx.body.filename)
|
|
assert.notOk(ctx.body.path)
|
|
assert.strictEqual(ctx.body[assertKey].filename, `${assertFilename}_${assertKey}\.jpg`)
|
|
assert.strictEqual(ctx.body[assertKey].path, `/${assertSite}/${assertFilename}_${assertKey}.jpg`)
|
|
})
|
|
|
|
let validOperations = [
|
|
'resize',
|
|
'blur',
|
|
'trim',
|
|
'extend',
|
|
'flatten',
|
|
]
|
|
|
|
let operationStubMap = {
|
|
resize: stubSharpResize,
|
|
blur: stubSharpBlur,
|
|
trim: stubSharpTrim,
|
|
extend: stubSharpExtend,
|
|
flatten: stubSharpFlatten,
|
|
}
|
|
|
|
validOperations.forEach(function(operation) {
|
|
t.test(`should call sharp correctly if items and ${operation} are specified`, async function() {
|
|
reset()
|
|
const assertKey = 'herpderp'
|
|
const assertPng = { a: 1 }
|
|
const assertPayload = { a: 1 }
|
|
const assertFilename = 'asdfsafd'
|
|
const assertSite = 'mario'
|
|
stubVerifyToken.resolves(assertSite)
|
|
|
|
let called = 0
|
|
stubStat.returnWith(function() {
|
|
called += 10
|
|
return { size: called }
|
|
})
|
|
|
|
let ctx = createContext({ req: { body: {
|
|
[assertKey]: {
|
|
format: 'png',
|
|
}
|
|
}}})
|
|
ctx.params.filename = assertFilename + '.png'
|
|
ctx.req.body[assertKey].png = assertPng
|
|
ctx.req.body[assertKey][operation] = assertPayload
|
|
|
|
await routes.resizeExisting(ctx)
|
|
|
|
assert.ok(stubSharp.called)
|
|
assert.match(stubSharp.firstCall[0], new RegExp(`\/${assertSite}\/${assertFilename}\.png`))
|
|
assert.ok(stubSharpRotate.called)
|
|
assert.ok(stubSharpPng.called)
|
|
assert.strictEqual(stubSharpPng.firstCall[0], assertPng)
|
|
assert.ok(stubSharpToFile.called)
|
|
assert.match(stubSharpToFile.firstCall[0], new RegExp(`\/${assertSite}\/${assertFilename}_${assertKey}\.png`))
|
|
assert.notOk(stubSharpJpeg.called)
|
|
|
|
assert.ok(operationStubMap[operation].called)
|
|
assert.strictEqual(operationStubMap[operation].firstCall[0], assertPayload)
|
|
|
|
assert.notOk(ctx.body.filename)
|
|
assert.notOk(ctx.body.path)
|
|
assert.strictEqual(ctx.body[assertKey].filename, `${assertFilename}_${assertKey}\.png`)
|
|
assert.strictEqual(ctx.body[assertKey].path, `/${assertSite}/${assertFilename}_${assertKey}.png`)
|
|
|
|
let filesFromCache = routes.filesCacheGet(assertSite)
|
|
assert.strictEqual(filesFromCache.length, 1)
|
|
assert.strictEqual(filesFromCache[0].filename, `${assertFilename}_${assertKey}\.png`)
|
|
assert.strictEqual(filesFromCache[0].size, 10)
|
|
})
|
|
})
|
|
|
|
t.test('should notify which item failed if one fails', async function() {
|
|
reset()
|
|
const assertValidKey1 = 'herp'
|
|
const assertValidKey2 = 'derp'
|
|
const assertErrorKey = 'throwmyerr'
|
|
const assertErrorMessage = 'some message here'
|
|
stubVerifyToken.resolves('asdf')
|
|
|
|
let called = 0
|
|
stubStat.returnWith(function() {
|
|
called += 10
|
|
return { size: called }
|
|
})
|
|
|
|
let ctx = createContext({ req: { body: {
|
|
[assertValidKey1]: {
|
|
format: 'png',
|
|
png: { a: 1 },
|
|
},
|
|
[assertValidKey2]: {
|
|
format: 'png',
|
|
png: { a: 1 },
|
|
},
|
|
[assertErrorKey]: {
|
|
format: 'png',
|
|
png: { a: 1 },
|
|
},
|
|
}}})
|
|
ctx.params.filename = 'file.png'
|
|
|
|
stubSharpToFile.returnWith(function(file) {
|
|
if (file.match(new RegExp(assertErrorKey))) {
|
|
throw new Error(assertErrorMessage)
|
|
}
|
|
})
|
|
|
|
let err = await assert.isRejected(routes.resizeExisting(ctx))
|
|
|
|
assert.ok(err instanceof HttpError)
|
|
assert.ok(err instanceof Error)
|
|
assert.strictEqual(err.status, 422)
|
|
assert.match(err.message, new RegExp(assertErrorKey))
|
|
assert.match(err.message, new RegExp(assertErrorMessage))
|
|
})
|
|
|
|
t.test('should call sharp correctly and return base64 format if out is base64', async function() {
|
|
reset()
|
|
const assertKey = 'outtest'
|
|
const assertFilename = 'asdfsafd.png'
|
|
const assertSite = 'mario'
|
|
const assertBase64Data = 'asdf1234'
|
|
stubVerifyToken.resolves(assertSite)
|
|
|
|
let ctx = createContext({ req: { body: {
|
|
[assertKey]: {
|
|
format: 'png',
|
|
png: { a: 1 },
|
|
out: 'base64',
|
|
}
|
|
}}})
|
|
ctx.params.filename = assertFilename
|
|
|
|
stubSharpToBuffer.resolves(Buffer.from(assertBase64Data))
|
|
|
|
await routes.resizeExisting(ctx)
|
|
|
|
assert.ok(stubSharp.called)
|
|
assert.notOk(stubSharpToFile.called)
|
|
assert.ok(stubSharpToBuffer.called)
|
|
assert.ok(ctx.body[assertKey].base64)
|
|
assert.ok(ctx.body[assertKey].base64.startsWith(`data:image/png;base64,`))
|
|
let base64 = ctx.body[assertKey].base64
|
|
let bufferBase64 = Buffer.from(base64.slice(base64.indexOf(',')), 'base64')
|
|
assert.strictEqual(bufferBase64.toString(), assertBase64Data)
|
|
|
|
let filesFromCache = routes.filesCacheGet(assertSite)
|
|
assert.strictEqual(filesFromCache.length, 0)
|
|
})
|
|
})
|
|
|
|
t.describe('#resize', function() {
|
|
const stubVerifyToken = stub()
|
|
const stubVerifyBody = stub()
|
|
const stubUpload = stub()
|
|
const stubSharp = stub()
|
|
const stubSharpResize = stub()
|
|
const stubSharpBlur = stub()
|
|
const stubSharpTrim = stub()
|
|
const stubSharpExtend = stub()
|
|
const stubSharpFlatten = stub()
|
|
const stubSharpRotate = stub()
|
|
const stubSharpToFile = stub()
|
|
const stubSharpJpeg = stub()
|
|
const stubSharpPng = stub()
|
|
const stubSharpToBuffer = stub()
|
|
const stubStat = stub()
|
|
|
|
const routes = new MediaRoutes({
|
|
security: {
|
|
verifyToken: stubVerifyToken,
|
|
verifyBody: stubVerifyBody,
|
|
},
|
|
fs: { stat: stubStat },
|
|
formidable: { uploadFile: stubUpload },
|
|
sharp: stubSharp,
|
|
})
|
|
|
|
function reset() {
|
|
stubVerifyToken.reset()
|
|
stubVerifyBody.reset()
|
|
stubUpload.reset()
|
|
let def = {
|
|
toFile: stubSharpToFile,
|
|
jpeg: stubSharpJpeg,
|
|
png: stubSharpPng,
|
|
resize: stubSharpResize,
|
|
trim: stubSharpTrim,
|
|
extend: stubSharpExtend,
|
|
flatten: stubSharpFlatten,
|
|
blur: stubSharpBlur,
|
|
rotate: stubSharpRotate,
|
|
toBuffer: stubSharpToBuffer,
|
|
}
|
|
stubStat.reset()
|
|
stubStat.resolves({ size: 0 })
|
|
stubSharp.reset()
|
|
stubSharp.returns(def)
|
|
for (let key in def) {
|
|
def[key].reset()
|
|
def[key].returns(def)
|
|
}
|
|
stubSharpToFile.resolves()
|
|
stubSharpToBuffer.resolves()
|
|
routes.siteCache.clear()
|
|
}
|
|
|
|
t.test('should call security verifyBody correctly', async function() {
|
|
reset()
|
|
|
|
let ctx = createContext({ req: { body: { }}})
|
|
const assertError = new Error('temp')
|
|
stubUpload.resolves({ filename: 'bla' })
|
|
stubVerifyBody.throws(assertError)
|
|
|
|
let err = await assert.isRejected(routes.resize(ctx))
|
|
|
|
assert.strictEqual(err, assertError)
|
|
assert.ok(stubVerifyBody.called)
|
|
assert.strictEqual(stubVerifyBody.firstCall[0], ctx)
|
|
})
|
|
|
|
t.test('should call sharp correctly if items are specified', async function() {
|
|
reset()
|
|
const assertKey = 'asdf'
|
|
const assertJpeg = { a: 1 }
|
|
const assertFilename = 'asdfsafd'
|
|
const assertSite = 'mario'
|
|
stubVerifyToken.resolves(assertSite)
|
|
stubUpload.resolves({ filename: assertFilename + '.png' })
|
|
|
|
let ctx = createContext({ req: { body: {
|
|
[assertKey]: {
|
|
format: 'jpeg',
|
|
}
|
|
}}})
|
|
ctx.req.body[assertKey].jpeg = assertJpeg
|
|
|
|
await routes.resize(ctx)
|
|
|
|
assert.ok(stubSharp.called)
|
|
assert.match(stubSharp.firstCall[0], new RegExp(`\/${assertSite}\/${assertFilename}\.png`))
|
|
assert.ok(stubSharpRotate.called)
|
|
assert.ok(stubSharpJpeg.called)
|
|
assert.strictEqual(stubSharpJpeg.firstCall[0], assertJpeg)
|
|
assert.ok(stubSharpToFile.called)
|
|
assert.match(stubSharpToFile.firstCall[0], new RegExp(`\/${assertSite}\/${assertFilename}_${assertKey}\.jpg`))
|
|
assert.notOk(stubSharpPng.called)
|
|
assert.notOk(stubSharpResize.called)
|
|
assert.strictEqual(ctx.body.filename, assertFilename + '.png')
|
|
assert.strictEqual(ctx.body.path, `/${assertSite}/${assertFilename}.png`)
|
|
assert.strictEqual(ctx.body[assertKey].filename, `${assertFilename}_${assertKey}\.jpg`)
|
|
assert.strictEqual(ctx.body[assertKey].path, `/${assertSite}/${assertFilename}_${assertKey}.jpg`)
|
|
})
|
|
|
|
let validOperations = [
|
|
'resize',
|
|
'blur',
|
|
'trim',
|
|
'extend',
|
|
'flatten',
|
|
]
|
|
|
|
let operationStubMap = {
|
|
resize: stubSharpResize,
|
|
blur: stubSharpBlur,
|
|
trim: stubSharpTrim,
|
|
extend: stubSharpExtend,
|
|
flatten: stubSharpFlatten,
|
|
}
|
|
|
|
validOperations.forEach(function(operation) {
|
|
t.test(`should call sharp correctly if items and ${operation} are specified`, async function() {
|
|
reset()
|
|
const assertKey = 'herpderp'
|
|
const assertPng = { a: 1 }
|
|
const assertPayload = { a: 1 }
|
|
const assertFilename = 'asdfsafd'
|
|
const assertSite = 'mario'
|
|
stubVerifyToken.resolves(assertSite)
|
|
stubUpload.resolves({ filename: assertFilename + '.png' })
|
|
|
|
let called = 0
|
|
stubStat.returnWith(function() {
|
|
called += 10
|
|
return { size: called }
|
|
})
|
|
|
|
let ctx = createContext({ req: { body: {
|
|
[assertKey]: {
|
|
format: 'png',
|
|
}
|
|
}}})
|
|
ctx.req.body[assertKey].png = assertPng
|
|
ctx.req.body[assertKey][operation] = assertPayload
|
|
|
|
await routes.resize(ctx)
|
|
|
|
assert.ok(stubSharp.called)
|
|
assert.match(stubSharp.firstCall[0], new RegExp(`\/${assertSite}\/${assertFilename}\.png`))
|
|
assert.ok(stubSharpRotate.called)
|
|
assert.ok(stubSharpPng.called)
|
|
assert.strictEqual(stubSharpPng.firstCall[0], assertPng)
|
|
assert.ok(stubSharpToFile.called)
|
|
assert.match(stubSharpToFile.firstCall[0], new RegExp(`\/${assertSite}\/${assertFilename}_${assertKey}\.png`))
|
|
assert.notOk(stubSharpJpeg.called)
|
|
|
|
assert.ok(operationStubMap[operation].called)
|
|
assert.strictEqual(operationStubMap[operation].firstCall[0], assertPayload)
|
|
|
|
assert.strictEqual(ctx.body.filename, assertFilename + '.png')
|
|
assert.strictEqual(ctx.body.path, `/${assertSite}/${assertFilename}.png`)
|
|
assert.strictEqual(ctx.body[assertKey].filename, `${assertFilename}_${assertKey}\.png`)
|
|
assert.strictEqual(ctx.body[assertKey].path, `/${assertSite}/${assertFilename}_${assertKey}.png`)
|
|
|
|
let filesFromCache = routes.filesCacheGet(assertSite)
|
|
assert.strictEqual(filesFromCache.length, 2)
|
|
assert.strictEqual(filesFromCache[0].filename, `${assertFilename}_${assertKey}\.png`)
|
|
assert.strictEqual(filesFromCache[0].size, 20)
|
|
assert.strictEqual(filesFromCache[1].filename, assertFilename + '.png')
|
|
assert.strictEqual(filesFromCache[1].size, 10)
|
|
})
|
|
})
|
|
|
|
t.test('should notify which item failed if one fails', async function() {
|
|
reset()
|
|
const assertValidKey1 = 'herp'
|
|
const assertValidKey2 = 'derp'
|
|
const assertErrorKey = 'throwmyerr'
|
|
const assertErrorMessage = 'some message here'
|
|
stubVerifyToken.resolves('asdf')
|
|
stubUpload.resolves({ filename: 'file.png' })
|
|
|
|
let called = 0
|
|
stubStat.returnWith(function() {
|
|
called += 10
|
|
return { size: called }
|
|
})
|
|
|
|
let ctx = createContext({ req: { body: {
|
|
[assertValidKey1]: {
|
|
format: 'png',
|
|
png: { a: 1 },
|
|
},
|
|
[assertValidKey2]: {
|
|
format: 'png',
|
|
png: { a: 1 },
|
|
},
|
|
[assertErrorKey]: {
|
|
format: 'png',
|
|
png: { a: 1 },
|
|
},
|
|
}}})
|
|
|
|
stubSharpToFile.returnWith(function(file) {
|
|
if (file.match(new RegExp(assertErrorKey))) {
|
|
throw new Error(assertErrorMessage)
|
|
}
|
|
})
|
|
|
|
let err = await assert.isRejected(routes.resize(ctx))
|
|
|
|
assert.ok(err instanceof HttpError)
|
|
assert.ok(err instanceof Error)
|
|
assert.strictEqual(err.status, 422)
|
|
assert.match(err.message, new RegExp(assertErrorKey))
|
|
assert.match(err.message, new RegExp(assertErrorMessage))
|
|
})
|
|
|
|
t.test('should call sharp correctly and return base64 format if out is base64', async function() {
|
|
reset()
|
|
const assertKey = 'outtest'
|
|
const assertFilename = 'asdfsafd.png'
|
|
const assertSite = 'mario'
|
|
const assertBase64Data = 'asdf1234'
|
|
stubVerifyToken.resolves(assertSite)
|
|
stubUpload.resolves({ filename: assertFilename })
|
|
|
|
let ctx = createContext({ req: { body: {
|
|
[assertKey]: {
|
|
format: 'png',
|
|
png: { a: 1 },
|
|
out: 'base64',
|
|
}
|
|
}}})
|
|
|
|
stubSharpToBuffer.resolves(Buffer.from(assertBase64Data))
|
|
|
|
await routes.resize(ctx)
|
|
|
|
assert.ok(stubSharp.called)
|
|
assert.notOk(stubSharpToFile.called)
|
|
assert.ok(stubSharpToBuffer.called)
|
|
assert.ok(ctx.body[assertKey].base64)
|
|
assert.ok(ctx.body[assertKey].base64.startsWith(`data:image/png;base64,`))
|
|
let base64 = ctx.body[assertKey].base64
|
|
let bufferBase64 = Buffer.from(base64.slice(base64.indexOf(',')), 'base64')
|
|
assert.strictEqual(bufferBase64.toString(), assertBase64Data)
|
|
|
|
let filesFromCache = routes.filesCacheGet(assertSite)
|
|
assert.strictEqual(filesFromCache.length, 1)
|
|
assert.strictEqual(filesFromCache[0].filename, assertFilename)
|
|
})
|
|
})
|
|
|
|
let basicUploadTestRoutes = [
|
|
'upload',
|
|
'resize',
|
|
'uploadNoPrefix'
|
|
]
|
|
|
|
basicUploadTestRoutes.forEach(function(name) {
|
|
t.describe(`#${name}() Base`, function() {
|
|
const stubVerify = stub()
|
|
const stubUpload = stub()
|
|
const stubStat = stub()
|
|
|
|
const routes = new MediaRoutes({
|
|
security: {
|
|
verifyToken: stubVerify,
|
|
verifyBody: stub(),
|
|
},
|
|
formidable: { uploadFile: stubUpload, },
|
|
fs: { stat: stubStat },
|
|
})
|
|
|
|
function reset() {
|
|
stubVerify.reset()
|
|
stubUpload.reset()
|
|
stubStat.reset()
|
|
}
|
|
|
|
t.test('should call security correctly', async function() {
|
|
reset()
|
|
|
|
let ctx = createContext({ req: { body: { } } })
|
|
const assertError = new Error('temp')
|
|
stubVerify.rejects(assertError)
|
|
|
|
let err = await assert.isRejected(routes[name](ctx))
|
|
|
|
assert.ok(stubVerify.called)
|
|
assert.strictEqual(err, assertError)
|
|
assert.strictEqual(stubVerify.firstCall[0], ctx)
|
|
})
|
|
|
|
t.test('should call upload correctly', async function() {
|
|
reset()
|
|
|
|
let ctx = createContext({ req: { body: { } } })
|
|
const assertSiteName = 'benshapiro'
|
|
const assertError = new Error('hello')
|
|
stubVerify.resolves(assertSiteName)
|
|
stubUpload.rejects(assertError)
|
|
|
|
let err = await assert.isRejected(routes[name](ctx))
|
|
|
|
assert.ok(stubUpload.called)
|
|
assert.strictEqual(err, assertError)
|
|
assert.strictEqual(stubUpload.firstCall[0], ctx)
|
|
assert.strictEqual(stubUpload.firstCall[1], assertSiteName)
|
|
})
|
|
|
|
t.test('should otherwise return the file in result', async function() {
|
|
reset()
|
|
|
|
let ctx = createContext({ req: { body: { } } })
|
|
const assertSize = 1241412
|
|
const assertFilename = 'asdfsafd'
|
|
const assertSite = 'mario'
|
|
stubVerify.resolves(assertSite)
|
|
stubUpload.resolves({ filename: assertFilename })
|
|
stubStat.resolves({ size: assertSize })
|
|
await routes[name](ctx)
|
|
|
|
assert.strictEqual(ctx.body.filename, assertFilename)
|
|
assert.strictEqual(ctx.body.path, `/${assertSite}/${assertFilename}`)
|
|
|
|
assert.ok(stubStat.called)
|
|
assert.strictEqual(stubStat.firstCall[0], `./public/${assertSite}/${assertFilename}`)
|
|
|
|
let filesFromCache = routes.filesCacheGet(assertSite)
|
|
assert.strictEqual(filesFromCache.length, 1)
|
|
assert.strictEqual(filesFromCache[0].filename, assertFilename)
|
|
assert.strictEqual(filesFromCache[0].size, assertSize)
|
|
})
|
|
})
|
|
})
|
|
|
|
t.describe('#remove()', function() {
|
|
const stubVerify = stub()
|
|
const stubUnlink = stub()
|
|
const stubCacheRemove = stub()
|
|
|
|
const routes = new MediaRoutes({
|
|
security: {
|
|
verifyToken: stubVerify,
|
|
},
|
|
fs: { unlink: stubUnlink },
|
|
})
|
|
|
|
routes.filesCacheRemove = stubCacheRemove
|
|
|
|
function reset() {
|
|
stubVerify.reset()
|
|
stubUnlink.reset()
|
|
stubCacheRemove.reset()
|
|
stubUnlink.resolves(null)
|
|
}
|
|
|
|
t.test('should call security correctly', async function() {
|
|
reset()
|
|
|
|
let ctx = createContext({ req: { body: { } } })
|
|
const assertError = new Error('temp')
|
|
stubVerify.rejects(assertError)
|
|
|
|
let err = await assert.isRejected(routes.remove(ctx))
|
|
|
|
assert.ok(stubVerify.called)
|
|
assert.strictEqual(err, assertError)
|
|
assert.strictEqual(stubVerify.firstCall[0], ctx)
|
|
})
|
|
|
|
t.test('should call unlink correctly', async function() {
|
|
const assertSiteName = 'benshapiro'
|
|
const assertFilename = 'somefilename.png'
|
|
const assertErrorMessage = 'Noriyasu Agematsu Tensei'
|
|
const assertError = new Error(assertErrorMessage)
|
|
|
|
reset()
|
|
|
|
let ctx = createContext({ req: { body: { } } })
|
|
ctx.params.filename = assertFilename
|
|
stubVerify.resolves(assertSiteName)
|
|
stubUnlink.rejects(assertError)
|
|
|
|
let err = await assert.isRejected(routes.remove(ctx))
|
|
|
|
assert.ok(stubUnlink.called)
|
|
assert.ok(err instanceof HttpError)
|
|
assert.ok(err instanceof Error)
|
|
assert.strictEqual(err.status, 422)
|
|
assert.match(err.message, new RegExp(assertSiteName))
|
|
assert.match(err.message, new RegExp(assertFilename))
|
|
assert.match(err.message, new RegExp(assertErrorMessage))
|
|
|
|
assert.strictEqual(stubUnlink.firstCall[0], `./public/${assertSiteName}/${assertFilename}`)
|
|
})
|
|
|
|
t.test('should otherwise return 204 status and remove from array', async function() {
|
|
const assertSiteName = 'benshapiro'
|
|
const assertFilename = 'somefilename.png'
|
|
|
|
reset()
|
|
|
|
let ctx = createContext({ req: { body: { } } })
|
|
ctx.params.filename = assertFilename
|
|
stubVerify.resolves(assertSiteName)
|
|
|
|
await routes.remove(ctx)
|
|
|
|
assert.strictEqual(ctx.status, 204)
|
|
assert.ok(stubCacheRemove.called)
|
|
assert.strictEqual(stubCacheRemove.firstCall[0], assertSiteName)
|
|
assert.strictEqual(stubCacheRemove.firstCall[1], assertFilename)
|
|
})
|
|
})
|