mirror of
https://github.com/unraid/api.git
synced 2026-01-01 06:01:18 -06:00
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Standalone web bundle with auto-mount utilities and a self-contained test page. * New responsive modal components for consistent mobile/desktop dialogs. * Header actions to copy OS/API versions. * **Improvements** * Refreshed UI styles (muted borders), accessibility and animation refinements. * Theming updates and Tailwind v4–aligned, component-scoped styles. * Runtime GraphQL endpoint override and CSRF header support. * **Bug Fixes** * Safer network fetching and improved manifest/asset loading with duplicate protection. * **Tests/Chores** * Parallel plugin tests, new extractor test suite, and updated build/test scripts. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
190 lines
5.1 KiB
TypeScript
190 lines
5.1 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
import {
|
|
validatePluginEnv,
|
|
setupPluginEnv,
|
|
} from "../../cli/setup-plugin-environment";
|
|
import { access, readFile } from "node:fs/promises";
|
|
import { existsSync } from "node:fs";
|
|
|
|
// Mock fs/promises
|
|
vi.mock("node:fs/promises", () => ({
|
|
access: vi.fn(),
|
|
readFile: vi.fn(),
|
|
constants: {
|
|
F_OK: 0,
|
|
},
|
|
}));
|
|
|
|
// Mock node:fs
|
|
vi.mock("node:fs", () => ({
|
|
existsSync: vi.fn(),
|
|
}));
|
|
|
|
beforeEach(() => {
|
|
vi.resetAllMocks();
|
|
|
|
// Mock existsSync to return true for test.txz
|
|
vi.mocked(existsSync).mockImplementation((path) => {
|
|
return path.toString().includes("test.txz");
|
|
});
|
|
|
|
vi.mocked(readFile).mockImplementation((path, encoding) => {
|
|
console.log("Mock readFile called with:", path, encoding);
|
|
|
|
// If called with encoding parameter (for release notes)
|
|
if (encoding === "utf8") {
|
|
if (path.toString().includes("valid-release-notes.txt")) {
|
|
return Promise.resolve("Release notes content");
|
|
}
|
|
}
|
|
// If called without encoding (for txz file)
|
|
if (path.toString().includes("test.txz")) {
|
|
return Promise.resolve(Buffer.from("test content"));
|
|
}
|
|
|
|
return Promise.reject(new Error(`File not found: ${path}`));
|
|
});
|
|
vi.mocked(access).mockResolvedValue(undefined);
|
|
});
|
|
|
|
describe("validatePluginEnv", () => {
|
|
afterEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("validates required fields", async () => {
|
|
const validEnv = {
|
|
apiVersion: "4.17.0",
|
|
baseUrl: "https://example.com",
|
|
txzPath: "./test.txz",
|
|
pluginVersion: "2024.05.05.1232",
|
|
};
|
|
|
|
const result = await validatePluginEnv(validEnv);
|
|
expect(result).toMatchObject(validEnv);
|
|
});
|
|
|
|
it("throws on invalid URL", async () => {
|
|
const invalidEnv = {
|
|
apiVersion: "4.17.0",
|
|
baseUrl: "not-a-url",
|
|
txzPath: "./test.txz",
|
|
pluginVersion: "2024.05.05.1232",
|
|
};
|
|
|
|
await expect(validatePluginEnv(invalidEnv)).rejects.toThrow();
|
|
});
|
|
|
|
it("handles tag option in non-CI mode", async () => {
|
|
const envWithTag = {
|
|
apiVersion: "4.17.0",
|
|
baseUrl: "https://example.com",
|
|
txzPath: "./test.txz",
|
|
pluginVersion: "2024.05.05.1232",
|
|
tag: "v1.0.0",
|
|
};
|
|
|
|
const result = await validatePluginEnv(envWithTag);
|
|
|
|
expect(result.releaseNotes).toBe("FAST_TEST_CHANGELOG");
|
|
expect(result.tag).toBe("v1.0.0");
|
|
});
|
|
|
|
it("reads release notes when release-notes-path is provided", async () => {
|
|
const envWithNotes = {
|
|
apiVersion: "4.17.0",
|
|
baseUrl: "https://example.com",
|
|
txzPath: "./test.txz",
|
|
pluginVersion: "2024.05.05.1232",
|
|
releaseNotesPath: "valid-release-notes.txt",
|
|
};
|
|
|
|
const result = await validatePluginEnv(envWithNotes);
|
|
|
|
expect(access).toHaveBeenCalledWith("valid-release-notes.txt", 0);
|
|
expect(readFile).toHaveBeenCalledWith("valid-release-notes.txt", "utf8");
|
|
expect(result.releaseNotes).toBe("Release notes content");
|
|
});
|
|
|
|
it("throws when release notes file is empty", async () => {
|
|
// Instead of overwriting the entire mock, just mock this specific case
|
|
vi.mocked(readFile).mockImplementationOnce((path, encoding) => {
|
|
if (path === "/path/to/notes.md" && encoding === "utf8") {
|
|
return Promise.resolve("");
|
|
}
|
|
return Promise.reject(new Error("Unexpected mock call"));
|
|
});
|
|
|
|
const envWithEmptyNotes = {
|
|
apiVersion: "4.17.0",
|
|
baseUrl: "https://example.com",
|
|
txzPath: "./test.txz",
|
|
pluginVersion: "2024.05.05.1232",
|
|
releaseNotesPath: "/path/to/notes.md",
|
|
};
|
|
|
|
await expect(validatePluginEnv(envWithEmptyNotes)).rejects.toThrow(
|
|
"Release notes file is empty: /path/to/notes.md"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("setupPluginEnv", () => {
|
|
it("sets up environment from CLI arguments", async () => {
|
|
const argv = [
|
|
"node",
|
|
"script.js",
|
|
"--plugin-version",
|
|
"2024.05.05.1232",
|
|
"--txz-path",
|
|
"./test.txz",
|
|
"--base-url",
|
|
"https://example.com",
|
|
];
|
|
|
|
const result = await setupPluginEnv(argv);
|
|
expect(result).toMatchObject({
|
|
pluginVersion: "2024.05.05.1232",
|
|
txzPath: "./test.txz",
|
|
baseUrl: "https://example.com",
|
|
});
|
|
});
|
|
|
|
it("throws when required options are missing", async () => {
|
|
const argv = ["node", "script.js"]; // Missing required options
|
|
await expect(setupPluginEnv(argv)).rejects.toThrow();
|
|
});
|
|
|
|
it("handles optional CLI arguments", async () => {
|
|
const argv = [
|
|
"node",
|
|
"script.js",
|
|
"--txz-path",
|
|
"./test.txz",
|
|
"--base-url",
|
|
"https://example.com",
|
|
"--tag",
|
|
"PR1203",
|
|
"--ci",
|
|
"--plugin-version",
|
|
"2024.05.05.1232",
|
|
];
|
|
|
|
try {
|
|
const result = await setupPluginEnv(argv);
|
|
expect(result).toMatchObject({
|
|
pluginVersion: "2024.05.05.1232",
|
|
txzPath: "./test.txz",
|
|
txzSha256:
|
|
"6ae8a75555209fd6c44157c0aed8016e763ff435a19cf186f76863140143ff72",
|
|
baseUrl: "https://example.com",
|
|
tag: "PR1203",
|
|
ci: true,
|
|
});
|
|
} catch (error) {
|
|
console.error("Error:", error);
|
|
throw error;
|
|
}
|
|
});
|
|
});
|