1
0
Fork 0
forked from svrjs/svrjs
This repository has been archived on 2024-11-10. You can view files and clone it, but cannot push or open issues or pull requests.
svrjs/tests/middleware/staticFileServingAndDirectoryListing.test.js

244 lines
6.8 KiB
JavaScript

const middleware = require("../../src/middleware/staticFileServingAndDirectoryListings.js");
const fs = require("fs");
const http = require("http");
const httpMocks = require("node-mocks-http");
jest.mock("fs");
describe("Static file serving and directory listings middleware", () => {
let req, res, logFacilities, config, next;
beforeEach(() => {
req = httpMocks.createRequest({
method: "GET",
url: "/",
headers: {
host: "example.com",
"accept-encoding": "gzip, deflate, br",
"user-agent": "Mozilla/5.0"
},
socket: {
localAddress: "127.0.0.1"
}
});
req.parsedURL = {
pathname: "/"
};
req.originalParsedURL = {
pathname: "/"
};
res = httpMocks.createResponse({
eventEmitter: require("events").EventEmitter
});
res.error = (statusCode) => {
// Very simple replacement of res.error
res.writeHead(statusCode, { "Content-Type": "text/plain" });
res.end(statusCode + " " + http.STATUS_CODES[statusCode]);
};
res.head = "";
res.foot = "";
logFacilities = {
errmessage: jest.fn(),
resmessage: jest.fn()
};
config = {
enableDirectoryListing: true,
enableDirectoryListingVHost: [],
enableCompression: true,
dontCompress: [],
generateServerString: jest.fn().mockReturnValue("Server")
};
next = jest.fn();
});
afterEach(() => {
jest.clearAllMocks();
});
test("should return 404 if file does not exist", async () => {
fs.stat.mockImplementation((path, cb) => {
cb({ code: "ENOENT" });
});
await middleware(req, res, logFacilities, config, next);
expect(res.statusCode).toBe(404);
expect(logFacilities.errmessage).toHaveBeenCalledWith(
"Resource not found."
);
});
test("should return 403 if directory listing is disabled", async () => {
config.enableDirectoryListing = false;
fs.stat.mockImplementation((path, cb) => {
cb(null, { isDirectory: () => true, isFile: () => false });
});
await middleware(req, res, logFacilities, config, next);
expect(res.statusCode).toBe(403);
expect(logFacilities.errmessage).toHaveBeenCalledWith(
"Directory listing is disabled."
);
});
test("should return 403 if access is denied", async () => {
fs.stat.mockImplementation((path, cb) => {
cb({ code: "EACCES" });
});
await middleware(req, res, logFacilities, config, next);
expect(res.statusCode).toBe(403);
expect(logFacilities.errmessage).toHaveBeenCalledWith("Access denied.");
});
test("should return 414 if the URI is too long", async () => {
fs.stat.mockImplementation((path, cb) => {
cb({ code: "ENAMETOOLONG" });
});
await middleware(req, res, logFacilities, config, next);
expect(res.statusCode).toBe(414);
});
test("should return 503 if the server is unable to handle the request", async () => {
fs.stat.mockImplementation((path, cb) => {
cb({ code: "EMFILE" });
});
await middleware(req, res, logFacilities, config, next);
expect(res.statusCode).toBe(503);
});
test("should return 508 if a loop is detected in symbolic links", async () => {
fs.stat.mockImplementation((path, cb) => {
cb({ code: "ELOOP" });
});
await middleware(req, res, logFacilities, config, next);
expect(res.statusCode).toBe(508);
expect(logFacilities.errmessage).toHaveBeenCalledWith(
"Symbolic link loop detected."
);
});
test("should return 500 if an unknown error occurs", async () => {
fs.stat.mockImplementation((path, cb) => {
cb(new Error("Unknown error"));
});
await middleware(req, res, logFacilities, config, next);
expect(res.statusCode).toBe(500);
});
test("should return 501 if the file is a block device, character device, FIFO, or socket", async () => {
fs.stat.mockImplementation((path, cb) => {
cb(null, {
isDirectory: () => false,
isFile: () => false,
isBlockDevice: () => true
});
});
await middleware(req, res, logFacilities, config, next);
expect(res.statusCode).toBe(501);
expect(logFacilities.errmessage).toHaveBeenCalledWith(
expect.stringContaining("doesn't support block devices")
);
});
test("should return a directory listing if the path is a directory and directory listing is enabled", async () => {
fs.readdir.mockImplementation((path, cb) => {
cb(null, ["file1.txt", "file2.txt"]);
});
fs.readFile.mockImplementation((path, cb) => {
if (path.match(/(?:^|\/)file[12]\.txt$/)) {
cb(null, Buffer.from("test"));
} else {
cb({ code: "ENOENT" });
}
});
fs.stat.mockImplementation((path, cb) => {
if (!path.match(/(?:^|\/)file[12]\.txt$/)) {
cb(null, { isDirectory: () => true, isFile: () => false });
} else {
cb(null, {
isDirectory: () => false,
isFile: () => true,
size: 1024,
mtime: new Date()
});
}
});
await middleware(req, res, logFacilities, config, next);
expect(res.statusCode).toBe(200);
expect(res._getData()).toContain("Directory: /");
expect(res._getData()).toContain("file1.txt");
expect(res._getData()).toContain("file2.txt");
});
test("should serve static file if the path is a file", async () => {
req.headers["accept-encoding"] = undefined;
req.path = "/file.txt";
req.parsedURL.pathname = "/file.txt";
req.originalParsedURL.pathname = "/file.txt";
fs.stat.mockImplementation((path, cb) => {
if (!path.match(/(?:^|\/)file\.txt$/)) {
cb(null, { isDirectory: () => true, isFile: () => false });
} else {
cb(null, {
isDirectory: () => false,
isFile: () => true,
size: 9
});
}
});
let mockEndListener = () => {};
let mockDataSent = false;
const mockStream = {
on: (event, listener) => {
if (event == "open") {
listener();
} else if (event == "data") {
if (!mockDataSent) {
listener(Buffer.from("mock data"));
mockDataSent = true;
}
mockEndListener();
} else if (event == "end") {
mockEndListener = listener;
if (mockDataSent) mockEndListener();
}
return mockStream;
},
once: (event, listener) => {
mockStream.on(event, listener);
},
pipe: (destStream) => {
if (!mockDataSent) {
destStream.end("mock data");
}
return destStream;
}
};
fs.createReadStream.mockImplementation(() => {
return mockStream;
});
await middleware(req, res, logFacilities, config, next);
expect(res.statusCode).toBe(200);
expect(res._getData()).toBe("mock data");
});
});