mirror of
https://github.com/langgenius/dify.git
synced 2026-10-04 13:47:55 +08:00
77 lines
2.8 KiB
TypeScript
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,
|
|
)
|
|
})
|
|
})
|