add tests

This commit is contained in:
Venkatesan Ravi
2025-11-02 01:46:34 -08:00
parent cc9ce5ee71
commit 0412b26264
5 changed files with 285 additions and 1 deletions

37
tests/api/client.spec.ts Normal file
View File

@@ -0,0 +1,37 @@
import { describe, it, expect, vi, Mock } from "vitest";
import { makeClient } from "../../src/api/client";
import { type paths } from "../../generated/schema";
import createClient from "openapi-fetch";
describe("api/client", () => {
it("whenClientCreated_thenHeadersInjectedOnEachRequest()", async () => {
// Arrange
const spy: Mock = vi
.spyOn(globalThis as unknown as { fetch: typeof fetch }, "fetch")
.mockResolvedValue(
new Response(JSON.stringify({ ok: true }), { status: 200 }),
);
try {
// Act
const jf: ReturnType<typeof createClient<paths>> = makeClient(
"http://example:8096",
"XYZ",
);
await jf.GET("/System/Info/Public");
// Assert
expect(spy).toHaveBeenCalledTimes(1);
const req: Request = spy.mock.calls[0]?.[0] as Request;
expect(req).toBeInstanceOf(Request);
const h: Headers = req.headers;
expect(h.get("X-Emby-Token")).toBe("XYZ");
expect(h.get("X-Emby-Authorization")).toContain("jellarr");
} finally {
spy.mockRestore();
}
});
});

43
tests/lib/logger.spec.ts Normal file
View File

@@ -0,0 +1,43 @@
import { describe, it, expect, vi, Mock } from "vitest";
import { logger } from "../../src/lib/logger";
describe("lib/logger", () => {
it("whenInfoCalled_thenWritesToConsoleLog()", () => {
// Arrange
const spy: Mock = vi.spyOn(console, "log").mockImplementation(() => {});
// Act
logger.info("hello");
// Assert
expect(spy).toHaveBeenCalledWith("hello");
spy.mockRestore();
});
it("whenWarnCalled_thenWritesToConsoleWarn()", () => {
// Arrange
const spy: Mock = vi.spyOn(console, "warn").mockImplementation(() => {});
// Act
logger.warn("w");
// Assert
expect(spy).toHaveBeenCalledWith("w");
spy.mockRestore();
});
it("whenErrorCalled_thenWritesToConsoleError()", () => {
// Arrange
const spy: Mock = vi.spyOn(console, "error").mockImplementation(() => {});
// Act
logger.error("e");
// Assert
expect(spy).toHaveBeenCalledWith("e");
spy.mockRestore();
});
});

View File

@@ -0,0 +1,74 @@
import { describe, it, expect } from "vitest";
import {
fromJFRepos,
toJFRepos,
equalReposUnordered,
} from "../../src/mappers/system";
import type {
JFPluginRepo,
PluginRepositoryCfg,
} from "../../src/domain/system/types";
describe("mappers/system", () => {
it("whenJellyfinReposGiven_thenFromJFReposMapsToCfgShape()", () => {
// Arrange
const jf: JFPluginRepo[] = [
{ Name: "A", Url: "https://a", Enabled: true },
{ Name: "B", Url: "https://b", Enabled: false },
];
// Act
const out: PluginRepositoryCfg[] = fromJFRepos(jf);
// Assert
expect(out).toEqual<PluginRepositoryCfg[]>([
{ name: "A", url: "https://a", enabled: true },
{ name: "B", url: "https://b", enabled: false },
]);
});
it("whenCfgReposGiven_thenToJFReposMapsToJellyfinShape()", () => {
// Arrange
const cfg: PluginRepositoryCfg[] = [
{ name: "A", url: "https://a", enabled: true },
{ name: "B", url: "https://b", enabled: false },
];
// Act
const out: JFPluginRepo[] = toJFRepos(cfg);
// Assert
expect(out).toEqual<JFPluginRepo[]>([
{ Name: "A", Url: "https://a", Enabled: true },
{ Name: "B", Url: "https://b", Enabled: false },
]);
});
it("whenReposReordered_thenEqualReposUnorderedReturnsTrue()", () => {
// Arrange
const a: PluginRepositoryCfg[] = [
{ name: "A", url: "https://a", enabled: true },
{ name: "B", url: "https://b", enabled: false },
];
const b: PluginRepositoryCfg[] = [
{ name: "B", url: "https://b", enabled: false },
{ name: "A", url: "https://a", enabled: true },
];
// Act & Assert
expect(equalReposUnordered(a, b)).toBe(true);
});
it("whenRepoEnabledFlagDiffers_thenEqualReposUnorderedReturnsFalse()", () => {
// Arrange
const a: PluginRepositoryCfg[] = [
{ name: "A", url: "https://a", enabled: true },
];
const b: PluginRepositoryCfg[] = [
{ name: "A", url: "https://a", enabled: false },
];
// Act & Assert
expect(equalReposUnordered(a, b)).toBe(false);
});
});

