Add tests for emoji sheet; style and nits
This commit is contained in:
@@ -11,10 +11,18 @@ require("xxhash-wasm")().then(h => hasher = h)
|
||||
|
||||
const {sync, as, select} = require("../../passthrough")
|
||||
|
||||
/** @type {import("../../m2d/actions/emoji-sheet")} */
|
||||
const emojiSheet = sync.require("../../m2d/actions/emoji-sheet")
|
||||
/** @type {import("../../m2d/converters/emoji-sheet")} */
|
||||
const emojiSheetConverter = sync.require("../../m2d/converters/emoji-sheet")
|
||||
|
||||
const schema = {
|
||||
params: z.object({
|
||||
server_name: z.string(),
|
||||
media_id: z.string()
|
||||
}),
|
||||
sheet: z.object({
|
||||
e: z.array(z.string()).or(z.string())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -27,8 +35,16 @@ function getAPI(event) {
|
||||
return event.context.api || sync.require("../../matrix/api")
|
||||
}
|
||||
|
||||
function verifyMediaHash(serverName, mediaId) {
|
||||
const serverAndMediaID = `${serverName}/${mediaId}`
|
||||
/**
|
||||
* @param {H3Event} event
|
||||
* @returns {typeof emojiSheet["getAndConvertEmoji"]}
|
||||
*/
|
||||
function getMxcDownloader(event) {
|
||||
/* c8 ignore next */
|
||||
return event.context.mxcDownloader || emojiSheet.getAndConvertEmoji
|
||||
}
|
||||
|
||||
function verifyMediaHash(serverAndMediaID) {
|
||||
const unsignedHash = hasher.h64(serverAndMediaID)
|
||||
const signedHash = unsignedHash - 0x8000000000000000n // shifting down to signed 64-bit range
|
||||
|
||||
@@ -44,7 +60,7 @@ function verifyMediaHash(serverName, mediaId) {
|
||||
as.router.get(`/download/matrix/:server_name/:media_id`, defineEventHandler(async event => {
|
||||
const params = await getValidatedRouterParams(event, schema.params.parse)
|
||||
|
||||
verifyMediaHash(params.server_name, params.media_id)
|
||||
verifyMediaHash(`${params.server_name}/${params.media_id}`)
|
||||
const api = getAPI(event)
|
||||
const res = await api.getMedia(`mxc://${params.server_name}/${params.media_id}`)
|
||||
|
||||
@@ -57,33 +73,20 @@ as.router.get(`/download/matrix/:server_name/:media_id`, defineEventHandler(asyn
|
||||
return res.body
|
||||
}))
|
||||
|
||||
const emojiSchema = z.object({
|
||||
'e': z.array(z.string()).or(z.string())
|
||||
})
|
||||
|
||||
const emojiSheet = sync.require("../../m2d/actions/emoji-sheet")
|
||||
const emojiSheetConverter = sync.require("../../m2d/converters/emoji-sheet")
|
||||
|
||||
as.router.get(`/emoji/matrix`, defineEventHandler(async event => {
|
||||
|
||||
const query = await getValidatedQuery(event, emojiSchema.parse)
|
||||
as.router.get(`/download/sheet`, defineEventHandler(async event => {
|
||||
const query = await getValidatedQuery(event, schema.sheet.parse)
|
||||
|
||||
/** remember that these have no mxc:// protocol in the string for space reasons */
|
||||
let mxcs = query.e
|
||||
if(!Array.isArray(mxcs)) {
|
||||
if (!Array.isArray(mxcs)) {
|
||||
mxcs = [mxcs]
|
||||
}
|
||||
|
||||
for(let mxc of mxcs) {
|
||||
const mediaParts = mxc.match(/^mxc:\/\/([^/]+)\/(\w+)$/)
|
||||
if (!mediaParts) return undefined
|
||||
verifyMediaHash(mediaParts[1], mediaParts[2])
|
||||
for (const serverAndMediaID of mxcs) {
|
||||
verifyMediaHash(serverAndMediaID)
|
||||
}
|
||||
const buffer = await emojiSheetConverter.compositeMatrixEmojis(mxcs, emojiSheet.getAndConvertEmoji)
|
||||
|
||||
const contentType = 'image/png'
|
||||
|
||||
setResponseStatus(event, 200)
|
||||
setResponseHeader(event, "Content-Type", contentType)
|
||||
setResponseHeader(event, "Transfer-Encoding", "chunked")
|
||||
const buffer = await emojiSheetConverter.compositeMatrixEmojis(mxcs.map(s => `mxc://${s}`), getMxcDownloader(event))
|
||||
setResponseHeader(event, "Content-Type", "image/png")
|
||||
return buffer
|
||||
}))
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// @ts-check
|
||||
|
||||
const fs = require("fs")
|
||||
const {convertImageStream} = require("../../m2d/converters/emoji-sheet")
|
||||
const tryToCatch = require("try-to-catch")
|
||||
const {test} = require("supertape")
|
||||
const {router} = require("../../../test/web")
|
||||
@@ -33,3 +35,52 @@ test("web download matrix: works if a known attachment", async t => {
|
||||
t.equal(event.node.res.statusCode, 200)
|
||||
t.equal(event.node.res.getHeader("content-type"), "image/png")
|
||||
})
|
||||
|
||||
/**
|
||||
* MOCK: Gets the emoji from the filesystem and converts to uncompressed PNG data.
|
||||
* @param {string} mxc a single mxc:// URL
|
||||
* @returns {Promise<Buffer | undefined>} uncompressed PNG data, or undefined if the downloaded emoji is not valid
|
||||
*/
|
||||
async function mockGetAndConvertEmoji(mxc) {
|
||||
const id = mxc.match(/\/([^./]*)$/)?.[1]
|
||||
let s
|
||||
if (fs.existsSync(`test/res/${id}.png`)) {
|
||||
s = fs.createReadStream(`test/res/${id}.png`)
|
||||
} else {
|
||||
s = fs.createReadStream(`test/res/${id}.gif`)
|
||||
}
|
||||
return convertImageStream(s, () => {
|
||||
s.pause()
|
||||
s.emit("end")
|
||||
})
|
||||
}
|
||||
|
||||
test("web sheet: single emoji", async t => {
|
||||
const event = {}
|
||||
const sheet = await router.test("get", "/download/sheet?e=cadence.moe%2FRLMgJGfgTPjIQtvvWZsYjhjy", {
|
||||
event,
|
||||
mxcDownloader: mockGetAndConvertEmoji
|
||||
})
|
||||
t.equal(event.node.res.statusCode, 200)
|
||||
t.equal(sheet.subarray(0, 90).toString("base64"), "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAPoAAAD6AG1e1JrAAALoklEQVR4nM1ZaVBU2RU+LZSIGnAvFUtcRkSk6abpbkDH")
|
||||
})
|
||||
|
||||
test("web sheet: multiple sources", async t => {
|
||||
const event = {}
|
||||
const sheet = await router.test("get", "/download/sheet?e=cadence.moe%2FWbYqNlACRuicynBfdnPYtmvc&e=cadence.moe%2FHYcztccFIPgevDvoaWNsEtGJ", {
|
||||
event,
|
||||
mxcDownloader: mockGetAndConvertEmoji
|
||||
})
|
||||
t.equal(event.node.res.statusCode, 200)
|
||||
t.equal(sheet.subarray(0, 90).toString("base64"), "iVBORw0KGgoAAAANSUhEUgAAAGAAAAAwCAYAAADuFn/PAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAT/klEQVR4nOVcC3CVRZbuS2KAIMpDQt5PQkIScm/uvYRX")
|
||||
})
|
||||
|
||||
test("web sheet: big sheet", async t => {
|
||||
const event = {}
|
||||
const sheet = await router.test("get", "/download/sheet?e=cadence.moe%2FlHfmJpzgoNyNtYHdAmBHxXix&e=cadence.moe%2FMtRdXixoKjKKOyHJGWLsWLNU&e=cadence.moe%2FHXfFuougamkURPPMflTJRxGc&e=cadence.moe%2FikYKbkhGhMERAuPPbsnQzZiX&e=cadence.moe%2FAYPpqXzVJvZdzMQJGjioIQBZ&e=cadence.moe%2FUVuzvpVUhqjiueMxYXJiFEAj&e=cadence.moe%2FlHfmJpzgoNyNtYHdAmBHxXix&e=cadence.moe%2FMtRdXixoKjKKOyHJGWLsWLNU&e=cadence.moe%2FHXfFuougamkURPPMflTJRxGc&e=cadence.moe%2FikYKbkhGhMERAuPPbsnQzZiX&e=cadence.moe%2FAYPpqXzVJvZdzMQJGjioIQBZ&e=cadence.moe%2FUVuzvpVUhqjiueMxYXJiFEAj", {
|
||||
event,
|
||||
mxcDownloader: mockGetAndConvertEmoji
|
||||
})
|
||||
t.equal(event.node.res.statusCode, 200)
|
||||
t.equal(sheet.subarray(0, 90).toString("base64"), "iVBORw0KGgoAAAANSUhEUgAAAYAAAABgCAYAAAAU9KWJAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAgAElEQVR4nOx9B3hUVdr/KIpKL2nT0pPpLRNQkdXddV1c")
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user