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) }) })