feat(onboarding): enhance integration setup with Docker discovery and URL templating (#5607)
This commit is contained in:
1 parent
0a6467979a
commit
dd7a4908a3
68 files changed
+2922
-434
No files matched your search
@@ -0,0 +1,29 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { getIntegrationApiKeyUrl } from "../integration";
|
||||
|
||||
describe("getIntegrationApiKeyUrl", () => {
|
||||
it("builds URL for sonarr", () => {
|
||||
expect(getIntegrationApiKeyUrl("http://192.168.1.10:8989", "sonarr")).toBe(
|
||||
"http://192.168.1.10:8989/settings/general",
|
||||
);
|
||||
});
|
||||
|
||||
it("builds URL for jellyfin", () => {
|
||||
expect(getIntegrationApiKeyUrl("http://media.local:8096", "jellyfin")).toBe(
|
||||
"http://media.local:8096/web/index.html#!/dashboard/keys",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns null for integrations without apiKeySettingsPath", () => {
|
||||
expect(getIntegrationApiKeyUrl("http://localhost", "plex")).toBeNull();
|
||||
});
|
||||
|
||||
it("returns null for empty URL", () => {
|
||||
expect(getIntegrationApiKeyUrl("", "sonarr")).toBeNull();
|
||||
});
|
||||
|
||||
it("strips trailing slash from base URL", () => {
|
||||
expect(getIntegrationApiKeyUrl("http://sonarr.local/", "sonarr")).toBe("http://sonarr.local/settings/general");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,119 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
extractContainerImageName,
|
||||
matchIntegrationKind,
|
||||
matchIntegrationKindFromContainer,
|
||||
} from "../docker-integration-match";
|
||||
|
||||
describe("extractContainerImageName", () => {
|
||||
it("extracts name from full image reference", () => {
|
||||
expect(extractContainerImageName("linuxserver/sonarr:latest")).toBe("sonarr");
|
||||
});
|
||||
|
||||
it("extracts name from ghcr image", () => {
|
||||
expect(extractContainerImageName("ghcr.io/org/radarr:v5")).toBe("radarr");
|
||||
});
|
||||
|
||||
it("handles plain image name", () => {
|
||||
expect(extractContainerImageName("jellyfin")).toBe("jellyfin");
|
||||
});
|
||||
|
||||
it("handles empty string", () => {
|
||||
expect(extractContainerImageName("")).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("matchIntegrationKind", () => {
|
||||
it("matches exact kind name", () => {
|
||||
expect(matchIntegrationKind("sonarr")).toBe("sonarr");
|
||||
});
|
||||
|
||||
it("matches case-insensitively", () => {
|
||||
expect(matchIntegrationKind("Sonarr")).toBe("sonarr");
|
||||
});
|
||||
|
||||
it("matches icon slug for pi-hole", () => {
|
||||
expect(matchIntegrationKind("pi-hole")).toBe("piHole");
|
||||
});
|
||||
|
||||
it("matches alias pihole", () => {
|
||||
expect(matchIntegrationKind("pihole")).toBe("piHole");
|
||||
});
|
||||
|
||||
it("matches alias adguardhome", () => {
|
||||
expect(matchIntegrationKind("adguardhome")).toBe("adGuardHome");
|
||||
});
|
||||
|
||||
it("matches alias homeassistant", () => {
|
||||
expect(matchIntegrationKind("homeassistant")).toBe("homeAssistant");
|
||||
});
|
||||
|
||||
it("matches substring in image name", () => {
|
||||
expect(matchIntegrationKind("hotio-sonarr")).toBe("sonarr");
|
||||
});
|
||||
|
||||
it("returns null for unknown", () => {
|
||||
expect(matchIntegrationKind("nginx")).toBeNull();
|
||||
});
|
||||
|
||||
it("returns null for empty string", () => {
|
||||
expect(matchIntegrationKind("")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("matchIntegrationKindFromContainer", () => {
|
||||
it("matches from image name first", () => {
|
||||
expect(
|
||||
matchIntegrationKindFromContainer({
|
||||
image: "linuxserver/sonarr:latest",
|
||||
name: "my-sonarr",
|
||||
}),
|
||||
).toBe("sonarr");
|
||||
});
|
||||
|
||||
it("falls back to container name", () => {
|
||||
expect(
|
||||
matchIntegrationKindFromContainer({
|
||||
image: "custom/myapp:latest",
|
||||
name: "radarr",
|
||||
}),
|
||||
).toBe("radarr");
|
||||
});
|
||||
|
||||
it("returns null when neither matches", () => {
|
||||
expect(
|
||||
matchIntegrationKindFromContainer({
|
||||
image: "nginx:latest",
|
||||
name: "web-proxy",
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("excludes dockerHub even when image contains docker", () => {
|
||||
expect(
|
||||
matchIntegrationKindFromContainer({
|
||||
image: "portainer/portainer-ce:latest",
|
||||
name: "docker-proxy",
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("excludes ical from container matching", () => {
|
||||
expect(
|
||||
matchIntegrationKindFromContainer({
|
||||
image: "ical-relay:latest",
|
||||
name: "ical",
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("excludes github from container matching", () => {
|
||||
expect(
|
||||
matchIntegrationKindFromContainer({
|
||||
image: "github-runner:latest",
|
||||
name: "github-actions",
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { getIntegrationDefaultPort } from "../integration";
|
||||
import { buildIntegrationUrl } from "../integration-url-template";
|
||||
|
||||
describe("buildIntegrationUrl", () => {
|
||||
it("builds subdomain URL", () => {
|
||||
expect(buildIntegrationUrl("sonarr", "homelab.local", "subdomain")).toBe("https://sonarr.homelab.local");
|
||||
});
|
||||
|
||||
it("builds hostPort URL with default port", () => {
|
||||
expect(buildIntegrationUrl("sonarr", "192.168.1.1", "hostPort")).toBe("http://192.168.1.1:8989");
|
||||
});
|
||||
|
||||
it("uses docker port over default port", () => {
|
||||
expect(buildIntegrationUrl("sonarr", "192.168.1.1", "hostPort", 9999)).toBe("http://192.168.1.1:9999");
|
||||
});
|
||||
|
||||
it("builds hostPort URL without port when none known", () => {
|
||||
expect(buildIntegrationUrl("mock", "192.168.1.1", "hostPort")).toBe("http://192.168.1.1");
|
||||
});
|
||||
|
||||
it("strips trailing slashes from host", () => {
|
||||
expect(buildIntegrationUrl("sonarr", "homelab.local/", "subdomain")).toBe("https://sonarr.homelab.local");
|
||||
expect(buildIntegrationUrl("sonarr", "homelab.local///", "subdomain")).toBe("https://sonarr.homelab.local");
|
||||
});
|
||||
|
||||
it("returns empty string for empty host", () => {
|
||||
expect(buildIntegrationUrl("sonarr", "", "subdomain")).toBe("");
|
||||
});
|
||||
|
||||
it("uses pi-hole slug for subdomain mode", () => {
|
||||
expect(buildIntegrationUrl("piHole", "example.com", "subdomain")).toBe("https://pi-hole.example.com");
|
||||
});
|
||||
|
||||
it("reads default ports from integration definitions", () => {
|
||||
expect(getIntegrationDefaultPort("radarr")).toBe(7878);
|
||||
expect(getIntegrationDefaultPort("prowlarr")).toBe(9696);
|
||||
expect(getIntegrationDefaultPort("jellyfin")).toBe(8096);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user