teqo-backup/tests/unit/privateMediaUpload.unit.spec.ts
Francisco Solla 57b0d168d4
Some checks are pending
Deploy / preflight (push) Waiting to run
Deploy / verify (push) Blocked by required conditions
Deploy / deploy staging (teqo-staging) (push) Blocked by required conditions
Deploy / requeue when main advanced (push) Blocked by required conditions
Deploy / deploy production (teqo-1313) (push) Blocked by required conditions
feat(C199-large): gravações do acervo sem o teto de 4 GB
O diálogo, a rota e o stream deixam de recusar arquivos acima de 4 GiB; o
nome do arquivo e o `formatRecordingFileSize` continuam iguais. Acima de
2 GiB a mídia não passa mais pelo `fs.readFile` do Payload local API: a row
nasce de um placeholder de 0 byte e a mesma key é sobrescrita por upload
multipart direto no S3 (padrão C225-large, extraído para
`utilities/privateMedia/privateMediaUpload.ts` e reusado pelo espelho de
falas web, que mantém o teto de 5 GiB via `maxBytes` do chamador). O objeto
é removido se a transação falhar, sem row fantasma, e o upload multipart de
gravações não tem teto (`Infinity`).
2026-09-26 14:40:16 -03:00

140 lines
4.6 KiB
TypeScript

// @vitest-environment node
import { mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { describe, expect, it } from 'vitest'
import { createPrivateMediaFromFile } from '@/utilities/privateMedia/privateMediaUpload'
const withTempFile = async (
bytes: Buffer,
run: (path: string, dir: string) => Promise<void>,
): Promise<void> => {
const dir = await mkdtemp(join(tmpdir(), 'private-media-upload-test-'))
try {
const path = join(dir, 'source-video.mp4')
await writeFile(path, bytes)
await run(path, dir)
} finally {
await rm(dir, { recursive: true, force: true })
}
}
describe('private media from a local file', () => {
it('uses the Payload path below the buffer boundary', async () => {
await withTempFile(Buffer.alloc(16), async (path) => {
const created: string[] = []
let uploads = 0
const result = await createPrivateMediaFromFile({
inputPath: path,
thresholdBytes: 1024,
create: async (filePath) => {
created.push(filePath)
return { id: 7, filename: 'source-video.mp4' }
},
update: async () => undefined,
uploadLarge: async () => {
uploads += 1
throw new Error('não deveria subir em multipart')
},
})
expect(result.id).toBe(7)
expect(created).toEqual([path])
expect(uploads).toBe(0)
await result.cleanup()
})
})
it('creates the row from a placeholder and overwrites it through the multipart seam', async () => {
await withTempFile(Buffer.alloc(16), async (path, dir) => {
const created: string[] = []
const updated: { id: number; data: { filesize: number; mimeType: string } }[] = []
const uploaded: { inputPath: string; filename?: string; maxBytes?: number }[] = []
const removed: string[] = []
const result = await createPrivateMediaFromFile({
inputPath: path,
placeholderPath: join(dir, 'placeholder', 'meu-video.mp4'),
thresholdBytes: 0,
maxBytes: Number.POSITIVE_INFINITY,
create: async (filePath) => {
created.push(filePath)
return { id: 42, filename: 'meu-video.mp4' }
},
update: async (id, data) => {
updated.push({ id, data })
return { id }
},
uploadLarge: async (args) => {
uploaded.push(args)
return {
filename: args.filename ?? 'meu-video.mp4',
mimeType: 'video/mp4',
filesize: 16,
url: 'http://127.0.0.1:3900/teqo-media/meu-video.mp4',
}
},
removeLarge: async ({ filename }) => {
removed.push(filename)
},
})
expect(result.id).toBe(42)
expect(created[0]).toContain(join('placeholder', 'meu-video.mp4'))
expect((await stat(created[0])).size).toBe(0)
expect(await readFile(created[0])).toEqual(Buffer.alloc(0))
expect(uploaded).toEqual([
{ inputPath: path, filename: 'meu-video.mp4', maxBytes: Number.POSITIVE_INFINITY },
])
expect(updated).toEqual([{ id: 42, data: { filesize: 16, mimeType: 'video/mp4' } }])
expect(removed).toEqual([])
await result.cleanup()
expect(removed).toEqual(['meu-video.mp4'])
})
})
it('removes the object when the multipart upload fails', async () => {
await withTempFile(Buffer.alloc(16), async (path) => {
const removed: string[] = []
await expect(
createPrivateMediaFromFile({
inputPath: path,
thresholdBytes: 0,
create: async () => ({ id: 9, filename: 'falha.mp4' }),
update: async () => undefined,
uploadLarge: async () => {
throw new Error('S3 indisponível.')
},
removeLarge: async ({ filename }) => {
removed.push(filename)
},
}),
).rejects.toThrow('S3 indisponível.')
expect(removed).toEqual(['falha.mp4'])
})
})
it('fails closed when the placeholder row has no filename', async () => {
await withTempFile(Buffer.alloc(16), async (path) => {
let uploads = 0
await expect(
createPrivateMediaFromFile({
inputPath: path,
thresholdBytes: 0,
create: async () => ({ id: 9, filename: null }),
update: async () => undefined,
uploadLarge: async () => {
uploads += 1
throw new Error('não deveria subir')
},
removeLarge: async () => undefined,
}),
).rejects.toThrow('filename')
expect(uploads).toBe(0)
})
})
})