Files
dify/cli/src/api/file-upload.test.ts
T

77 lines
2.8 KiB
TypeScript

import type { StubServer } from '@test/fixtures/stub-server'
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { testHttpClient } from '@test/fixtures/http-client'
import { jsonResponder, startStubServer } from '@test/fixtures/stub-server'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { isHttpClientError } from '@/errors/base'
import { FileUploadClient } from './file-upload.js'
const UPLOADED = {
id: 'file-1',
name: 'hello.png',
size: 5,
extension: 'png',
mime_type: 'image/png',
}
function makeClient(host: string): FileUploadClient {
return new FileUploadClient(testHttpClient(host, 'dfoa_test'))
}
describe('FileUploadClient.upload', () => {
let stub: StubServer
let dir: string
beforeEach(async () => {
dir = await mkdtemp(join(tmpdir(), 'difyctl-upload-'))
})
afterEach(async () => {
await stub?.stop()
await rm(dir, { recursive: true, force: true })
})
it('POSTs multipart/form-data (boundary intact, no JSON content-type) and returns the parsed file', async () => {
const filePath = join(dir, 'hello.png')
await writeFile(filePath, 'hello')
stub = await startStubServer(cap => jsonResponder(200, UPLOADED, cap))
const result = await makeClient(stub.url).upload('app-1', filePath)
expect(stub.captured.method).toBe('POST')
expect(stub.captured.url).toBe('/openapi/v1/apps/app-1/files/upload')
// The client must let fetch own the multipart Content-Type + boundary; it
// must NOT coerce this to application/json the way a json body would.
const contentType = stub.captured.headers?.['content-type'] ?? ''
expect(contentType).toMatch(/^multipart\/form-data; boundary=/)
// Bearer still applied on the upload path.
expect(stub.captured.headers?.authorization).toBe('Bearer dfoa_test')
// The multipart payload carries the filename and the file bytes.
expect(stub.captured.body).toContain('filename="hello.png"')
expect(stub.captured.body).toContain('hello')
expect(result.id).toBe('file-1')
})
it('encodes the app id in the path', async () => {
const filePath = join(dir, 'a.txt')
await writeFile(filePath, 'x')
stub = await startStubServer(cap => jsonResponder(200, UPLOADED, cap))
await makeClient(stub.url).upload('app/with space', filePath)
expect(stub.captured.url).toBe('/openapi/v1/apps/app%2Fwith%20space/files/upload')
})
it('propagates a server 413 as a classified BaseError', async () => {
const filePath = join(dir, 'big.bin')
await writeFile(filePath, 'data')
stub = await startStubServer(cap => jsonResponder(413, { error: 'file too large' }, cap))
await expect(makeClient(stub.url).upload('app-1', filePath)).rejects.toSatisfy(
err => isHttpClientError(err) && err.httpStatus === 413,
)
})
})