mirror of
https://github.com/MasterAcnolo/Freedom-Loader.git
synced 2026-11-13 15:09:20 +01:00
214 lines
7.9 KiB
JavaScript
214 lines
7.9 KiB
JavaScript
jest.mock("../../server/services/download.services", () => ({
|
|
fetchDownload: jest.fn(),
|
|
cancelDownload: jest.fn(),
|
|
}));
|
|
jest.mock("../../server/logger", () => ({
|
|
logger: {
|
|
info: jest.fn(),
|
|
error: jest.fn(),
|
|
warn: jest.fn(),
|
|
debug: jest.fn(),
|
|
},
|
|
}));
|
|
jest.mock("../../server/helpers/validation.helpers", () => ({
|
|
isValidUrl: jest.fn(),
|
|
isSafePath: jest.fn(),
|
|
}));
|
|
jest.mock("../../server/helpers/notify.helpers", () => ({
|
|
notifyDownloadFinished: jest.fn(),
|
|
}));
|
|
jest.mock("../../config", () => ({
|
|
configFeatures: {
|
|
notifySystem: true,
|
|
},
|
|
}));
|
|
|
|
const {fetchDownload, cancelDownload} = require("../../server/services/download.services");
|
|
const {logger} = require("../../server/logger");
|
|
const {isValidUrl, isSafePath} = require("../../server/helpers/validation.helpers");
|
|
const {notifyDownloadFinished} = require("../../server/helpers/notify.helpers");
|
|
const {
|
|
downloadController,
|
|
cancelDownloadController,
|
|
progressController,
|
|
speedController,
|
|
stageController,
|
|
playlistInfoController,
|
|
} = require("../../server/controller/download.controller");
|
|
|
|
describe("download.controller", () => {
|
|
let req, res;
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
req = {body: {}, on: jest.fn()};
|
|
res = {
|
|
status: jest.fn().mockReturnThis(),
|
|
json: jest.fn().mockReturnThis(),
|
|
send: jest.fn().mockReturnThis(),
|
|
set: jest.fn().mockReturnThis(),
|
|
write: jest.fn().mockReturnThis(),
|
|
flushHeaders: jest.fn(),
|
|
end: jest.fn(),
|
|
};
|
|
});
|
|
|
|
describe("downloadController", () => {
|
|
test("returns 400 for invalid url", async () => {
|
|
isValidUrl.mockReturnValue(false);
|
|
req.body.url = "bad-url";
|
|
await downloadController(req, res);
|
|
expect(res.status).toHaveBeenCalledWith(400);
|
|
expect(res.json).toHaveBeenCalledWith({
|
|
error: {code: "INVALID_URL", message: "Invalid URL"},
|
|
});
|
|
});
|
|
|
|
test("returns 400 for unsafe path", async () => {
|
|
isValidUrl.mockReturnValue(true);
|
|
isSafePath.mockReturnValue(false);
|
|
req.body.url = "https://youtube.com/watch?v=123";
|
|
req.body.savePath = "C:\\Windows\\System32";
|
|
await downloadController(req, res);
|
|
expect(res.status).toHaveBeenCalledWith(400);
|
|
expect(res.json).toHaveBeenCalledWith({
|
|
error: {code: "INVALID_PATH", message: "Save path not allowed"},
|
|
});
|
|
expect(logger.warn).toHaveBeenCalled();
|
|
});
|
|
|
|
test("starts download and returns success", async () => {
|
|
isValidUrl.mockReturnValue(true);
|
|
isSafePath.mockReturnValue(true);
|
|
req.body = {
|
|
url: "https://youtube.com/watch?v=123",
|
|
audioOnly: "1",
|
|
quality: "720",
|
|
savePath: "/home/user/Downloads",
|
|
playlistTitle: "My Playlist",
|
|
};
|
|
fetchDownload.mockResolvedValue("/home/user/Downloads");
|
|
await downloadController(req, res);
|
|
expect(fetchDownload).toHaveBeenCalled();
|
|
const callArgs = fetchDownload.mock.calls[0][0];
|
|
expect(callArgs.url).toBe("https://youtube.com/watch?v=123");
|
|
expect(callArgs.audioOnly).toBe(true);
|
|
expect(callArgs.quality).toBe("720");
|
|
expect(callArgs.outputFolder).toBe("/home/user/Downloads");
|
|
expect(callArgs.playlistTitle).toBe("My Playlist");
|
|
expect(notifyDownloadFinished).toHaveBeenCalledWith(
|
|
"/home/user/Downloads",
|
|
true
|
|
);
|
|
expect(res.json).toHaveBeenCalledWith({
|
|
success: true,
|
|
message: "Download Done !",
|
|
outputFolder: "/home/user/Downloads",
|
|
});
|
|
});
|
|
|
|
test("defaults quality to best for invalid quality", async () => {
|
|
isValidUrl.mockReturnValue(true);
|
|
fetchDownload.mockResolvedValue("/downloads");
|
|
req.body = {url: "https://youtube.com/watch?v=123", quality: "invalid"};
|
|
await downloadController(req, res);
|
|
const callArgs = fetchDownload.mock.calls[0][0];
|
|
expect(callArgs.quality).toBe("best");
|
|
});
|
|
|
|
test("returns 409 when download already in progress", async () => {
|
|
isValidUrl.mockReturnValue(true);
|
|
fetchDownload.mockRejectedValue(new Error("Download already in progress"));
|
|
req.body.url = "https://youtube.com/watch?v=123";
|
|
await downloadController(req, res);
|
|
expect(res.status).toHaveBeenCalledWith(409);
|
|
expect(res.json).toHaveBeenCalledWith({
|
|
error: {code: "DOWNLOAD_IN_PROGRESS", message: "A download is already in progress"},
|
|
});
|
|
});
|
|
|
|
test("returns 500 on generic error", async () => {
|
|
isValidUrl.mockReturnValue(true);
|
|
fetchDownload.mockRejectedValue(new Error("yt-dlp failed"));
|
|
req.body.url = "https://youtube.com/watch?v=123";
|
|
await downloadController(req, res);
|
|
expect(res.status).toHaveBeenCalledWith(500);
|
|
expect(res.json).toHaveBeenCalledWith({
|
|
error: {code: "DOWNLOAD_FAILED", message: "Server Error"},
|
|
});
|
|
expect(logger.error).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("cancelDownloadController", () => {
|
|
test("returns success when download cancelled", () => {
|
|
cancelDownload.mockReturnValue(true);
|
|
cancelDownloadController(req, res);
|
|
expect(res.json).toHaveBeenCalledWith({
|
|
success: true,
|
|
message: "Download stopped! Queue cleared.",
|
|
});
|
|
expect(logger.info).toHaveBeenCalled();
|
|
});
|
|
|
|
test("returns 400 when no download in progress", () => {
|
|
cancelDownload.mockReturnValue(false);
|
|
cancelDownloadController(req, res);
|
|
expect(res.status).toHaveBeenCalledWith(400);
|
|
expect(res.json).toHaveBeenCalledWith({
|
|
error: {code: "NO_DOWNLOAD_IN_PROGRESS", message: "No download in progress !"},
|
|
});
|
|
expect(logger.warn).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("SSE controllers", () => {
|
|
test("progressController sets headers and writes data", () => {
|
|
req.on = jest.fn((event, cb) => {
|
|
if (event === "close") {
|
|
// don't call immediately
|
|
}
|
|
});
|
|
progressController(req, res);
|
|
expect(res.set).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
"Content-Type": "text/event-stream",
|
|
"Cache-Control": "no-cache",
|
|
Connection: "keep-alive",
|
|
})
|
|
);
|
|
expect(res.flushHeaders).toHaveBeenCalled();
|
|
});
|
|
|
|
test("speedController sets headers", () => {
|
|
req.on = jest.fn();
|
|
speedController(req, res);
|
|
expect(res.set).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
"Content-Type": "text/event-stream",
|
|
})
|
|
);
|
|
});
|
|
|
|
test("stageController sets headers", () => {
|
|
req.on = jest.fn();
|
|
stageController(req, res);
|
|
expect(res.set).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
"Content-Type": "text/event-stream",
|
|
})
|
|
);
|
|
});
|
|
|
|
test("playlistInfoController sets headers", () => {
|
|
req.on = jest.fn();
|
|
playlistInfoController(req, res);
|
|
expect(res.set).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
"Content-Type": "text/event-stream",
|
|
})
|
|
);
|
|
});
|
|
});
|
|
});
|