mirror of
https://github.com/gethomepage/homepage.git
synced 2026-02-08 00:40:52 +08:00
test: add widget proxy tests (batch 4)
This commit is contained in:
92
src/widgets/crowdsec/proxy.test.js
Normal file
92
src/widgets/crowdsec/proxy.test.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import createMockRes from "test-utils/create-mock-res";
|
||||||
|
|
||||||
|
const { httpProxy, getServiceWidget, cache, logger } = vi.hoisted(() => {
|
||||||
|
const store = new Map();
|
||||||
|
return {
|
||||||
|
httpProxy: vi.fn(),
|
||||||
|
getServiceWidget: vi.fn(),
|
||||||
|
cache: {
|
||||||
|
get: vi.fn((k) => store.get(k)),
|
||||||
|
put: vi.fn((k, v) => store.set(k, v)),
|
||||||
|
del: vi.fn((k) => store.delete(k)),
|
||||||
|
_reset: () => store.clear(),
|
||||||
|
},
|
||||||
|
logger: {
|
||||||
|
debug: vi.fn(),
|
||||||
|
error: vi.fn(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.mock("memory-cache", () => ({
|
||||||
|
default: cache,
|
||||||
|
...cache,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/logger", () => ({
|
||||||
|
default: () => logger,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/config/service-helpers", () => ({
|
||||||
|
default: getServiceWidget,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/proxy/http", () => ({
|
||||||
|
httpProxy,
|
||||||
|
}));
|
||||||
|
vi.mock("widgets/widgets", () => ({
|
||||||
|
default: {
|
||||||
|
crowdsec: {
|
||||||
|
api: "{url}/{endpoint}",
|
||||||
|
loginURL: "{url}/login",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import crowdsecProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/crowdsec/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
cache._reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs in, caches a token, and uses it for requests", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({
|
||||||
|
type: "crowdsec",
|
||||||
|
url: "http://cs",
|
||||||
|
username: "machine",
|
||||||
|
password: "pw",
|
||||||
|
});
|
||||||
|
|
||||||
|
httpProxy
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
JSON.stringify({ token: "tok", expire: new Date(Date.now() + 60_000).toISOString() }),
|
||||||
|
])
|
||||||
|
.mockResolvedValueOnce([200, "application/json", Buffer.from("data")]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", endpoint: "alerts", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await crowdsecProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(httpProxy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(httpProxy.mock.calls[1][1].headers.Authorization).toBe("Bearer tok");
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toEqual(Buffer.from("data"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 500 if token cannot be obtained", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ type: "crowdsec", url: "http://cs", username: "machine", password: "pw" });
|
||||||
|
httpProxy.mockResolvedValueOnce([200, "application/json", JSON.stringify({ expire: "2099-01-01T00:00:00Z" })]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", endpoint: "alerts", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await crowdsecProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(500);
|
||||||
|
expect(res.body).toEqual({ error: "Failed to authenticate with Crowdsec" });
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -69,7 +69,7 @@ export default async function frigateProxyHandler(req, res, map) {
|
|||||||
data = asJson(data);
|
data = asJson(data);
|
||||||
|
|
||||||
if (endpoint == "stats") {
|
if (endpoint == "stats") {
|
||||||
res.status(status).send({
|
return res.status(status).send({
|
||||||
num_cameras: data?.cameras !== undefined ? Object.keys(data?.cameras).length : 0,
|
num_cameras: data?.cameras !== undefined ? Object.keys(data?.cameras).length : 0,
|
||||||
uptime: data?.service?.uptime,
|
uptime: data?.service?.uptime,
|
||||||
version: data?.service.version,
|
version: data?.service.version,
|
||||||
|
|||||||
71
src/widgets/frigate/proxy.test.js
Normal file
71
src/widgets/frigate/proxy.test.js
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import createMockRes from "test-utils/create-mock-res";
|
||||||
|
|
||||||
|
const { httpProxy, getServiceWidget, cookieJar, logger } = vi.hoisted(() => ({
|
||||||
|
httpProxy: vi.fn(),
|
||||||
|
getServiceWidget: vi.fn(),
|
||||||
|
cookieJar: {
|
||||||
|
addCookieToJar: vi.fn(),
|
||||||
|
},
|
||||||
|
logger: {
|
||||||
|
debug: vi.fn(),
|
||||||
|
error: vi.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("utils/logger", () => ({
|
||||||
|
default: () => logger,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/config/service-helpers", () => ({
|
||||||
|
default: getServiceWidget,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/proxy/http", () => ({
|
||||||
|
httpProxy,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/proxy/cookie-jar", () => cookieJar);
|
||||||
|
vi.mock("widgets/widgets", () => ({
|
||||||
|
default: {
|
||||||
|
frigate: {
|
||||||
|
api: "{url}/api/{endpoint}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import frigateProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/frigate/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs in after a 401 and returns derived stats", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({
|
||||||
|
type: "frigate",
|
||||||
|
url: "http://frigate",
|
||||||
|
username: "u",
|
||||||
|
password: "p",
|
||||||
|
});
|
||||||
|
|
||||||
|
httpProxy
|
||||||
|
// initial request
|
||||||
|
.mockResolvedValueOnce([401, "application/json", Buffer.from("nope")])
|
||||||
|
// login
|
||||||
|
.mockResolvedValueOnce([200, "application/json", Buffer.from("{}"), { "set-cookie": ["sid=1"] }])
|
||||||
|
// retry stats
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
Buffer.from(JSON.stringify({ cameras: { a: {}, b: {} }, service: { uptime: 123, version: "1.0" } })),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", endpoint: "stats", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await frigateProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(cookieJar.addCookieToJar).toHaveBeenCalled();
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toEqual({ num_cameras: 2, uptime: 123, version: "1.0" });
|
||||||
|
});
|
||||||
|
});
|
||||||
76
src/widgets/fritzbox/proxy.test.js
Normal file
76
src/widgets/fritzbox/proxy.test.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import createMockRes from "test-utils/create-mock-res";
|
||||||
|
|
||||||
|
const { httpProxy, getServiceWidget, xml2json, logger } = vi.hoisted(() => ({
|
||||||
|
httpProxy: vi.fn(),
|
||||||
|
getServiceWidget: vi.fn(),
|
||||||
|
xml2json: vi.fn((xml) => {
|
||||||
|
const xmlString = Buffer.isBuffer(xml) ? xml.toString() : xml;
|
||||||
|
if (xmlString === "GetStatusInfo") {
|
||||||
|
return JSON.stringify({
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
elements: [
|
||||||
|
{ name: "NewConnectionStatus", elements: [{ text: "Connected" }] },
|
||||||
|
{ name: "NewUptime", elements: [{ text: "42" }] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return JSON.stringify({ elements: [] });
|
||||||
|
}),
|
||||||
|
logger: { debug: vi.fn() },
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("xml-js", () => ({
|
||||||
|
xml2json,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/logger", () => ({
|
||||||
|
default: () => logger,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/config/service-helpers", () => ({
|
||||||
|
default: getServiceWidget,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/proxy/http", () => ({
|
||||||
|
httpProxy,
|
||||||
|
}));
|
||||||
|
|
||||||
|
import fritzboxProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/fritzbox/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("queries the configured fields and returns derived data", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({
|
||||||
|
url: "http://fritz.box",
|
||||||
|
fields: ["connectionStatus", "uptime"],
|
||||||
|
});
|
||||||
|
|
||||||
|
httpProxy.mockResolvedValueOnce([200, "text/xml", Buffer.from("GetStatusInfo")]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await fritzboxProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
connectionStatus: "Connected",
|
||||||
|
uptime: "42",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
67
src/widgets/gamedig/proxy.test.js
Normal file
67
src/widgets/gamedig/proxy.test.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import createMockRes from "test-utils/create-mock-res";
|
||||||
|
|
||||||
|
const { GameDig, getServiceWidget, logger } = vi.hoisted(() => ({
|
||||||
|
GameDig: { query: vi.fn() },
|
||||||
|
getServiceWidget: vi.fn(),
|
||||||
|
logger: { error: vi.fn() },
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("gamedig", () => ({
|
||||||
|
GameDig,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/config/service-helpers", () => ({
|
||||||
|
default: getServiceWidget,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/logger", () => ({
|
||||||
|
default: () => logger,
|
||||||
|
}));
|
||||||
|
|
||||||
|
import gamedigProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/gamedig/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns online=true with server details when query succeeds", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ url: "http://example.com:1234", serverType: "csgo" });
|
||||||
|
GameDig.query.mockResolvedValue({
|
||||||
|
name: "Server",
|
||||||
|
map: "de_dust2",
|
||||||
|
numplayers: 3,
|
||||||
|
maxplayers: 10,
|
||||||
|
bots: [],
|
||||||
|
ping: 42,
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await gamedigProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
online: true,
|
||||||
|
name: "Server",
|
||||||
|
players: 3,
|
||||||
|
maxplayers: 10,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns online=false when query fails", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ url: "http://example.com:1234", serverType: "csgo" });
|
||||||
|
GameDig.query.mockRejectedValue(new Error("nope"));
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await gamedigProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toEqual({ online: false });
|
||||||
|
});
|
||||||
|
});
|
||||||
60
src/widgets/homeassistant/proxy.test.js
Normal file
60
src/widgets/homeassistant/proxy.test.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import createMockRes from "test-utils/create-mock-res";
|
||||||
|
|
||||||
|
const { httpProxy, getServiceWidget, logger } = vi.hoisted(() => ({
|
||||||
|
httpProxy: vi.fn(),
|
||||||
|
getServiceWidget: vi.fn(),
|
||||||
|
logger: { debug: vi.fn() },
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("utils/logger", () => ({
|
||||||
|
default: () => logger,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/config/service-helpers", () => ({
|
||||||
|
default: getServiceWidget,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/proxy/http", () => ({
|
||||||
|
httpProxy,
|
||||||
|
}));
|
||||||
|
|
||||||
|
import homeassistantProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/homeassistant/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 400 when custom JSON cannot be parsed", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ url: "http://hass", key: "k", custom: "{not-json" });
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await homeassistantProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(400);
|
||||||
|
expect(res.body).toEqual({ error: "Error parsing widget custom label" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("runs default template queries and returns label/value pairs", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ url: "http://hass", key: "k" });
|
||||||
|
httpProxy
|
||||||
|
.mockResolvedValueOnce([200, "text/plain", Buffer.from("1 / 2")])
|
||||||
|
.mockResolvedValueOnce([200, "text/plain", Buffer.from("3 / 4")])
|
||||||
|
.mockResolvedValueOnce([200, "text/plain", Buffer.from("5 / 6")]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await homeassistantProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(httpProxy).toHaveBeenCalledTimes(3);
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toEqual([
|
||||||
|
{ label: "homeassistant.people_home", value: "1 / 2" },
|
||||||
|
{ label: "homeassistant.lights_on", value: "3 / 4" },
|
||||||
|
{ label: "homeassistant.switches_on", value: "5 / 6" },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
79
src/widgets/jdownloader/proxy.test.js
Normal file
79
src/widgets/jdownloader/proxy.test.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import createMockRes from "test-utils/create-mock-res";
|
||||||
|
|
||||||
|
const { httpProxy, getServiceWidget, tools, logger } = vi.hoisted(() => ({
|
||||||
|
httpProxy: vi.fn(),
|
||||||
|
getServiceWidget: vi.fn(),
|
||||||
|
tools: {
|
||||||
|
uniqueRid: vi.fn(() => 123),
|
||||||
|
sha256: vi.fn(() => "secret"),
|
||||||
|
validateRid: vi.fn(() => true),
|
||||||
|
createEncryptionToken: vi.fn(() => "enc-token"),
|
||||||
|
decrypt: vi.fn((cipherText) => {
|
||||||
|
if (cipherText === "connect") {
|
||||||
|
return JSON.stringify({ rid: 123, sessiontoken: "sess" });
|
||||||
|
}
|
||||||
|
if (cipherText === "devices") {
|
||||||
|
return JSON.stringify({ list: [{ name: "myclient", id: "dev1" }] });
|
||||||
|
}
|
||||||
|
if (cipherText === "packages") {
|
||||||
|
return JSON.stringify({
|
||||||
|
data: [
|
||||||
|
{ bytesLoaded: 40, bytesTotal: 100, finished: false, speed: 10 },
|
||||||
|
{ bytesLoaded: 100, bytesTotal: 100, finished: true, speed: 0 },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return JSON.stringify({});
|
||||||
|
}),
|
||||||
|
encrypt: vi.fn(() => "encrypted-body"),
|
||||||
|
},
|
||||||
|
logger: { debug: vi.fn(), error: vi.fn() },
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("./tools", () => tools);
|
||||||
|
vi.mock("utils/logger", () => ({
|
||||||
|
default: () => logger,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/config/service-helpers", () => ({
|
||||||
|
default: getServiceWidget,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/proxy/http", () => ({
|
||||||
|
httpProxy,
|
||||||
|
}));
|
||||||
|
|
||||||
|
import jdownloaderProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/jdownloader/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("aggregates package stats from the JDownloader API", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({
|
||||||
|
url: "http://ignored",
|
||||||
|
username: "user@example.com",
|
||||||
|
password: "pw",
|
||||||
|
client: "myclient",
|
||||||
|
});
|
||||||
|
|
||||||
|
httpProxy
|
||||||
|
.mockResolvedValueOnce([200, "application/json", Buffer.from("connect")])
|
||||||
|
.mockResolvedValueOnce([200, "application/json", Buffer.from("devices")])
|
||||||
|
.mockResolvedValueOnce([200, "application/json", Buffer.from("packages")]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await jdownloaderProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(httpProxy).toHaveBeenCalledTimes(3);
|
||||||
|
expect(res.body).toEqual({
|
||||||
|
downloadCount: 2,
|
||||||
|
bytesRemaining: 60,
|
||||||
|
totalBytes: 200,
|
||||||
|
totalSpeed: 10,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
72
src/widgets/komodo/proxy.test.js
Normal file
72
src/widgets/komodo/proxy.test.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import createMockRes from "test-utils/create-mock-res";
|
||||||
|
|
||||||
|
const { httpProxy, getServiceWidget, validateWidgetData, logger } = vi.hoisted(() => ({
|
||||||
|
httpProxy: vi.fn(),
|
||||||
|
getServiceWidget: vi.fn(),
|
||||||
|
validateWidgetData: vi.fn(() => true),
|
||||||
|
logger: { debug: vi.fn(), error: vi.fn() },
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("utils/logger", () => ({
|
||||||
|
default: () => logger,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/config/service-helpers", () => ({
|
||||||
|
default: getServiceWidget,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/proxy/http", () => ({
|
||||||
|
httpProxy,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/proxy/validate-widget-data", () => ({
|
||||||
|
default: validateWidgetData,
|
||||||
|
}));
|
||||||
|
vi.mock("widgets/widgets", () => ({
|
||||||
|
default: {
|
||||||
|
komodo: {
|
||||||
|
api: "{url}/{endpoint}",
|
||||||
|
mappings: {
|
||||||
|
stats: { body: { hello: "world" } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import komodoProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/komodo/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
validateWidgetData.mockReturnValue(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("POSTs to the unified read endpoint with API key/secret", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ type: "komodo", url: "http://komodo", key: "k", secret: "s" });
|
||||||
|
httpProxy.mockResolvedValueOnce([200, "application/json", Buffer.from("ok")]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", endpoint: "stats", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await komodoProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(httpProxy.mock.calls[0][0]).toBe("http://komodo/read");
|
||||||
|
expect(httpProxy.mock.calls[0][1].headers["X-API-Key"]).toBe("k");
|
||||||
|
expect(httpProxy.mock.calls[0][1].headers["X-API-Secret"]).toBe("s");
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toEqual(Buffer.from("ok"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 500 when data validation fails", async () => {
|
||||||
|
validateWidgetData.mockReturnValue(false);
|
||||||
|
getServiceWidget.mockResolvedValue({ type: "komodo", url: "http://komodo", key: "k", secret: "s" });
|
||||||
|
httpProxy.mockResolvedValueOnce([200, "application/json", Buffer.from("bad")]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", endpoint: "stats", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await komodoProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(500);
|
||||||
|
expect(res.body.error.message).toBe("Invalid data");
|
||||||
|
});
|
||||||
|
});
|
||||||
82
src/widgets/qnap/proxy.test.js
Normal file
82
src/widgets/qnap/proxy.test.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import createMockRes from "test-utils/create-mock-res";
|
||||||
|
|
||||||
|
const { httpProxy, getServiceWidget, cache, xml2json, logger } = vi.hoisted(() => {
|
||||||
|
const store = new Map();
|
||||||
|
return {
|
||||||
|
httpProxy: vi.fn(),
|
||||||
|
getServiceWidget: vi.fn(),
|
||||||
|
cache: {
|
||||||
|
get: vi.fn((k) => store.get(k)),
|
||||||
|
put: vi.fn((k, v) => store.set(k, v)),
|
||||||
|
del: vi.fn((k) => store.delete(k)),
|
||||||
|
_reset: () => store.clear(),
|
||||||
|
},
|
||||||
|
xml2json: vi.fn((xml) => {
|
||||||
|
if (xml === "login") {
|
||||||
|
return JSON.stringify({ QDocRoot: { authSid: { _cdata: "sid1" } } });
|
||||||
|
}
|
||||||
|
if (xml === "system") {
|
||||||
|
return JSON.stringify({
|
||||||
|
QDocRoot: {
|
||||||
|
authPassed: { _cdata: "1" },
|
||||||
|
func: { ownContent: { root: { cpu: 1 } } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (xml === "volume") {
|
||||||
|
return JSON.stringify({ QDocRoot: { authPassed: { _cdata: "1" }, volume: { ok: true } } });
|
||||||
|
}
|
||||||
|
return JSON.stringify({ QDocRoot: { authPassed: { _cdata: "1" } } });
|
||||||
|
}),
|
||||||
|
logger: { debug: vi.fn(), error: vi.fn() },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.mock("memory-cache", () => ({
|
||||||
|
default: cache,
|
||||||
|
...cache,
|
||||||
|
}));
|
||||||
|
vi.mock("xml-js", () => ({
|
||||||
|
xml2json,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/logger", () => ({
|
||||||
|
default: () => logger,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/config/service-helpers", () => ({
|
||||||
|
default: getServiceWidget,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/proxy/http", () => ({
|
||||||
|
httpProxy,
|
||||||
|
}));
|
||||||
|
|
||||||
|
import qnapProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/qnap/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
cache._reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs in and returns system + volume data", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ url: "http://qnap", username: "u", password: "p" });
|
||||||
|
|
||||||
|
httpProxy
|
||||||
|
// login
|
||||||
|
.mockResolvedValueOnce([200, "application/xml", Buffer.from("login")])
|
||||||
|
// system
|
||||||
|
.mockResolvedValueOnce([200, "application/xml", Buffer.from("system")])
|
||||||
|
// volume
|
||||||
|
.mockResolvedValueOnce([200, "application/xml", Buffer.from("volume")]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await qnapProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body.system).toEqual({ cpu: 1 });
|
||||||
|
expect(res.body.volume).toEqual(expect.objectContaining({ authPassed: { _cdata: "1" } }));
|
||||||
|
});
|
||||||
|
});
|
||||||
68
src/widgets/suwayomi/proxy.test.js
Normal file
68
src/widgets/suwayomi/proxy.test.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import createMockRes from "test-utils/create-mock-res";
|
||||||
|
|
||||||
|
const { httpProxy, getServiceWidget, logger } = vi.hoisted(() => ({
|
||||||
|
httpProxy: vi.fn(),
|
||||||
|
getServiceWidget: vi.fn(),
|
||||||
|
logger: { debug: vi.fn(), error: vi.fn() },
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("utils/logger", () => ({
|
||||||
|
default: () => logger,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/config/service-helpers", () => ({
|
||||||
|
default: getServiceWidget,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/proxy/http", () => ({
|
||||||
|
httpProxy,
|
||||||
|
}));
|
||||||
|
vi.mock("widgets/widgets", () => ({
|
||||||
|
default: {
|
||||||
|
suwayomi: {
|
||||||
|
api: "{url}/graphql",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import suwayomiProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/suwayomi/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns extracted counts from GraphQL response (no category)", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ type: "suwayomi", url: "http://su", fields: ["download", "unread"] });
|
||||||
|
|
||||||
|
httpProxy.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
Buffer.from(JSON.stringify({ data: { download: { totalCount: 2 }, unread: { totalCount: 5 } } })),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", endpoint: "graphql", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await suwayomiProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toEqual([
|
||||||
|
{ count: 2, label: "suwayomi.download" },
|
||||||
|
{ count: 5, label: "suwayomi.unread" },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 401 when credentials are invalid", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ type: "suwayomi", url: "http://su", username: "u", password: "p" });
|
||||||
|
httpProxy.mockResolvedValueOnce([401, "application/json", Buffer.from("{}")]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", endpoint: "graphql", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await suwayomiProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(401);
|
||||||
|
expect(res.body.error.message).toContain("unauthorized");
|
||||||
|
});
|
||||||
|
});
|
||||||
50
src/widgets/tdarr/proxy.test.js
Normal file
50
src/widgets/tdarr/proxy.test.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import createMockRes from "test-utils/create-mock-res";
|
||||||
|
|
||||||
|
const { httpProxy, getServiceWidget, logger } = vi.hoisted(() => ({
|
||||||
|
httpProxy: vi.fn(),
|
||||||
|
getServiceWidget: vi.fn(),
|
||||||
|
logger: { debug: vi.fn(), error: vi.fn() },
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("utils/logger", () => ({
|
||||||
|
default: () => logger,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/config/service-helpers", () => ({
|
||||||
|
default: getServiceWidget,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/proxy/http", () => ({
|
||||||
|
httpProxy,
|
||||||
|
}));
|
||||||
|
vi.mock("widgets/widgets", () => ({
|
||||||
|
default: {
|
||||||
|
tdarr: {
|
||||||
|
api: "{url}/api",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import tdarrProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/tdarr/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("POSTs the stats request and includes the API key header", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ type: "tdarr", url: "http://td", key: "k" });
|
||||||
|
httpProxy.mockResolvedValueOnce([200, "application/json", Buffer.from("ok")]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await tdarrProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(httpProxy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(httpProxy.mock.calls[0][0].toString()).toBe("http://td/api");
|
||||||
|
expect(httpProxy.mock.calls[0][1].headers["x-api-key"]).toBe("k");
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toEqual(Buffer.from("ok"));
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user