Jonatan Nilsson
68eef3a6b6
Removed large amount of dependencies. Added vips support and automatically resizing based on any criteria. Faster and leaner. Added ability to fetch listing of files in folders.
518 lines
16 KiB
JavaScript
518 lines
16 KiB
JavaScript
import { Eltro as t, assert, stub } from 'eltro'
|
|
import { createContext } from '../helper.server.mjs'
|
|
|
|
import MediaRoutes from '../../api/media/routes.mjs'
|
|
import { HttpError } from '../../api/error.mjs'
|
|
|
|
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].name, '.gitkeep')
|
|
assert.strictEqual(routes.filesCacheGet('development')[0].size, 0)
|
|
assert.strictEqual(routes.filesCacheGet('existing')[0].name, '20220105_101610_test1.jpg')
|
|
assert.strictEqual(routes.filesCacheGet('existing')[0].size, 15079)
|
|
assert.strictEqual(routes.filesCacheGet('existing')[1].name, '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].name, '20220105_101610_test1.jpg')
|
|
assert.strictEqual(files[0].size, 15079)
|
|
assert.strictEqual(files[1].name, '20220105_101610_test2.png')
|
|
assert.strictEqual(files[1].size, 31705)
|
|
|
|
routes.siteCache.get('existing').push({ name: '0000.png', size: 0 })
|
|
|
|
files = routes.filesCacheGet('existing')
|
|
assert.strictEqual(files[0].name, '0000.png')
|
|
assert.strictEqual(files[0].size, 0)
|
|
assert.strictEqual(files[1].name, '20220105_101610_test1.jpg')
|
|
assert.strictEqual(files[1].size, 15079)
|
|
assert.strictEqual(files[2].name, '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].name, assertName)
|
|
assert.strictEqual(routes.filesCacheGet('nonexisting')[0].size, assertSize)
|
|
})
|
|
})
|
|
|
|
t.describe('#upload', function() {
|
|
const stubVerify = stub()
|
|
const stubUpload = stub()
|
|
const stubStat = stub()
|
|
|
|
const routes = new MediaRoutes({
|
|
security: { verifyToken: stubVerify },
|
|
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()
|
|
const assertError = new Error('temp')
|
|
stubVerify.rejects(assertError)
|
|
|
|
let err = await assert.isRejected(routes.upload(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()
|
|
const assertSiteName = 'benshapiro'
|
|
const assertError = new Error('hello')
|
|
stubVerify.resolves(assertSiteName)
|
|
stubUpload.rejects(assertError)
|
|
|
|
let err = await assert.isRejected(routes.upload(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 set context status to 204 and file in result', async function() {
|
|
reset()
|
|
|
|
let ctx = createContext()
|
|
const assertSize = 1241412
|
|
const assertFilename = 'asdfsafd'
|
|
const assertSite = 'mario'
|
|
stubVerify.resolves(assertSite)
|
|
stubUpload.resolves({ filename: assertFilename })
|
|
stubStat.resolves({ size: assertSize })
|
|
await routes.upload(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].name, assertFilename)
|
|
assert.strictEqual(filesFromCache[0].size, assertSize)
|
|
})
|
|
})
|
|
|
|
t.describe('#resize', function() {
|
|
const stubVerifyToken = stub()
|
|
const stubVerifyBody = stub()
|
|
const stubUpload = stub()
|
|
const stubSharp = stub()
|
|
const stubSharpResize = 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,
|
|
rotate: stubSharpRotate,
|
|
toBuffer: stubSharpToBuffer,
|
|
}
|
|
stubStat.reset()
|
|
stubStat.resolves({ size: 0 })
|
|
stubSharp.reset()
|
|
stubSharp.returns(def)
|
|
stubSharpToFile.reset()
|
|
stubSharpToFile.resolves()
|
|
stubSharpJpeg.reset()
|
|
stubSharpJpeg.returns(def)
|
|
stubSharpResize.reset()
|
|
stubSharpResize.returns(def)
|
|
stubSharpRotate.reset()
|
|
stubSharpRotate.returns(def)
|
|
stubSharpPng.reset()
|
|
stubSharpPng.returns(def)
|
|
stubSharpToBuffer.reset()
|
|
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.resize(ctx))
|
|
|
|
assert.ok(stubVerifyToken.called)
|
|
assert.strictEqual(err, assertError)
|
|
assert.strictEqual(stubVerifyToken.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')
|
|
stubVerifyToken.resolves(assertSiteName)
|
|
stubUpload.rejects(assertError)
|
|
|
|
let err = await assert.isRejected(routes.resize(ctx))
|
|
|
|
assert.strictEqual(err, assertError)
|
|
assert.ok(stubUpload.called)
|
|
assert.strictEqual(stubUpload.firstCall[0], ctx)
|
|
assert.strictEqual(stubUpload.firstCall[1], assertSiteName)
|
|
})
|
|
|
|
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 otherwise return original in result', async function() {
|
|
reset()
|
|
|
|
let ctx = createContext({ req: { body: { }}})
|
|
const assertFilename = 'asdfsafd.png'
|
|
const assertSite = 'mario'
|
|
stubVerifyToken.resolves(assertSite)
|
|
stubUpload.resolves({ filename: assertFilename })
|
|
|
|
await routes.resize(ctx)
|
|
|
|
assert.notOk(stubSharp.called)
|
|
assert.strictEqual(ctx.body.original.filename, assertFilename)
|
|
assert.strictEqual(ctx.body.original.path, `/${assertSite}/${assertFilename}`)
|
|
})
|
|
|
|
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.original.filename, assertFilename + '.png')
|
|
assert.strictEqual(ctx.body.original.path, `/${assertSite}/${assertFilename}.png`)
|
|
assert.strictEqual(ctx.body[assertKey].filename, `${assertFilename}_${assertKey}\.jpg`)
|
|
assert.strictEqual(ctx.body[assertKey].path, `/${assertSite}/${assertFilename}_${assertKey}.jpg`)
|
|
})
|
|
|
|
t.test('should call sharp correctly if items and resize are specified', async function() {
|
|
reset()
|
|
const assertKey = 'herpderp'
|
|
const assertPng = { a: 1 }
|
|
const assertResize = { 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].resize = assertResize
|
|
|
|
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(stubSharpResize.called)
|
|
assert.strictEqual(stubSharpResize.firstCall[0], assertResize)
|
|
assert.strictEqual(ctx.body.original.filename, assertFilename + '.png')
|
|
assert.strictEqual(ctx.body.original.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].name, `${assertFilename}_${assertKey}\.png`)
|
|
assert.strictEqual(filesFromCache[0].size, 20)
|
|
assert.strictEqual(filesFromCache[1].name, 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].name, assertFilename)
|
|
})
|
|
})
|