The download endpoint served files under their internal uuid-based storage name; it now builds the Content-Disposition filename from the probed video title (persisted on submit) with the original extension, falling back to the old behavior when no title is available. The status page also gets an always-visible link back to the homepage. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
109 lines
3.6 KiB
TypeScript
109 lines
3.6 KiB
TypeScript
import { POST } from '../route'
|
|
import { prisma } from '@/lib/prisma'
|
|
import { downloadRateLimiter } from '@/lib/rate-limit'
|
|
|
|
jest.mock('@/lib/prisma', () => ({
|
|
prisma: { download: { create: jest.fn() } },
|
|
}))
|
|
jest.mock('@/lib/rate-limit', () => ({
|
|
downloadRateLimiter: { isLimited: jest.fn() },
|
|
}))
|
|
|
|
const mockCreate = prisma.download.create as jest.Mock
|
|
const mockIsLimited = downloadRateLimiter.isLimited as jest.Mock
|
|
|
|
function req(body: object, ip = '1.2.3.4') {
|
|
return new Request('http://localhost/api/downloads', {
|
|
method: 'POST',
|
|
headers: { 'content-type': 'application/json', 'x-forwarded-for': ip },
|
|
body: JSON.stringify(body),
|
|
})
|
|
}
|
|
|
|
describe('POST /api/downloads', () => {
|
|
beforeEach(() => {
|
|
mockIsLimited.mockReturnValue(false)
|
|
mockCreate.mockResolvedValue({ uuid: 'test-uuid' })
|
|
})
|
|
|
|
it('returns 429 when rate limited', async () => {
|
|
mockIsLimited.mockReturnValue(true)
|
|
const res = await POST(req({ url: 'https://y.com', format: 'mp4', quality: 'best', subtitles: false }))
|
|
expect(res.status).toBe(429)
|
|
})
|
|
|
|
it('returns 400 when url is missing', async () => {
|
|
const res = await POST(req({ format: 'mp4', quality: 'best', subtitles: false }))
|
|
expect(res.status).toBe(400)
|
|
})
|
|
|
|
it('returns 400 when format is missing', async () => {
|
|
const res = await POST(req({ url: 'https://y.com', quality: 'best', subtitles: false }))
|
|
expect(res.status).toBe(400)
|
|
})
|
|
|
|
it('returns 400 when clipEnd is not greater than clipStart', async () => {
|
|
const res = await POST(req({
|
|
url: 'https://y.com', format: 'mp4', quality: 'best', subtitles: false,
|
|
clipStart: 30, clipEnd: 10,
|
|
}))
|
|
expect(res.status).toBe(400)
|
|
})
|
|
|
|
it('returns 201 with uuid on success', async () => {
|
|
const res = await POST(req({ url: 'https://youtube.com/watch?v=abc', format: 'mp4', quality: 'best', subtitles: false }))
|
|
expect(res.status).toBe(201)
|
|
expect(await res.json()).toEqual({ uuid: 'test-uuid' })
|
|
})
|
|
|
|
it('passes correct fields to prisma', async () => {
|
|
await POST(req({ url: 'https://y.com/watch?v=x', format: 'mp3', quality: '720p', subtitles: true }, '9.9.9.9'))
|
|
expect(mockCreate).toHaveBeenCalledWith({
|
|
data: expect.objectContaining({
|
|
url: 'https://y.com/watch?v=x',
|
|
format: 'mp3',
|
|
quality: '720p',
|
|
subtitles: true,
|
|
ipAddress: '9.9.9.9',
|
|
}),
|
|
})
|
|
})
|
|
|
|
it('stores subtitleLangs as a comma-joined string', async () => {
|
|
await POST(req({
|
|
url: 'https://y.com', format: 'mp4', quality: 'best', subtitles: true,
|
|
subtitleLangs: ['fr', 'en'],
|
|
}))
|
|
expect(mockCreate).toHaveBeenCalledWith({
|
|
data: expect.objectContaining({ subtitleLangs: 'fr,en' }),
|
|
})
|
|
})
|
|
|
|
it('stores clip range and audio quality when provided', async () => {
|
|
await POST(req({
|
|
url: 'https://y.com', format: 'mp3', quality: 'best', subtitles: false,
|
|
clipStart: 10, clipEnd: 30, audioQuality: '192',
|
|
}))
|
|
expect(mockCreate).toHaveBeenCalledWith({
|
|
data: expect.objectContaining({ clipStart: 10, clipEnd: 30, audioQuality: '192' }),
|
|
})
|
|
})
|
|
|
|
it('stores the video title when provided', async () => {
|
|
await POST(req({
|
|
url: 'https://y.com', format: 'mp4', quality: 'best', subtitles: false,
|
|
title: 'My Video',
|
|
}))
|
|
expect(mockCreate).toHaveBeenCalledWith({
|
|
data: expect.objectContaining({ title: 'My Video' }),
|
|
})
|
|
})
|
|
|
|
it('stores a null title when not provided', async () => {
|
|
await POST(req({ url: 'https://y.com', format: 'mp4', quality: 'best', subtitles: false }))
|
|
expect(mockCreate).toHaveBeenCalledWith({
|
|
data: expect.objectContaining({ title: null }),
|
|
})
|
|
})
|
|
})
|