import sharp from 'sharp-lite' import { Eltro as t, assert} from 'eltro' import fs from 'fs/promises' import { fileURLToPath } from 'url' import path from 'path' import { server, resetLog } from '../helper.server.mjs' import Client from '../helper.client.mjs' import encode from '../../api/jwt/encode.mjs' let __dirname = path.dirname(fileURLToPath(import.meta.url)) function resolve(file) { return path.resolve(path.join(__dirname, file)) } const currYear = new Date().getFullYear().toString() t.describe('Media (API)', () => { const client = new Client() const secret = 'asdf1234' let testFiles = [] t.after(function() { return Promise.all(testFiles.map(function(file) { return fs.unlink(resolve(`../../public/${file}`)).catch(function() {}) })) .then(function() { // return fs.unlink(resolve('../../public/existing/test.png')).catch(function() {}) }) }) t.timeout(10000).describe('POST /media', function temp() { t.test('should require authentication', async () => { resetLog() assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(server.log.warn.callCount, 0) let err = await assert.isRejected( client.upload('/media', resolve('test.png') ) ) assert.strictEqual(err.status, 422) assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Mm]issing/) assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(server.log.warn.firstCall[0], /[Mm]issing/) }) t.test('should verify token correctly', async () => { const assertToken = 'asdf.asdf.asdf' resetLog() assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(server.log.info.callCount, 0) let err = await assert.isRejected( client.upload('/media?token=' + assertToken, resolve('test.png') ) ) assert.strictEqual(err.status, 422) assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Ii]nvalid/) assert.strictEqual(server.log.error.callCount, 1) assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(server.log.warn.firstCall[0], /[Ii]nvalid/) assert.ok(server.log.error.lastCall[0] instanceof Error) assert.match(server.log.error.lastCall[1], new RegExp(assertToken)) }) t.test('should upload file and create file', async () => { let token = encode(null, { iss: 'development' }, secret) let data = await assert.isFulfilled( client.upload( `/media?token=${token}`, resolve('test.png') ) ) testFiles.push(data.path) assert.ok(data) assert.ok(data.filename) assert.ok(data.filename.startsWith(currYear)) assert.ok(data.path) let stats = await Promise.all([ fs.stat(resolve('test.png')), fs.stat(resolve(`../../public/${data.path}`)), ]) assert.strictEqual(stats[0].size, stats[1].size) let img = await sharp(resolve(`../../public/${data.path}`)).metadata() assert.strictEqual(img.width, 600) assert.strictEqual(img.height, 700) assert.strictEqual(img.format, 'png') }) }) t.timeout(10000).describe('POST /media/noprefix', function temp() { t.test('should require authentication', async () => { resetLog() assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(server.log.warn.callCount, 0) let err = await assert.isRejected( client.upload('/media/noprefix', resolve('test.png') ) ) assert.strictEqual(err.status, 422) assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Mm]issing/) assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(server.log.warn.firstCall[0], /[Mm]issing/) }) t.test('should verify token correctly', async () => { const assertToken = 'asdf.asdf.asdf' resetLog() assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(server.log.info.callCount, 0) let err = await assert.isRejected( client.upload('/media/noprefix?token=' + assertToken, resolve('test.png') ) ) assert.strictEqual(err.status, 422) assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Ii]nvalid/) assert.strictEqual(server.log.error.callCount, 1) assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(server.log.warn.firstCall[0], /[Ii]nvalid/) assert.ok(server.log.error.lastCall[0] instanceof Error) assert.match(server.log.error.lastCall[1], new RegExp(assertToken)) }) t.test('should upload and create file with no prefix', async () => { let token = encode(null, { iss: 'development' }, secret) let data = await assert.isFulfilled( client.upload( `/media/noprefix?token=${token}`, resolve('test.png') ) ) testFiles.push(data.path) assert.ok(data) assert.strictEqual(data.filename, 'test.png') assert.ok(data.path) let stats = await Promise.all([ fs.stat(resolve('test.png')), fs.stat(resolve('../../public/development/test.png')), ]) assert.strictEqual(stats[0].size, stats[1].size) let img = await sharp(resolve('../../public/development/test.png')).metadata() assert.strictEqual(img.width, 600) assert.strictEqual(img.height, 700) assert.strictEqual(img.format, 'png') }) }) t.timeout(10000).describe('POST /media/resize', function temp() { t.test('should require authentication', async () => { resetLog() assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(server.log.warn.callCount, 0) let err = await assert.isRejected( client.upload('/media/resize', resolve('test.png') ) ) assert.strictEqual(err.status, 422) assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Mm]issing/) assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(server.log.warn.firstCall[0], /[Mm]issing/) }) t.test('should verify token correctly', async () => { const assertToken = 'asdf.asdf.asdf' resetLog() assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(server.log.info.callCount, 0) let err = await assert.isRejected( client.upload('/media/resize?token=' + assertToken, resolve('test.png') ) ) assert.strictEqual(err.status, 422) assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Ii]nvalid/) assert.strictEqual(server.log.error.callCount, 1) assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(server.log.warn.firstCall[0], /[Ii]nvalid/) assert.ok(server.log.error.lastCall[0] instanceof Error) assert.match(server.log.error.lastCall[1], new RegExp(assertToken)) }) t.test('should upload file and create file', async () => { let token = encode(null, { iss: 'development' }, secret) let data = await assert.isFulfilled( client.upload( `/media/resize?token=${token}`, resolve('test.png'), 'POST', { } ) ) testFiles.push(data.path) assert.ok(data) assert.ok(data.filename) assert.ok(data.filename.startsWith(currYear)) assert.ok(data.path) let stats = await Promise.all([ fs.stat(resolve('test.png')), fs.stat(resolve(`../../public/${data.path}`)), ]) assert.strictEqual(stats[0].size, stats[1].size) let img = await sharp(resolve(`../../public/${data.path}`)).metadata() assert.strictEqual(img.width, 600) assert.strictEqual(img.height, 700) assert.strictEqual(img.format, 'png') }) t.test('should upload file and create multiple sizes for file', async () => { let token = encode(null, { iss: 'development' }, secret) let data = await assert.isFulfilled( client.upload( `/media/resize?token=${token}`, resolve('test.png'), 'POST', { test1: { format: 'jpeg', resize: { width: 300, }, blur: 0.5, flatten: {r:0,g:0,b:0}, trim: 1, extend: { top: 10, left: 10, bottom: 10, right: 10, background: {r:0,g:0,b:0} }, jpeg: { quality: 80, mozjpeg: true, } }, test2: { format: 'png', resize: { width: 150, }, png: { compressionLevel: 9, } }, } ) ) testFiles.push(data.path) testFiles.push(data.test1.path) testFiles.push(data.test2.path) assert.ok(data) assert.ok(data.filename) assert.ok(data.filename.startsWith(currYear)) assert.ok(data.path) assert.ok(data.test1.filename) assert.ok(data.test1.filename.startsWith(currYear)) assert.ok(data.test1.path) assert.ok(data.test2.filename) assert.ok(data.test2.filename.startsWith(currYear)) assert.ok(data.test2.path) let stats = await Promise.all([ fs.stat(resolve('test.png')), fs.stat(resolve(`../../public/${data.path}`)), ]) assert.strictEqual(stats[0].size, stats[1].size) let img = await sharp(resolve(`../../public/${data.path}`)).metadata() assert.strictEqual(img.width, 600) assert.strictEqual(img.height, 700) assert.strictEqual(img.format, 'png') img = await sharp(resolve(`../../public/${data.test1.path}`)).metadata() assert.strictEqual(img.width, 320) assert.strictEqual(img.height, 413) assert.strictEqual(img.format, 'jpeg') img = await sharp(resolve(`../../public/${data.test2.path}`)).metadata() assert.strictEqual(img.width, 150) assert.strictEqual(img.height, 175) assert.strictEqual(img.format, 'png') }) t.test('should upload file and support base64 output', async () => { let token = encode(null, { iss: 'development' }, secret) let data = await assert.isFulfilled( client.upload( `/media/resize?token=${token}`, resolve('test.png'), 'POST', { outtest: { out: 'base64', format: 'jpeg', resize: { width: 10, }, jpeg: { quality: 80, mozjpeg: true, } }, } ) ) assert.ok(data) assert.ok(data.filename) assert.ok(data.path) assert.ok(data.outtest.base64) testFiles.push(data.path) let stats = await Promise.all([ fs.stat(resolve('test.png')), fs.stat(resolve(`../../public/${data.path}`)), ]) assert.strictEqual(stats[0].size, stats[1].size) let img = await sharp(resolve(`../../public/${data.path}`)).metadata() assert.strictEqual(img.width, 600) assert.strictEqual(img.height, 700) assert.strictEqual(img.format, 'png') let bufferBase64 = Buffer.from(data.outtest.base64.slice(data.outtest.base64.indexOf(',')), 'base64') img = await sharp(bufferBase64).metadata() assert.strictEqual(img.width, 10) assert.strictEqual(img.height, 12) assert.strictEqual(img.format, 'jpeg') }) }) t.timeout(10000).describe('DELETE /media/:filename', function temp() { t.test('should require authentication', async () => { resetLog() assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(server.log.warn.callCount, 0) let err = await assert.isRejected( client.del('/media/20220105_101610_test1.jpg', resolve('test.png') ) ) assert.strictEqual(err.status, 422) assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Mm]issing/) assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(server.log.warn.firstCall[0], /[Mm]issing/) }) t.test('should verify token correctly', async () => { const assertToken = 'asdf.asdf.asdf' resetLog() assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(server.log.info.callCount, 0) let err = await assert.isRejected( client.del('/media/20220105_101610_test1.jpg?token=' + assertToken, resolve('test.png') ) ) assert.strictEqual(err.status, 422) assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Ii]nvalid/) assert.strictEqual(server.log.error.callCount, 1) assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(server.log.warn.firstCall[0], /[Ii]nvalid/) assert.ok(server.log.error.lastCall[0] instanceof Error) assert.match(server.log.error.lastCall[1], new RegExp(assertToken)) }) t.test('should remove the file', async () => { let token = encode(null, { iss: 'existing' }, secret) let data = await client.upload( `/media/noprefix?token=${token}`, resolve('test.png') ) let filepath = data.path testFiles.push(filepath) let files = await client.get('/media/existing') let found = false for (let file of files) { if (file.filename === 'test.png') { found = true } } assert.ok(found) await assert.isFulfilled( fs.stat(resolve(`../../public/${filepath}`)) ) await assert.isFulfilled( client.del(`/media/test.png?token=${token}`) ) testFiles.splice(testFiles.length - 1) files = await client.get('/media/existing') found = false for (let file of files) { if (file.filename === 'test.png') { found = true } } assert.notOk(found) await assert.isRejected( fs.stat(resolve(`../../public/${filepath}`)) ) }) }) t.describe('GET /media', function() { t.test('should require authentication', async () => { resetLog() assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(server.log.warn.callCount, 0) let err = await assert.isRejected(client.get('/media')) assert.strictEqual(err.status, 422) assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Mm]issing/) assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(server.log.warn.firstCall[0], /[Mm]issing/) }) t.test('should verify token correctly', async () => { const assertToken = 'asdf.asdf.asdf' resetLog() assert.strictEqual(server.log.error.callCount, 0) assert.strictEqual(server.log.warn.callCount, 0) assert.strictEqual(server.log.info.callCount, 0) let err = await assert.isRejected(client.get('/media?token=' + assertToken)) assert.strictEqual(err.status, 422) assert.match(err.message, /[Tt]oken/) assert.match(err.message, /[Ii]nvalid/) assert.strictEqual(server.log.error.callCount, 1) assert.strictEqual(server.log.warn.callCount, 2) assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string') assert.match(server.log.warn.firstCall[0], /[Tt]oken/) assert.match(server.log.warn.firstCall[0], /[Ii]nvalid/) assert.ok(server.log.error.lastCall[0] instanceof Error) assert.match(server.log.error.lastCall[1], new RegExp(assertToken)) }) t.test('should return list of files in specified folder', async () => { let token = encode(null, { iss: 'development' }, secret) let data = await client.get('/media?token=' + token) assert.ok(data.length) let found = false for (let file of data) { if (file.filename === '.gitkeep' && file.size === 0) { found = true break } } assert.ok(found) }) }) t.describe('GET /media/:site', function() { t.test('should give 404 on invalid sites', async () => { let err = await assert.isRejected(client.get('/media/development')) assert.strictEqual(err.status, 404) err = await assert.isRejected(client.get('/media/nonexisting')) assert.strictEqual(err.status, 404) err = await assert.isRejected(client.get('/media/blabla')) assert.strictEqual(err.status, 404) }) t.test('should otherwise return list of files for a public site', async () => { let files = await client.get('/media/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) }) }) })