mirror of
https://github.com/ajnart/homarr.git
synced 2026-01-29 10:49:14 +01:00
236 lines
8.4 KiB
TypeScript
236 lines
8.4 KiB
TypeScript
import { join } from "path";
|
|
import { GenericContainer, getContainerRuntimeClient, ImageName, Wait } from "testcontainers";
|
|
import type { StartedTestContainer } from "testcontainers";
|
|
import { beforeAll, describe, expect, test } from "vitest";
|
|
|
|
import { SabnzbdIntegration } from "../src";
|
|
import type { DownloadClientItem } from "../src/interfaces/downloads/download-client-items";
|
|
|
|
const DEFAULT_API_KEY = "8r45mfes43s3iw7x3oecto6dl9ilxnf9";
|
|
const IMAGE_NAME = "linuxserver/sabnzbd:latest";
|
|
|
|
describe("Sabnzbd integration", () => {
|
|
beforeAll(async () => {
|
|
const containerRuntimeClient = await getContainerRuntimeClient();
|
|
await containerRuntimeClient.image.pull(ImageName.fromString(IMAGE_NAME));
|
|
}, 100_000);
|
|
|
|
test("Test connection should work", async () => {
|
|
// Arrange
|
|
const startedContainer = await createSabnzbdContainer().start();
|
|
const sabnzbdIntegration = createSabnzbdIntegration(startedContainer, DEFAULT_API_KEY);
|
|
|
|
// Act
|
|
const actAsync = async () => await sabnzbdIntegration.testConnectionAsync();
|
|
|
|
// Assert
|
|
await expect(actAsync()).resolves.not.toThrow();
|
|
|
|
// Cleanup
|
|
await startedContainer.stop();
|
|
}, 30_000); // Timeout of 30 seconds
|
|
|
|
test("Test connection should fail with wrong ApiKey", async () => {
|
|
// Arrange
|
|
const startedContainer = await createSabnzbdContainer().start();
|
|
const sabnzbdIntegration = createSabnzbdIntegration(startedContainer, "wrong-api-key");
|
|
|
|
// Act
|
|
const actAsync = async () => await sabnzbdIntegration.testConnectionAsync();
|
|
|
|
// Assert
|
|
await expect(actAsync()).rejects.toThrow();
|
|
|
|
// Cleanup
|
|
await startedContainer.stop();
|
|
}, 30_000); // Timeout of 30 seconds
|
|
|
|
test("pauseQueueAsync should work", async () => {
|
|
// Arrange
|
|
const startedContainer = await createSabnzbdContainer().start();
|
|
const sabnzbdIntegration = createSabnzbdIntegration(startedContainer, DEFAULT_API_KEY);
|
|
|
|
// Acts
|
|
const actAsync = async () => await sabnzbdIntegration.pauseQueueAsync();
|
|
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync();
|
|
|
|
// Assert
|
|
await expect(actAsync()).resolves.not.toThrow();
|
|
await expect(getAsync()).resolves.toMatchObject({ status: { paused: true } });
|
|
|
|
// Cleanup
|
|
await startedContainer.stop();
|
|
}, 30_000); // Timeout of 30 seconds
|
|
|
|
test("resumeQueueAsync should work", async () => {
|
|
// Arrange
|
|
const startedContainer = await createSabnzbdContainer().start();
|
|
const sabnzbdIntegration = createSabnzbdIntegration(startedContainer, DEFAULT_API_KEY);
|
|
await sabnzbdIntegration.pauseQueueAsync();
|
|
|
|
// Acts
|
|
const actAsync = async () => await sabnzbdIntegration.resumeQueueAsync();
|
|
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync();
|
|
|
|
// Assert
|
|
await expect(actAsync()).resolves.not.toThrow();
|
|
await expect(getAsync()).resolves.toMatchObject({
|
|
status: { paused: false },
|
|
});
|
|
|
|
// Cleanup
|
|
await startedContainer.stop();
|
|
}, 30_000); // Timeout of 30 seconds
|
|
|
|
test("Items should be empty", async () => {
|
|
// Arrange
|
|
const startedContainer = await createSabnzbdContainer().start();
|
|
const sabnzbdIntegration = createSabnzbdIntegration(startedContainer, DEFAULT_API_KEY);
|
|
|
|
// Act
|
|
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync();
|
|
|
|
// Assert
|
|
await expect(getAsync()).resolves.not.toThrow();
|
|
await expect(getAsync()).resolves.toMatchObject({
|
|
items: [],
|
|
});
|
|
|
|
// Cleanup
|
|
await startedContainer.stop();
|
|
}, 30_000); // Timeout of 30 seconds
|
|
|
|
test("1 Items should exist after adding one", async () => {
|
|
// Arrange
|
|
const startedContainer = await createSabnzbdContainer().start();
|
|
const sabnzbdIntegration = createSabnzbdIntegration(startedContainer, DEFAULT_API_KEY);
|
|
await sabNzbdAddItemAsync(startedContainer, DEFAULT_API_KEY, sabnzbdIntegration);
|
|
|
|
// Act
|
|
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync();
|
|
|
|
// Assert
|
|
await expect(getAsync()).resolves.not.toThrow();
|
|
expect((await getAsync()).items).toHaveLength(1);
|
|
|
|
// Cleanup
|
|
await startedContainer.stop();
|
|
}, 30_000); // Timeout of 30 seconds
|
|
|
|
test("Pause item should work", async () => {
|
|
// Arrange
|
|
const startedContainer = await createSabnzbdContainer().start();
|
|
const sabnzbdIntegration = createSabnzbdIntegration(startedContainer, DEFAULT_API_KEY);
|
|
const item = await sabNzbdAddItemAsync(startedContainer, DEFAULT_API_KEY, sabnzbdIntegration);
|
|
|
|
// Act
|
|
const actAsync = async () => await sabnzbdIntegration.pauseItemAsync(item);
|
|
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync();
|
|
|
|
// Assert
|
|
await expect(getAsync()).resolves.toMatchObject({ items: [{ ...item, state: "downloading" }] });
|
|
await expect(actAsync()).resolves.not.toThrow();
|
|
await expect(getAsync()).resolves.toMatchObject({ items: [{ ...item, state: "paused" }] });
|
|
|
|
// Cleanup
|
|
await startedContainer.stop();
|
|
}, 30_000); // Timeout of 30 seconds
|
|
|
|
test("Resume item should work", async () => {
|
|
// Arrange
|
|
const startedContainer = await createSabnzbdContainer().start();
|
|
const sabnzbdIntegration = createSabnzbdIntegration(startedContainer, DEFAULT_API_KEY);
|
|
const item = await sabNzbdAddItemAsync(startedContainer, DEFAULT_API_KEY, sabnzbdIntegration);
|
|
await sabnzbdIntegration.pauseItemAsync(item);
|
|
|
|
// Act
|
|
const actAsync = async () => await sabnzbdIntegration.resumeItemAsync(item);
|
|
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync();
|
|
|
|
// Assert
|
|
await expect(getAsync()).resolves.toMatchObject({ items: [{ ...item, state: "paused" }] });
|
|
await expect(actAsync()).resolves.not.toThrow();
|
|
await expect(getAsync()).resolves.toMatchObject({ items: [{ ...item, state: "downloading" }] });
|
|
|
|
// Cleanup
|
|
await startedContainer.stop();
|
|
}, 30_000); // Timeout of 30 seconds
|
|
|
|
test("Delete item should result in empty items", async () => {
|
|
// Arrange
|
|
const startedContainer = await createSabnzbdContainer().start();
|
|
const sabnzbdIntegration = createSabnzbdIntegration(startedContainer, DEFAULT_API_KEY);
|
|
const item = await sabNzbdAddItemAsync(startedContainer, DEFAULT_API_KEY, sabnzbdIntegration);
|
|
|
|
// Act - fromDisk already doesn't work for sabnzbd, so only test deletion itself.
|
|
const actAsync = async () =>
|
|
await sabnzbdIntegration.deleteItemAsync({ ...item, progress: 0 } as DownloadClientItem, false);
|
|
const getAsync = async () => await sabnzbdIntegration.getClientJobsAndStatusAsync();
|
|
|
|
// Assert
|
|
await expect(actAsync()).resolves.not.toThrow();
|
|
await expect(getAsync()).resolves.toMatchObject({ items: [] });
|
|
|
|
// Cleanup
|
|
await startedContainer.stop();
|
|
}, 30_000); // Timeout of 30 seconds
|
|
});
|
|
|
|
const createSabnzbdContainer = () => {
|
|
return new GenericContainer(IMAGE_NAME)
|
|
.withCopyFilesToContainer([
|
|
{
|
|
source: join(__dirname, "/volumes/usenet/sabnzbd.ini"),
|
|
target: "/config/sabnzbd.ini",
|
|
},
|
|
])
|
|
.withExposedPorts(1212)
|
|
.withEnvironment({ PUID: "0", PGID: "0" })
|
|
.withWaitStrategy(Wait.forHttp("/", 1212));
|
|
};
|
|
|
|
const createSabnzbdIntegration = (container: StartedTestContainer, apiKey: string) => {
|
|
return new SabnzbdIntegration({
|
|
id: "1",
|
|
decryptedSecrets: [
|
|
{
|
|
kind: "apiKey",
|
|
value: apiKey,
|
|
},
|
|
],
|
|
name: "Sabnzbd",
|
|
url: `http://${container.getHost()}:${container.getMappedPort(1212)}`,
|
|
});
|
|
};
|
|
|
|
const sabNzbdAddItemAsync = async (
|
|
container: StartedTestContainer,
|
|
apiKey: string,
|
|
integration: SabnzbdIntegration,
|
|
) => {
|
|
// Add nzb file in the watch folder
|
|
await container.copyFilesToContainer([
|
|
{
|
|
source: join(__dirname, "/volumes/usenet/test_download_100MB.nzb"),
|
|
target: "/nzb/test_download_100MB.nzb",
|
|
},
|
|
]);
|
|
// Adding file is faster than triggering scan of the watch folder
|
|
// (local add: 1.4-1.6s, scan trigger: 2.5-2.7s, auto scan: 2.9-3s)
|
|
await fetch(
|
|
`http://${container.getHost()}:${container.getMappedPort(1212)}/api` +
|
|
"?mode=addlocalfile" +
|
|
"&name=%2Fnzb%2Ftest_download_100MB.nzb" +
|
|
`&apikey=${apiKey}`,
|
|
);
|
|
// Retries up to 5 times to let SabNzbd scan and process the nzb (1 retry should suffice tbh)
|
|
for (let i = 0; i < 5; i++) {
|
|
const {
|
|
items: [item],
|
|
} = await integration.getClientJobsAndStatusAsync();
|
|
if (item) return item;
|
|
}
|
|
// Throws if it can't find the item
|
|
throw new Error("No item found");
|
|
};
|