View File

@@ -0,0 +1,130 @@
import { describe, it, expect } from "vitest";
import { apply } from "../../../src/services/system/apply";
import type {
SystemCfg,
ServerConfiguration,
JFTrickplay,
PluginRepositoryCfg,
} from "../../../src/domain/system/types";
function makeConfig(
partial?: Partial<ServerConfiguration>,
): ServerConfiguration {
const base: ServerConfiguration = {
EnableMetrics: false,
PluginRepositories: [],
TrickplayOptions: undefined,
} as ServerConfiguration;
return { ...base, ...(partial ?? {}) } as ServerConfiguration;
}
describe("services/system/apply", () => {
it("whenDesiredEmpty_thenApplyReturnsCurrentUnchanged()", () => {
// Arrange
const current: ServerConfiguration = makeConfig({ EnableMetrics: true });
const desired: SystemCfg = {};
// Act
const updated: ServerConfiguration = apply(current, desired);
// Assert
expect(updated).toEqual(current);
});
it("whenEnableMetricsDiffers_thenApplyUpdatesOnlyEnableMetrics()", () => {
// Arrange
const current: ServerConfiguration = makeConfig({ EnableMetrics: false });
const desired: SystemCfg = { enableMetrics: true };
// Act
const updated: ServerConfiguration = apply(current, desired);
// Assert
expect(updated.EnableMetrics).toBe(true);
expect(updated.PluginRepositories).toEqual(current.PluginRepositories);
expect(updated.TrickplayOptions).toEqual(current.TrickplayOptions);
});
it("whenPluginRepositoriesProvided_thenApplyReplacesRepos()", () => {
// Arrange
const current: ServerConfiguration = makeConfig({
PluginRepositories: [{ Name: "A", Url: "https://a", Enabled: true }],
});
const repos: PluginRepositoryCfg[] = [
{ name: "B", url: "https://b", enabled: true },
{ name: "A", url: "https://a", enabled: true },
];
const desired: SystemCfg = { pluginRepositories: repos };
// Act
const updated: ServerConfiguration = apply(current, desired);
// Assert
expect(updated.PluginRepositories).toEqual([
{ Name: "B", Url: "https://b", Enabled: true },
{ Name: "A", Url: "https://a", Enabled: true },
]);
});
describe("trickplay tri-state (true/false/null)", () => {
it("whenBothBooleansProvided_thenApplySetsBothFields()", () => {
// Arrange
const current: ServerConfiguration = makeConfig({
TrickplayOptions: {} as JFTrickplay,
});
const desired: SystemCfg = {
trickplayOptions: {
enableHwAcceleration: true,
enableHwEncoding: false,
},
};
// Act
const updated: ServerConfiguration = apply(current, desired);
// Assert
expect(updated.TrickplayOptions?.EnableHwAcceleration).toBe(true);
expect(updated.TrickplayOptions?.EnableHwEncoding).toBe(false);
});
it("whenAccelerationIsNull_thenApplyOmitsAccelerationKey()", () => {
// Arrange
const current: ServerConfiguration = makeConfig({
TrickplayOptions: {
EnableHwAcceleration: true,
EnableHwEncoding: true,
},
});
const desired: SystemCfg = {
trickplayOptions: { enableHwAcceleration: null },
};
// Act
const updated: ServerConfiguration = apply(current, desired);
// Assert
expect(updated.TrickplayOptions?.EnableHwAcceleration).toBeUndefined();
expect(updated.TrickplayOptions?.EnableHwEncoding).toBe(true);
});
it("whenOnlyAccelerationProvided_thenApplyLeavesEncodingUntouched()", () => {
// Arrange
const current: ServerConfiguration = makeConfig({
TrickplayOptions: {
EnableHwAcceleration: false,
EnableHwEncoding: true,
},
});
const desired: SystemCfg = {
trickplayOptions: { enableHwAcceleration: true },
};
// Act
const updated: ServerConfiguration = apply(current, desired);
// Assert
expect(updated.TrickplayOptions?.EnableHwAcceleration).toBe(true);
expect(updated.TrickplayOptions?.EnableHwEncoding).toBe(true);
});
});
});

View File

@@ -11,5 +11,5 @@
"outDir": "dist",
"noImplicitAny": true
},
"include": ["src", "generated"]
"include": ["src", "tests", "generated"]
}