Files
homepage/src/widgets/qbittorrent/proxy.session.test.js
T

95 lines
2.6 KiB
JavaScript

import { createServer } from "node:http";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
import createMockRes from "test-utils/create-mock-res";
const { getServiceWidget, logger } = vi.hoisted(() => ({
getServiceWidget: vi.fn(),
logger: {
debug: vi.fn(),
error: vi.fn(),
},
}));
vi.mock("utils/logger", () => ({
default: () => logger,
}));
vi.mock("utils/config/service-helpers", () => ({
default: getServiceWidget,
}));
import qbittorrentProxyHandler from "./proxy";
// Integration test using the real httpProxy and cookie jar against a mock
// qBittorrent server, so session cookie handling across login/retry is exercised.
describe("widgets/qbittorrent/proxy session cookies", () => {
let server;
let validSid = null;
let sidCounter = 0;
const getSid = (req) => /SID=([^;]+)/.exec(req.headers.cookie ?? "")?.[1];
beforeAll(async () => {
server = createServer((req, res) => {
if (req.method === "POST" && req.url === "/api/v2/auth/login") {
sidCounter += 1;
validSid = `sid-${sidCounter}`;
res.writeHead(200, { "Set-Cookie": `SID=${validSid}; HttpOnly; path=/`, "Content-Type": "text/plain" });
res.end("Ok.");
return;
}
if (req.method === "GET" && req.url === "/api/v2/torrents/info") {
if (validSid && getSid(req) === validSid) {
res.writeHead(200, { "Content-Type": "application/json" });
res.end("[]");
return;
}
res.writeHead(403, { "Content-Type": "text/plain" });
res.end("Forbidden");
return;
}
res.writeHead(404);
res.end();
});
await new Promise((resolve) => {
server.listen(0, "127.0.0.1", resolve);
});
getServiceWidget.mockResolvedValue({
type: "qbittorrent",
url: `http://127.0.0.1:${server.address().port}`,
username: "user",
password: "pass",
});
});
afterAll(() => {
server.close();
});
const callHandler = async () => {
const req = { query: { group: "g", service: "svc", endpoint: "torrents/info", index: "0" } };
const res = createMockRes();
await qbittorrentProxyHandler(req, res);
return res;
};
it("logs in and returns data when no session exists yet", async () => {
const res = await callHandler();
expect(res.statusCode).toBe(200);
});
it("recovers when the cached session cookie has expired server-side", async () => {
// session died server-side (timeout/restart) but the jar still holds the old cookie
validSid = null;
const res = await callHandler();
expect(res.statusCode).toBe(200);
});
});