Files
minimax-ai__cli/test/files/download.test.ts
2026-07-19 01:49:32 +08:00

112 lines
3.6 KiB
TypeScript

import { afterEach, describe, expect, it } from 'bun:test';
import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { downloadFile } from '../../src/files/download';
const originalFetch = globalThis.fetch;
const tempDirs: string[] = [];
function makeTempDir(): string {
const dir = mkdtempSync(join(tmpdir(), 'mmx-download-test-'));
tempDirs.push(dir);
return dir;
}
afterEach(() => {
globalThis.fetch = originalFetch;
for (const dir of tempDirs.splice(0)) {
rmSync(dir, { recursive: true, force: true });
}
});
describe('downloadFile', () => {
it('keeps an existing destination intact when the download stream fails', async () => {
const dir = makeTempDir();
const destPath = join(dir, 'video.mp4');
writeFileSync(destPath, 'original');
globalThis.fetch = (async () => {
let sentChunk = false;
const stream = new ReadableStream<Uint8Array>({
pull(controller) {
if (!sentChunk) {
sentChunk = true;
controller.enqueue(new TextEncoder().encode('partial'));
return;
}
controller.error(new Error('stream failed'));
},
});
return new Response(stream, {
status: 200,
headers: { 'content-length': '100' },
});
}) as unknown as typeof fetch;
await expect(
downloadFile('https://example.com/video.mp4', destPath, { quiet: true, retries: 0 }),
).rejects.toThrow('Download failed');
expect(readFileSync(destPath, 'utf-8')).toBe('original');
expect(readdirSync(dir)).toEqual(['video.mp4']);
});
it('replaces the destination only after a successful download', async () => {
const dir = makeTempDir();
const destPath = join(dir, 'video.mp4');
writeFileSync(destPath, 'original');
globalThis.fetch = (async () => new Response(new TextEncoder().encode('new'), {
status: 200,
headers: { 'content-length': '3' },
})) as unknown as typeof fetch;
await expect(
downloadFile('https://example.com/video.mp4', destPath, { quiet: true, retries: 0 }),
).resolves.toEqual({ size: 3 });
expect(readFileSync(destPath, 'utf-8')).toBe('new');
expect(existsSync(destPath)).toBe(true);
expect(readdirSync(dir)).toEqual(['video.mp4']);
});
it('rejects a cleanly ended response whose body is shorter than Content-Length', async () => {
const dir = makeTempDir();
const destPath = join(dir, 'video.mp4');
writeFileSync(destPath, 'original');
globalThis.fetch = (async () => new Response(new TextEncoder().encode('new'), {
status: 200,
headers: { 'content-length': '10' },
})) as unknown as typeof fetch;
await expect(
downloadFile('https://example.com/video.mp4', destPath, { quiet: true, retries: 0 }),
).rejects.toThrow('Download truncated: expected 10 bytes, received 3');
expect(readFileSync(destPath, 'utf-8')).toBe('original');
expect(readdirSync(dir)).toEqual(['video.mp4']);
});
it('does not compare decoded response bytes with compressed Content-Length', async () => {
const dir = makeTempDir();
const destPath = join(dir, 'video.mp4');
globalThis.fetch = (async () => new Response(new TextEncoder().encode('new'), {
status: 200,
headers: {
'content-encoding': 'gzip',
'content-length': '10',
},
})) as unknown as typeof fetch;
await expect(
downloadFile('https://example.com/video.mp4', destPath, { quiet: true, retries: 0 }),
).resolves.toEqual({ size: 3 });
expect(readFileSync(destPath, 'utf-8')).toBe('new');
});
});