mirror of
https://github.com/gethomepage/homepage.git
synced 2026-02-08 00:40:52 +08:00
test: add widget proxy tests (batch 3)
This commit is contained in:
82
src/widgets/dockhand/proxy.test.js
Normal file
82
src/widgets/dockhand/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, 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: {
|
||||||
|
dockhand: {
|
||||||
|
api: "{url}/{endpoint}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import dockhandProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/dockhand/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("retries after a 401 by logging in once", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({
|
||||||
|
type: "dockhand",
|
||||||
|
url: "http://dockhand/",
|
||||||
|
username: "u",
|
||||||
|
password: "p",
|
||||||
|
});
|
||||||
|
|
||||||
|
httpProxy
|
||||||
|
.mockResolvedValueOnce([401, "application/json", Buffer.from("nope")])
|
||||||
|
.mockResolvedValueOnce([200, "application/json", Buffer.from("ok")]) // login
|
||||||
|
.mockResolvedValueOnce([200, "application/json", Buffer.from("data")]); // retry
|
||||||
|
|
||||||
|
const req = { method: "GET", query: { group: "g", service: "svc", endpoint: "api/v1/status", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await dockhandProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(httpProxy).toHaveBeenCalledTimes(3);
|
||||||
|
expect(httpProxy.mock.calls[1][0]).toBe("http://dockhand/api/auth/login");
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toEqual(Buffer.from("data"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns a sanitized error response for HTTP errors", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({
|
||||||
|
type: "dockhand",
|
||||||
|
url: "http://dockhand",
|
||||||
|
});
|
||||||
|
|
||||||
|
httpProxy.mockResolvedValueOnce([500, "application/json", Buffer.from("boom")]);
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
query: { group: "g", service: "svc", endpoint: "api/v1/status?token=abc", index: "0" },
|
||||||
|
};
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await dockhandProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(500);
|
||||||
|
expect(res.body.error.message).toBe("HTTP Error");
|
||||||
|
expect(res.body.error.url).toContain("token=***");
|
||||||
|
});
|
||||||
|
});
|
||||||
73
src/widgets/homebox/proxy.test.js
Normal file
73
src/widgets/homebox/proxy.test.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
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("utils/logger", () => ({
|
||||||
|
default: () => logger,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/config/service-helpers", () => ({
|
||||||
|
default: getServiceWidget,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/proxy/http", () => ({
|
||||||
|
httpProxy,
|
||||||
|
}));
|
||||||
|
vi.mock("memory-cache", () => ({
|
||||||
|
default: cache,
|
||||||
|
...cache,
|
||||||
|
}));
|
||||||
|
|
||||||
|
import homeboxProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/homebox/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
cache._reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs in and returns group statistics + currency", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({
|
||||||
|
url: "http://homebox",
|
||||||
|
username: "u",
|
||||||
|
password: "p",
|
||||||
|
});
|
||||||
|
|
||||||
|
httpProxy
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
Buffer.from(JSON.stringify({ token: "tok", expiresAt: new Date(Date.now() + 60_000).toISOString() })),
|
||||||
|
])
|
||||||
|
.mockResolvedValueOnce([200, "application/json", Buffer.from(JSON.stringify({ totalItems: 1, totalUsers: 2 }))])
|
||||||
|
.mockResolvedValueOnce([200, "application/json", Buffer.from(JSON.stringify({ currency: "USD" }))]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await homeboxProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(httpProxy).toHaveBeenCalledTimes(3);
|
||||||
|
expect(httpProxy.mock.calls[0][0]).toBe("http://homebox/api/v1/users/login");
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body.currencyCode).toBe("USD");
|
||||||
|
expect(res.body.users).toBe(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
96
src/widgets/homebridge/proxy.test.js
Normal file
96
src/widgets/homebridge/proxy.test.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
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("utils/logger", () => ({
|
||||||
|
default: () => logger,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/config/service-helpers", () => ({
|
||||||
|
default: getServiceWidget,
|
||||||
|
}));
|
||||||
|
vi.mock("utils/proxy/http", () => ({
|
||||||
|
httpProxy,
|
||||||
|
}));
|
||||||
|
vi.mock("memory-cache", () => ({
|
||||||
|
default: cache,
|
||||||
|
...cache,
|
||||||
|
}));
|
||||||
|
vi.mock("widgets/widgets", () => ({
|
||||||
|
default: {
|
||||||
|
homebridge: {
|
||||||
|
api: "{url}/{endpoint}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import homebridgeProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/homebridge/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
cache._reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs in and aggregates status, versions, plugin updates, and child bridge counts", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ type: "homebridge", url: "http://hb", username: "u", password: "p" });
|
||||||
|
|
||||||
|
httpProxy
|
||||||
|
// login
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
Buffer.from(JSON.stringify({ access_token: "tok", expires_in: 3600 })),
|
||||||
|
{},
|
||||||
|
])
|
||||||
|
// status
|
||||||
|
.mockResolvedValueOnce([200, "application/json", Buffer.from(JSON.stringify({ status: "ok" })), {}])
|
||||||
|
// version
|
||||||
|
.mockResolvedValueOnce([200, "application/json", Buffer.from(JSON.stringify({ updateAvailable: true })), {}])
|
||||||
|
// child bridges
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
Buffer.from(JSON.stringify([{ status: "ok" }, { status: "down" }])),
|
||||||
|
{},
|
||||||
|
])
|
||||||
|
// plugins
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
Buffer.from(JSON.stringify([{ updateAvailable: true }, { updateAvailable: false }])),
|
||||||
|
{},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await homebridgeProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toEqual({
|
||||||
|
status: "ok",
|
||||||
|
updateAvailable: true,
|
||||||
|
plugins: { updatesAvailable: 1 },
|
||||||
|
childBridges: { running: 1, total: 2 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
79
src/widgets/jackett/proxy.test.js
Normal file
79
src/widgets/jackett/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, logger } = vi.hoisted(() => ({
|
||||||
|
httpProxy: vi.fn(),
|
||||||
|
getServiceWidget: vi.fn(),
|
||||||
|
logger: {
|
||||||
|
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: {
|
||||||
|
jackett: {
|
||||||
|
api: "{url}/{endpoint}",
|
||||||
|
loginURL: "{url}/UI/Dashboard",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import jackettProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/jackett/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fetches an auth cookie when password is set and passes it on requests", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({
|
||||||
|
type: "jackett",
|
||||||
|
url: "http://jackett",
|
||||||
|
password: "pw",
|
||||||
|
});
|
||||||
|
|
||||||
|
httpProxy
|
||||||
|
// login cookie fetch
|
||||||
|
.mockResolvedValueOnce([200, "text/plain", null, null, { headers: { Cookie: "c=1" } }])
|
||||||
|
// api call
|
||||||
|
.mockResolvedValueOnce([200, "application/json", Buffer.from("ok")]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", endpoint: "api/v2.0/indexers/all/results", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await jackettProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(httpProxy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(httpProxy.mock.calls[1][1].headers.Cookie).toBe("c=1");
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toEqual(Buffer.from("ok"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 500 when cookie authentication fails", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({
|
||||||
|
type: "jackett",
|
||||||
|
url: "http://jackett",
|
||||||
|
password: "pw",
|
||||||
|
});
|
||||||
|
|
||||||
|
httpProxy.mockResolvedValueOnce([200, "text/plain", null, null, { headers: {} }]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", endpoint: "api", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await jackettProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(500);
|
||||||
|
expect(res.body).toEqual({ error: "Failed to authenticate with Jackett" });
|
||||||
|
});
|
||||||
|
});
|
||||||
121
src/widgets/omada/proxy.test.js
Normal file
121
src/widgets/omada/proxy.test.js
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
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,
|
||||||
|
}));
|
||||||
|
|
||||||
|
import omadaProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/omada/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fetches controller info, logs in, selects site, and returns overview stats (v4)", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({
|
||||||
|
url: "http://omada",
|
||||||
|
username: "u",
|
||||||
|
password: "p",
|
||||||
|
site: "Default",
|
||||||
|
});
|
||||||
|
|
||||||
|
httpProxy
|
||||||
|
// controller info
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
JSON.stringify({ result: { omadacId: "cid", controllerVer: "4.5.6" } }),
|
||||||
|
])
|
||||||
|
// login
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
Buffer.from(JSON.stringify({ errorCode: 0, result: { token: "t" } })),
|
||||||
|
])
|
||||||
|
// sites list
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
JSON.stringify({ errorCode: 0, result: { data: [{ name: "Default", key: "sitekey" }] } }),
|
||||||
|
])
|
||||||
|
// overview diagram
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
JSON.stringify({
|
||||||
|
errorCode: 0,
|
||||||
|
result: {
|
||||||
|
totalClientNum: 10,
|
||||||
|
connectedApNum: 2,
|
||||||
|
connectedGatewayNum: 1,
|
||||||
|
connectedSwitchNum: 3,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
// alert count
|
||||||
|
.mockResolvedValueOnce([200, "application/json", JSON.stringify({ errorCode: 0, result: { alertNum: 4 } })]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await omadaProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(httpProxy).toHaveBeenCalledTimes(5);
|
||||||
|
expect(res.statusCode).toBe(null); // uses res.send directly without setting status
|
||||||
|
expect(res.body).toBe(
|
||||||
|
JSON.stringify({
|
||||||
|
connectedAp: 2,
|
||||||
|
activeUser: 10,
|
||||||
|
alerts: 4,
|
||||||
|
connectedGateways: 1,
|
||||||
|
connectedSwitches: 3,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an error when the site is not found", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ url: "http://omada", username: "u", password: "p", site: "Missing" });
|
||||||
|
|
||||||
|
httpProxy
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
JSON.stringify({ result: { omadacId: "cid", controllerVer: "4.5.6" } }),
|
||||||
|
])
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
Buffer.from(JSON.stringify({ errorCode: 0, result: { token: "t" } })),
|
||||||
|
])
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
JSON.stringify({ errorCode: 0, result: { data: [{ name: "Default", key: "sitekey" }] } }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await omadaProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body.error.message).toContain("Site Missing is not found");
|
||||||
|
});
|
||||||
|
});
|
||||||
74
src/widgets/openmediavault/proxy.test.js
Normal file
74
src/widgets/openmediavault/proxy.test.js
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
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(),
|
||||||
|
setCookieHeader: 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: {
|
||||||
|
openmediavault: {
|
||||||
|
api: "{url}/rpc.php",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import openmediavaultProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/openmediavault/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs in after a 401 and retries the RPC call", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({
|
||||||
|
type: "openmediavault",
|
||||||
|
url: "http://omv",
|
||||||
|
username: "u",
|
||||||
|
password: "p",
|
||||||
|
method: "foo.bar",
|
||||||
|
});
|
||||||
|
|
||||||
|
httpProxy
|
||||||
|
// initial rpc unauthorized
|
||||||
|
.mockResolvedValueOnce([401, "application/json", Buffer.from(JSON.stringify({ response: {} })), {}])
|
||||||
|
// login rpc
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
Buffer.from(JSON.stringify({ response: { authenticated: true } })),
|
||||||
|
{ "set-cookie": ["sid=1"] },
|
||||||
|
])
|
||||||
|
// retry rpc
|
||||||
|
.mockResolvedValueOnce([200, "application/json", Buffer.from(JSON.stringify({ response: { ok: true } })), {}]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await openmediavaultProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(cookieJar.addCookieToJar).toHaveBeenCalled();
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toEqual(Buffer.from(JSON.stringify({ response: { ok: true } })));
|
||||||
|
});
|
||||||
|
});
|
||||||
59
src/widgets/openwrt/proxy.test.js
Normal file
59
src/widgets/openwrt/proxy.test.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import createMockRes from "test-utils/create-mock-res";
|
||||||
|
|
||||||
|
const { sendJsonRpcRequest, getServiceWidget, logger } = vi.hoisted(() => ({
|
||||||
|
sendJsonRpcRequest: 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/handlers/jsonrpc", () => ({
|
||||||
|
sendJsonRpcRequest,
|
||||||
|
}));
|
||||||
|
vi.mock("widgets/widgets", () => ({
|
||||||
|
default: {
|
||||||
|
openwrt: {
|
||||||
|
api: "{url}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import openwrtProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/openwrt/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs in and retries after an unauthorized response", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ type: "openwrt", url: "http://openwrt", username: "u", password: "p" });
|
||||||
|
|
||||||
|
sendJsonRpcRequest
|
||||||
|
// initial call -> unauthorized
|
||||||
|
.mockResolvedValueOnce([200, "application/json", Buffer.from(JSON.stringify({ error: { code: -32002 } }))])
|
||||||
|
// login -> sets ubus token
|
||||||
|
.mockResolvedValueOnce([200, "application/json", Buffer.from(JSON.stringify([0, { ubus_rpc_session: "sess" }]))])
|
||||||
|
// retry system info -> ok
|
||||||
|
.mockResolvedValueOnce([
|
||||||
|
200,
|
||||||
|
"application/json",
|
||||||
|
Buffer.from(JSON.stringify([0, { uptime: 1, load: [0, 131072, 0] }])),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await openwrtProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(sendJsonRpcRequest).toHaveBeenCalledTimes(3);
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(JSON.parse(res.body).cpuLoad).toBe("2.00");
|
||||||
|
});
|
||||||
|
});
|
||||||
41
src/widgets/photoprism/proxy.test.js
Normal file
41
src/widgets/photoprism/proxy.test.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
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,
|
||||||
|
}));
|
||||||
|
|
||||||
|
import photoprismProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/photoprism/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("supports bearer-token auth and returns config count", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ url: "http://pp", key: "k" });
|
||||||
|
httpProxy.mockResolvedValueOnce([200, "application/json", Buffer.from(JSON.stringify({ config: { count: 123 } }))]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await photoprismProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(httpProxy.mock.calls[0][1].headers.Authorization).toBe("Bearer k");
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toBe(123);
|
||||||
|
});
|
||||||
|
});
|
||||||
93
src/widgets/plex/proxy.test.js
Normal file
93
src/widgets/plex/proxy.test.js
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
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.has(k) ? store.get(k) : null)),
|
||||||
|
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 === "sessions") return JSON.stringify({ MediaContainer: { _attributes: { size: "2" } } });
|
||||||
|
if (xml === "libraries")
|
||||||
|
return JSON.stringify({
|
||||||
|
MediaContainer: {
|
||||||
|
Directory: [
|
||||||
|
{ _attributes: { type: "movie", key: "1" } },
|
||||||
|
{ _attributes: { type: "show", key: "2" } },
|
||||||
|
{ _attributes: { type: "artist", key: "3" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (xml === "movies") return JSON.stringify({ MediaContainer: { _attributes: { size: "10" } } });
|
||||||
|
if (xml === "tv") return JSON.stringify({ MediaContainer: { _attributes: { totalSize: "20" } } });
|
||||||
|
if (xml === "albums") return JSON.stringify({ MediaContainer: { _attributes: { size: "30" } } });
|
||||||
|
return JSON.stringify({ MediaContainer: { _attributes: { size: "0" } } });
|
||||||
|
}),
|
||||||
|
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("memory-cache", () => ({
|
||||||
|
default: cache,
|
||||||
|
...cache,
|
||||||
|
}));
|
||||||
|
vi.mock("xml-js", () => ({
|
||||||
|
xml2json,
|
||||||
|
}));
|
||||||
|
vi.mock("widgets/widgets", () => ({
|
||||||
|
default: {
|
||||||
|
plex: {
|
||||||
|
api: "{url}{endpoint}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import plexProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/plex/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
cache._reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fetches sessions and library counts, caching intermediate results", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ type: "plex", url: "http://plex" });
|
||||||
|
|
||||||
|
httpProxy
|
||||||
|
// sessions
|
||||||
|
.mockResolvedValueOnce([200, "application/xml", Buffer.from("sessions")])
|
||||||
|
// libraries
|
||||||
|
.mockResolvedValueOnce([200, "application/xml", Buffer.from("libraries")])
|
||||||
|
// movies
|
||||||
|
.mockResolvedValueOnce([200, "application/xml", Buffer.from("movies")])
|
||||||
|
// tv
|
||||||
|
.mockResolvedValueOnce([200, "application/xml", Buffer.from("tv")])
|
||||||
|
// albums
|
||||||
|
.mockResolvedValueOnce([200, "application/xml", Buffer.from("albums")]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await plexProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toEqual({ streams: "2", albums: 30, movies: 10, tv: 20 });
|
||||||
|
expect(cache.put).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
48
src/widgets/rutorrent/proxy.test.js
Normal file
48
src/widgets/rutorrent/proxy.test.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
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: { 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: {
|
||||||
|
rutorrent: {
|
||||||
|
api: "{url}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import rutorrentProxyHandler from "./proxy";
|
||||||
|
|
||||||
|
describe("widgets/rutorrent/proxy", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("parses torrent list data into an array", async () => {
|
||||||
|
getServiceWidget.mockResolvedValue({ type: "rutorrent", url: "http://ru", username: "u", password: "p" });
|
||||||
|
httpProxy.mockResolvedValueOnce([200, "application/json", JSON.stringify({ t: { hash1: Array(34).fill(0) } })]);
|
||||||
|
|
||||||
|
const req = { query: { group: "g", service: "svc", index: "0" } };
|
||||||
|
const res = createMockRes();
|
||||||
|
|
||||||
|
await rutorrentProxyHandler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(Array.isArray(res.body)).toBe(true);
|
||||||
|
expect(res.body[0]["d.get_name"]).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user