als-send-file
Version:
file serving with advanced options for caching, headers, and error handling, compatible with Express middleware.
153 lines (132 loc) • 6.3 kB
JavaScript
const { describe, it, beforeEach, afterEach, after } = require('node:test');
const assert = require('node:assert');
const http = require('http');
const sendFile = require('../lib/send-file.js');
const { writeFileSync, unlinkSync, promises } = require('fs');
const path = require('path');
const { checkPrimeSync } = require('node:crypto');
// Создание временного файла для тестирования
const testFilePath = path.join(__dirname, 'temp-test-file.txt');
const fileContent = 'Hello, this is a test file for testing sendFile functionality.';
writeFileSync(testFilePath, fileContent);
describe('sendFile Module Tests', () => {
let request, response;
beforeEach(() => {
request = new http.IncomingMessage();
response = new http.ServerResponse(request);
response.setHeader = function (key, value) {
this.headers = this.headers || {};
this.headers[key] = value;
};
response.writeHead = function (statusCode, headers) {
this.statusCode = statusCode;
this.headers = { ...this.headers, ...headers };
};
response.end = function () { this.ended = true; };
});
afterEach(() => {
response.end();
});
// Cleanup after tests
after(() => {
unlinkSync(testFilePath);
});
//#region req.headers.range
it('should handle range requests correctly', async () => {
request.headers['range'] = 'bytes=0-5';
await sendFile(request, response, testFilePath, { size: fileContent.length });
const { statusCode, headers } = response;
// console.log({statusCode,headers})
assert.strictEqual(response.statusCode, 206);
assert.strictEqual(response.headers['Content-Range'], `bytes 0-5/${fileContent.length}`);
assert.strictEqual(response.headers['Content-Length'], '6');
});
it('should reject invalid range requests', async () => {
request.headers['range'] = 'bytes=-';
let error = null;
await sendFile(request, response, testFilePath, { size: fileContent.length }, (res, code, msg) => {
error = { res, code, msg };
});
assert(error);
assert.strictEqual(error.code, 416);
});
it('should handle range requests starting from a specific byte', async () => {
request.headers['range'] = 'bytes=7-';
await sendFile(request, response, testFilePath, { size: fileContent.length });
const { statusCode, headers } = response;
assert.strictEqual(response.statusCode, 206);
assert.strictEqual(headers['Content-Range'], `bytes 7-${fileContent.length - 1}/${fileContent.length}`);
assert.strictEqual(headers['Content-Length'], (fileContent.length - 7).toString());
});
it('should handle range requests ending at a specific byte', async () => {
request.headers['range'] = 'bytes=-6';
await sendFile(request, response, testFilePath, { size: fileContent.length });
const { statusCode, headers } = response;
assert.strictEqual(response.statusCode, 206);
assert.strictEqual(headers['Content-Range'], `bytes ${fileContent.length - 6}-${fileContent.length - 1}/${fileContent.length}`);
assert.strictEqual(headers['Content-Length'], '6');
});
it('should handle invalid range that is out of bounds', async () => {
request.headers['range'] = `bytes=${fileContent.length}-1000`;
let error = null;
await sendFile(request, response, testFilePath, { size: fileContent.length }, (res, code, msg) => {
error = { res, code, msg };
});
assert(error);
assert.strictEqual(error.code, 416);
});
it('should handle valid range that exceeds file length', async () => {
request.headers['range'] = `bytes=0-${fileContent.length + 100}`;
await sendFile(request, response, testFilePath, { size: fileContent.length });
const { statusCode, headers } = response;
assert.strictEqual(response.statusCode, 206);
assert.strictEqual(headers['Content-Range'], `bytes 0-${fileContent.length - 1}/${fileContent.length}`);
assert.strictEqual(headers['Content-Length'], fileContent.length.toString());
});
//#endregion
it('should send the entire file if no range is specified', async () => {
request.headers['range'] = undefined;
await sendFile(request, response, testFilePath, { size: fileContent.length });
assert.strictEqual(response.statusCode, 200);
// assert.strictEqual(response.headers['Content-Length'], fileContent.length.toString());
assert.strictEqual(response.headers['Content-Length'], undefined);
});
it('should handle errors from the file stream', async () => {
const brokenFilePath = path.join(__dirname, 'nonexistent.txt');
let errorCaught = null;
await sendFile(request, response, brokenFilePath, { size: 0 }, (res,code,msg) => {
errorCaught = {code,msg}
});
assert(errorCaught);
assert(errorCaught.code === 500)
assert(errorCaught.msg.includes('nonexistent.txt'))
});
it('should handle interrupted download', async () => {
let errorCaught = null;
let ended = false
const originalEnd = response.end
response.end = () => {originalEnd(),ended = true}
const promise = sendFile(request, response, testFilePath, { size: fileContent.length }, (res,code,msg) => {
errorCaught = {code,msg}
});
setTimeout(() => response.emit('close'), 1); // Имитация прерывания загрузки через 1 мс
response.on('end',() => ended = true)
const result = await promise
assert(ended)
assert(errorCaught === null)
});
it('should handle file deletion error', async () => {
const filePath = path.join(__dirname, 'temp-delete-file.txt');
const content = 'This file will be deleted during the request.'.repeat(10)
writeFileSync(filePath, content);
let errorCaught = null
const promise = sendFile(request, response, filePath, { size: content.length }, (res, code, msg) => {
errorCaught = { code, msg };
});
await promises.unlink(filePath)
await promise
assert(errorCaught)
assert(errorCaught.code === 500)
assert(errorCaught.msg.includes('temp-delete-file.txt'))
});
});