storage-upload/test/media/api.test.mjs

776 lines
24 KiB
JavaScript

import sharp from 'sharp'
import { Eltro as t, assert} from 'eltro'
import fs from 'fs/promises'
import { fileURLToPath } from 'url'
import path from 'path'
import { createClient, startServer, log, resetLog } from '../helper.server.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)', () => {
let client
let secret = 'asdf1234'
let testFiles = []
t.before(function() {
client = createClient()
return startServer()
})
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(log.error.callCount, 0)
assert.strictEqual(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(log.error.callCount, 0)
assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(log.warn.firstCall[0], /[Mm]issing/)
})
t.test('should verify token correctly', async () => {
const assertToken = 'asdf.asdf.asdf'
resetLog()
assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(log.warn.callCount, 0)
assert.strictEqual(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(log.error.callCount, 1)
assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(log.warn.firstCall[0], /[Ii]nvalid/)
assert.ok(log.error.lastCall[0] instanceof Error)
assert.match(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(log.error.callCount, 0)
assert.strictEqual(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(log.error.callCount, 0)
assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(log.warn.firstCall[0], /[Mm]issing/)
})
t.test('should verify token correctly', async () => {
const assertToken = 'asdf.asdf.asdf'
resetLog()
assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(log.warn.callCount, 0)
assert.strictEqual(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(log.error.callCount, 1)
assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(log.warn.firstCall[0], /[Ii]nvalid/)
assert.ok(log.error.lastCall[0] instanceof Error)
assert.match(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.test('should upload and create file with prefix if exists', async () => {
let token = encode(null, { iss: 'development' }, secret)
await fs.unlink(resolve(`../../public/development/test.png`)).catch(function() {})
let datas = []
datas.push(await client.upload(
`/media/noprefix?token=${token}`,
resolve('test.png')
))
datas.push(await client.upload(
`/media/noprefix?token=${token}`,
resolve('test.png')
))
testFiles.push(datas[0].path)
testFiles.push(datas[1].path)
let prefix = new Date().toISOString().replace(/-/g,'').split('T')[0]
let first = datas[0]
let second = datas[1]
assert.notStrictEqual(first.filename, second.filename)
assert.notOk(first.filename.startsWith(currYear))
assert.notOk(first.filename.startsWith(prefix))
assert.ok(second.filename.startsWith(currYear))
assert.ok(second.filename.startsWith(prefix))
let stats = await Promise.all([
fs.stat(resolve('test.png')),
fs.stat(resolve('../../public/development/test.png')),
fs.stat(resolve('../../public/development/' + second.filename)),
])
assert.strictEqual(stats[0].size, stats[1].size)
assert.strictEqual(stats[0].size, stats[2].size)
})
})
t.timeout(10000).describe('POST /media/resize', function temp() {
t.test('should require authentication', async () => {
resetLog()
assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(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(log.error.callCount, 0)
assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(log.warn.firstCall[0], /[Mm]issing/)
})
t.test('should verify token correctly', async () => {
const assertToken = 'asdf.asdf.asdf'
resetLog()
assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(log.warn.callCount, 0)
assert.strictEqual(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(log.error.callCount, 1)
assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(log.warn.firstCall[0], /[Ii]nvalid/)
assert.ok(log.error.lastCall[0] instanceof Error)
assert.match(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('POST /media/resize/:filename', function temp() {
let sourceFilename
let sourcePath
t.before(async function() {
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)
sourceFilename = data.filename
sourcePath = data.path
})
t.test('should require authentication', async () => {
resetLog()
assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(log.warn.callCount, 0)
let err = await assert.isRejected(
client.post(`/media/resize/${sourceFilename}`, {})
)
assert.strictEqual(err.status, 422)
assert.match(err.message, /[Tt]oken/)
assert.match(err.message, /[Mm]issing/)
assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(log.warn.firstCall[0], /[Mm]issing/)
})
t.test('should verify token correctly', async () => {
const assertToken = 'asdf.asdf.asdf'
resetLog()
assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(log.warn.callCount, 0)
assert.strictEqual(log.info.callCount, 0)
let err = await assert.isRejected(
client.post(`/media/resize/${sourceFilename}?token=${assertToken}`, {})
)
assert.strictEqual(err.status, 422)
assert.match(err.message, /[Tt]oken/)
assert.match(err.message, /[Ii]nvalid/)
assert.strictEqual(log.error.callCount, 1)
assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(log.warn.firstCall[0], /[Ii]nvalid/)
assert.ok(log.error.lastCall[0] instanceof Error)
assert.match(log.error.lastCall[1], new RegExp(assertToken))
})
t.test('should create multiple sizes for existing file', async () => {
let token = encode(null, { iss: 'development' }, secret)
let data = await assert.isFulfilled(
client.post(
`/media/resize/${sourceFilename}?token=${token}`,
{
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.test1.path)
testFiles.push(data.test2.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 img = await sharp(resolve(`../../public/${sourcePath}`)).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 base64 output of existing file', async () => {
let token = encode(null, { iss: 'development' }, secret)
let data = await assert.isFulfilled(
client.post(
`/media/resize/${sourceFilename}?token=${token}`,
{
outtest: {
out: 'base64',
format: 'jpeg',
resize: {
width: 10,
},
jpeg: {
quality: 80,
mozjpeg: true,
}
},
}
)
)
assert.ok(data)
assert.ok(data.outtest.base64)
let bufferBase64 = Buffer.from(data.outtest.base64.slice(data.outtest.base64.indexOf(',')), 'base64')
let 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(log.error.callCount, 0)
assert.strictEqual(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(log.error.callCount, 0)
assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(log.warn.firstCall[0], /[Mm]issing/)
})
t.test('should verify token correctly', async () => {
const assertToken = 'asdf.asdf.asdf'
resetLog()
assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(log.warn.callCount, 0)
assert.strictEqual(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(log.error.callCount, 1)
assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(log.warn.firstCall[0], /[Ii]nvalid/)
assert.ok(log.error.lastCall[0] instanceof Error)
assert.match(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(log.error.callCount, 0)
assert.strictEqual(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(log.error.callCount, 0)
assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(log.warn.firstCall[0], /[Mm]issing/)
})
t.test('should verify token correctly', async () => {
const assertToken = 'asdf.asdf.asdf'
resetLog()
assert.strictEqual(log.error.callCount, 0)
assert.strictEqual(log.warn.callCount, 0)
assert.strictEqual(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(log.error.callCount, 1)
assert.strictEqual(log.warn.callCount, 2)
assert.strictEqual(typeof(log.warn.firstCall[0]), 'string')
assert.match(log.warn.firstCall[0], /[Tt]oken/)
assert.match(log.warn.firstCall[0], /[Ii]nvalid/)
assert.ok(log.error.lastCall[0] instanceof Error)
assert.match(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)
})
})
})