@gorizond/catalog-backend-module-fleet
Version:
Backstage catalog backend module for Rancher Fleet GitOps entities
810 lines (809 loc) • 38 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const entityMapper_1 = require("./entityMapper");
// Helper to safely access entity spec
function getSpec(entity) {
return entity.spec;
}
// ============================================================================
// Test Fixtures
// ============================================================================
const createMockClusterConfig = (overrides = {}) => ({
name: "test-cluster",
url: "https://rancher.example.com",
namespaces: [{ name: "fleet-default" }],
...overrides,
});
const createMockContext = (overrides = {}) => ({
cluster: createMockClusterConfig(),
locationKey: "fleet:test",
autoTechdocsRef: true,
...overrides,
});
const createMockGitRepo = (overrides = {}) => ({
apiVersion: "fleet.cattle.io/v1alpha1",
kind: "GitRepo",
metadata: {
name: "my-app",
namespace: "fleet-default",
uid: "123-456",
labels: {
"objectset.rio.cattle.io/hash": "hash-abc",
},
annotations: {},
},
spec: {
repo: "https://github.com/example/my-app",
branch: "main",
paths: ["./"],
targets: [{ name: "production" }],
},
status: {
display: {
state: "Ready",
readyClusters: "3/3",
},
},
...overrides,
});
const createMockBundle = (overrides = {}) => ({
apiVersion: "fleet.cattle.io/v1alpha1",
kind: "Bundle",
metadata: {
name: "my-app-main",
namespace: "fleet-default",
uid: "789-012",
labels: {
"fleet.cattle.io/repo-name": "my-app",
"fleet.cattle.io/bundle-path": ".",
"fleet.cattle.io/commit": "abc123",
"objectset.rio.cattle.io/hash": "bundle-hash",
},
annotations: {},
},
spec: {
targets: [{ name: "production" }],
},
status: {
display: {
state: "Ready",
readyClusters: "3/3",
},
},
...overrides,
});
const createMockBundleDeployment = (overrides = {}) => ({
apiVersion: "fleet.cattle.io/v1alpha1",
kind: "BundleDeployment",
metadata: {
name: "my-app-main",
namespace: "cluster-fleet-default-prod-cluster",
uid: "345-678",
labels: {
"fleet.cattle.io/bundle-name": "my-app-main",
},
annotations: {},
},
status: {
display: {
state: "Ready",
message: "Deployed successfully",
},
ready: true,
},
...overrides,
});
const createMockFleetYaml = (overrides = {}) => ({
defaultNamespace: "my-app",
helm: {
releaseName: "my-app-release",
},
backstage: {
type: "service",
description: "My application",
owner: "team-platform",
tags: ["production"],
},
...overrides,
});
// ============================================================================
// toBackstageName Tests
// ============================================================================
describe("toBackstageName", () => {
it("should convert uppercase to lowercase", () => {
expect((0, entityMapper_1.toBackstageName)("MyApp")).toBe("myapp");
});
it("should replace invalid characters with hyphens", () => {
expect((0, entityMapper_1.toBackstageName)("my_app.name")).toBe("my-app-name");
});
it("should collapse multiple hyphens", () => {
expect((0, entityMapper_1.toBackstageName)("my---app")).toBe("my-app");
});
it("should remove leading and trailing hyphens", () => {
expect((0, entityMapper_1.toBackstageName)("-my-app-")).toBe("my-app");
});
it("should handle empty string", () => {
expect((0, entityMapper_1.toBackstageName)("")).toBe("fleet-entity");
});
it("should truncate to 63 characters", () => {
const longName = "a".repeat(100);
expect((0, entityMapper_1.toBackstageName)(longName).length).toBe(63);
});
it("should strip trailing hyphens after truncation", () => {
const value = "a-".repeat(40); // results in trailing hyphen when sliced
expect((0, entityMapper_1.toBackstageName)(value).endsWith("-")).toBe(false);
});
it("should create stable short names with hash", () => {
const value = "very-long-name".repeat(10);
const result = (0, entityMapper_1.toStableBackstageName)(value, 30);
expect(result.length).toBeLessThanOrEqual(30);
expect(result).toMatch(/-[a-f0-9]{6}$/);
});
it("should handle special characters", () => {
expect((0, entityMapper_1.toBackstageName)("my@app#name!")).toBe("my-app-name");
});
it("should handle numbers", () => {
expect((0, entityMapper_1.toBackstageName)("app123")).toBe("app123");
});
});
// ============================================================================
// toEntityNamespace Tests
// ============================================================================
describe("toEntityNamespace", () => {
it("should convert fleet namespace to entity namespace", () => {
expect((0, entityMapper_1.toEntityNamespace)("fleet-default")).toBe("fleet-default");
});
it("should handle uppercase", () => {
expect((0, entityMapper_1.toEntityNamespace)("Fleet-Default")).toBe("fleet-default");
});
it("should handle special characters", () => {
expect((0, entityMapper_1.toEntityNamespace)("fleet_local")).toBe("fleet-local");
});
});
// ============================================================================
// mapFleetClusterToDomain Tests
// ============================================================================
describe("mapFleetClusterToDomain", () => {
it("should create a Domain entity from cluster config", () => {
const context = createMockContext();
const entity = (0, entityMapper_1.mapFleetClusterToDomain)(context);
expect(entity.kind).toBe("Domain");
expect(entity.metadata.name).toBe("test-cluster");
expect(entity.metadata.namespace).toBe("default");
expect(getSpec(entity).owner).toBe("platform-team");
});
it("should extract hostname from URL for description", () => {
const context = createMockContext({
cluster: createMockClusterConfig({
url: "https://rancher.example.com/k8s/clusters/local",
}),
});
const entity = (0, entityMapper_1.mapFleetClusterToDomain)(context);
expect(entity.metadata.description).toContain("rancher.example.com");
});
it("should include Fleet annotations", () => {
const context = createMockContext();
const entity = (0, entityMapper_1.mapFleetClusterToDomain)(context);
const annotations = entity.metadata.annotations;
expect(annotations[entityMapper_1.ANNOTATION_FLEET_CLUSTER]).toBe("test-cluster");
expect(annotations["fleet.cattle.io/url"]).toBe("https://rancher.example.com");
});
it("should include namespace list in annotations", () => {
const context = createMockContext({
cluster: createMockClusterConfig({
namespaces: [{ name: "fleet-default" }, { name: "fleet-local" }],
}),
});
const entity = (0, entityMapper_1.mapFleetClusterToDomain)(context);
const annotations = entity.metadata.annotations;
expect(annotations["fleet.cattle.io/namespaces"]).toBe("fleet-default,fleet-local");
});
it("should include links to Rancher", () => {
const context = createMockContext();
const entity = (0, entityMapper_1.mapFleetClusterToDomain)(context);
expect(entity.metadata.links).toContainEqual({
url: "https://rancher.example.com",
title: "Rancher Fleet",
});
});
it("should use custom entity namespace when provided", () => {
const context = createMockContext();
const entity = (0, entityMapper_1.mapFleetClusterToDomain)(context, "custom-namespace");
expect(entity.metadata.namespace).toBe("custom-namespace");
});
it("should handle invalid URL gracefully", () => {
const context = createMockContext({
cluster: createMockClusterConfig({
url: "not-a-valid-url",
}),
});
const entity = (0, entityMapper_1.mapFleetClusterToDomain)(context);
expect(entity.metadata.description).toContain("not-a-valid-url");
});
});
// ============================================================================
// mapGitRepoToSystem Tests
// ============================================================================
describe("mapGitRepoToSystem", () => {
it("should create a System entity from GitRepo", () => {
const gitRepo = createMockGitRepo();
const context = createMockContext();
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(entity.kind).toBe("System");
expect(entity.metadata.name).toBe("my-app");
expect(entity.metadata.namespace).toBe("fleet-default");
});
it("should set type to service by default", () => {
const gitRepo = createMockGitRepo();
const context = createMockContext();
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(getSpec(entity).type).toBeUndefined(); // System has no type
});
it("should include Fleet annotations", () => {
const gitRepo = createMockGitRepo();
const context = createMockContext();
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
const annotations = entity.metadata.annotations;
expect(annotations[entityMapper_1.ANNOTATION_FLEET_REPO]).toBe("https://github.com/example/my-app");
expect(annotations[entityMapper_1.ANNOTATION_FLEET_BRANCH]).toBe("main");
expect(annotations[entityMapper_1.ANNOTATION_FLEET_STATUS]).toBe("Ready");
expect(annotations[entityMapper_1.ANNOTATION_FLEET_CLUSTER]).toBe("test-cluster");
expect(annotations[entityMapper_1.ANNOTATION_FLEET_NAMESPACE]).toBe("fleet-default");
});
it("should include Kubernetes annotations (namespace/selector)", () => {
const gitRepo = createMockGitRepo();
const context = createMockContext();
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
const annotations = entity.metadata.annotations;
expect(annotations["backstage.io/kubernetes-namespace"]).toBe("fleet-default");
expect(annotations["backstage.io/kubernetes-label-selector"]).toBe("app.kubernetes.io/name=my-app");
});
it("should include source location annotation", () => {
const gitRepo = createMockGitRepo();
const context = createMockContext();
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
const annotations = entity.metadata.annotations;
expect(annotations["backstage.io/source-location"]).toBe("url:https://github.com/example/my-app");
});
it("should set lifecycle based on status", () => {
const gitRepo = createMockGitRepo({
status: { display: { state: "Ready" } },
});
const context = createMockContext();
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(getSpec(entity).lifecycle).toBe("production");
});
it("should set lifecycle to deprecated for error states", () => {
const gitRepo = createMockGitRepo({
status: { display: { state: "ErrApplied" } },
});
const context = createMockContext();
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(getSpec(entity).lifecycle).toBe("deprecated");
});
it("should include links to Git repository", () => {
const gitRepo = createMockGitRepo();
const context = createMockContext();
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(entity.metadata.links).toContainEqual({
url: "https://github.com/example/my-app",
title: "Git Repository",
});
});
it("should use fleet.yaml description when available", () => {
const gitRepo = createMockGitRepo();
const fleetYaml = createMockFleetYaml();
const context = createMockContext({ fleetYaml });
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(entity.metadata.description).toBe("My application");
});
it("should use fleet.yaml owner when available", () => {
const gitRepo = createMockGitRepo();
const fleetYaml = createMockFleetYaml();
const context = createMockContext({ fleetYaml });
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(getSpec(entity).owner).toBe("team-platform");
});
it("should derive owner from repo when fleet.yaml owner is missing", () => {
const gitRepo = createMockGitRepo({
metadata: {
name: "my-app",
namespace: "fleet-default",
annotations: {},
},
status: { display: { state: "Ready" } },
});
const context = createMockContext({ fleetYaml: undefined });
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(getSpec(entity).owner).toBe("group:default/example");
});
it("should add techdocs ref annotation when enabled", () => {
const gitRepo = createMockGitRepo();
const context = createMockContext();
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
const annotations = entity.metadata.annotations;
expect(annotations["backstage.io/techdocs-ref"]).toBe("url:https://github.com/example/my-app/-/tree/main");
});
it("should use gitrepo description when fleet.yaml is missing", () => {
const gitRepo = createMockGitRepo({
metadata: {
name: "my-app",
namespace: "fleet-default",
annotations: { description: "Description from GitRepo" },
},
});
const context = createMockContext({ fleetYaml: undefined });
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(entity.metadata.description).toBe("Description from GitRepo");
});
it("should prefer field.cattle.io/description when present", () => {
const gitRepo = createMockGitRepo({
metadata: {
name: "my-app",
namespace: "fleet-default",
annotations: {
"field.cattle.io/description": "Fleet annotation description",
description: "Fallback description",
},
},
});
const context = createMockContext({ fleetYaml: undefined });
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(entity.metadata.description).toBe("Fleet annotation description");
});
it("should use fleet.yaml type when available", () => {
const gitRepo = createMockGitRepo();
const fleetYaml = createMockFleetYaml({
backstage: { type: "website" },
});
const context = createMockContext({ fleetYaml });
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(getSpec(entity).type).toBeUndefined();
});
it("should include fleet.yaml tags", () => {
const gitRepo = createMockGitRepo();
const fleetYaml = createMockFleetYaml();
const context = createMockContext({ fleetYaml });
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(entity.metadata.tags).toContain("production");
expect(entity.metadata.tags).toContain("fleet");
});
it("should include fleet.yaml dependsOn", () => {
const gitRepo = createMockGitRepo();
const fleetYaml = createMockFleetYaml({
backstage: {
dependsOn: ["component:default/database"],
},
});
const context = createMockContext({ fleetYaml });
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(getSpec(entity).dependsOn).toContain("component:default/database");
});
it("should include fleet.yaml providesApis references", () => {
const gitRepo = createMockGitRepo();
const fleetYaml = createMockFleetYaml({
backstage: {
providesApis: [{ name: "my-api", type: "openapi" }],
},
});
const context = createMockContext({ fleetYaml });
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(getSpec(entity).providesApis).toContain("api:fleet-default/my-api");
});
it("should include fleet.yaml consumesApis", () => {
const gitRepo = createMockGitRepo();
const fleetYaml = createMockFleetYaml({
backstage: {
consumesApis: ["api:default/auth-api"],
},
});
const context = createMockContext({ fleetYaml });
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(getSpec(entity).consumesApis).toContain("api:default/auth-api");
});
it("should merge custom annotations from fleet.yaml", () => {
const gitRepo = createMockGitRepo();
const fleetYaml = createMockFleetYaml({
backstage: {
annotations: {
"pagerduty.com/integration-key": "abc123",
},
},
});
const context = createMockContext({ fleetYaml });
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
const annotations = entity.metadata.annotations;
expect(annotations["pagerduty.com/integration-key"]).toBe("abc123");
});
it("should handle missing metadata gracefully", () => {
const gitRepo = {
spec: { repo: "https://github.com/test/repo" },
};
const context = createMockContext();
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(entity.metadata.name).toBe("fleet-gitrepo");
expect(entity.metadata.namespace).toBe("fleet-default");
});
it("should handle missing status gracefully", () => {
const gitRepo = createMockGitRepo({ status: undefined });
const context = createMockContext();
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
expect(getSpec(entity).lifecycle).toBe("production");
});
it("should include ready clusters in annotations", () => {
const gitRepo = createMockGitRepo();
const context = createMockContext();
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
const annotations = entity.metadata.annotations;
expect(annotations["fleet.cattle.io/ready-clusters"]).toBe("3/3");
});
it("should include targets in annotations", () => {
const gitRepo = createMockGitRepo({
spec: {
repo: "https://github.com/test/repo",
targets: [{ name: "prod" }, { name: "staging" }],
},
});
const context = createMockContext();
const entity = (0, entityMapper_1.mapGitRepoToSystem)(gitRepo, context);
const annotations = entity.metadata.annotations;
expect(annotations["fleet.cattle.io/targets"]).toBe('["prod","staging"]');
});
});
// ============================================================================
// mapBundleToResource Tests
// ============================================================================
describe("mapBundleToComponent", () => {
it("should create a Component entity from Bundle", () => {
const bundle = createMockBundle();
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleToComponent)(bundle, context);
expect(entity.kind).toBe("Component");
expect(entity.metadata.name).toBe("my-app-main");
expect(entity.metadata.namespace).toBe("fleet-default");
});
it("should set type to service", () => {
const bundle = createMockBundle();
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleToComponent)(bundle, context);
expect(getSpec(entity).type).toBe("service");
});
it("should include Fleet annotations", () => {
const bundle = createMockBundle();
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleToComponent)(bundle, context);
const annotations = entity.metadata.annotations;
expect(annotations[entityMapper_1.ANNOTATION_FLEET_STATUS]).toBe("Ready");
expect(annotations[entityMapper_1.ANNOTATION_FLEET_CLUSTER]).toBe("test-cluster");
expect(annotations["fleet.cattle.io/repo-name"]).toBe("my-app");
expect(annotations["fleet.cattle.io/bundle-path"]).toBe(".");
});
it("should include Kubernetes annotations", () => {
const bundle = createMockBundle();
const fleetYaml = createMockFleetYaml();
const context = createMockContext({ fleetYaml });
const entity = (0, entityMapper_1.mapBundleToComponent)(bundle, context);
const annotations = entity.metadata.annotations;
expect(annotations["backstage.io/kubernetes-namespace"]).toBe("my-app");
// Helm release name takes priority over objectset hash
expect(annotations["backstage.io/kubernetes-label-selector"]).toBe("app.kubernetes.io/instance=my-app-release");
});
it("should link to parent GitRepo System", () => {
const bundle = createMockBundle();
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleToComponent)(bundle, context);
expect(getSpec(entity).system).toBe("system:fleet-default/my-app");
});
it("should add techdocs entity annotation pointing to parent System", () => {
const bundle = createMockBundle();
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleToComponent)(bundle, context);
const annotations = entity.metadata.annotations;
expect(annotations["backstage.io/techdocs-entity"]).toBe("system:fleet-default/my-app");
});
it("should include fleet.yaml tags", () => {
const bundle = createMockBundle();
const fleetYaml = createMockFleetYaml();
const context = createMockContext({ fleetYaml });
const entity = (0, entityMapper_1.mapBundleToComponent)(bundle, context);
expect(entity.metadata.tags).toContain("production");
expect(entity.metadata.tags).toContain("fleet-bundle");
});
it("should use fleet.yaml owner when available", () => {
const bundle = createMockBundle();
const fleetYaml = createMockFleetYaml();
const context = createMockContext({ fleetYaml });
const entity = (0, entityMapper_1.mapBundleToComponent)(bundle, context);
expect(getSpec(entity).owner).toBe("team-platform");
});
it("should handle missing labels gracefully", () => {
const bundle = createMockBundle({
metadata: {
name: "orphan-bundle",
namespace: "fleet-default",
labels: {},
},
});
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleToComponent)(bundle, context);
expect(entity.metadata.name).toBe("orphan-bundle");
expect(getSpec(entity).dependsOn).toBeUndefined();
});
it("should include bundle dependsOn as Resource references", () => {
const bundle = createMockBundle({
spec: {
dependsOn: [{ name: "cert-manager-bundle" }],
},
});
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleToComponent)(bundle, context);
expect(getSpec(entity).dependsOn).toContain("resource:fleet-default/cert-manager-bundle");
});
it("should merge custom annotations from fleet.yaml", () => {
const bundle = createMockBundle();
const fleetYaml = createMockFleetYaml({
annotations: {
"custom.io/key": "value",
},
});
const context = createMockContext({ fleetYaml });
const entity = (0, entityMapper_1.mapBundleToComponent)(bundle, context);
const annotations = entity.metadata.annotations;
expect(annotations["custom.io/key"]).toBe("value");
});
});
// ============================================================================
// mapBundleDeploymentToResource Tests
// ============================================================================
describe("mapBundleDeploymentToResource", () => {
it("should create a Resource entity from BundleDeployment", () => {
const bd = createMockBundleDeployment();
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleDeploymentToResource)(bd, "prod-cluster", context);
expect(entity.kind).toBe("Resource");
expect(entity.metadata.name).toBe("my-app-main-prod-cluster");
// Namespace is converted from the BundleDeployment namespace
expect(entity.metadata.namespace).toBe("cluster-fleet-default-prod-cluster");
});
it("should set type to fleet-deployment", () => {
const bd = createMockBundleDeployment();
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleDeploymentToResource)(bd, "prod-cluster", context);
expect(getSpec(entity).type).toBe("fleet-deployment");
});
it("should include status annotations", () => {
const bd = createMockBundleDeployment();
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleDeploymentToResource)(bd, "prod-cluster", context);
const annotations = entity.metadata.annotations;
expect(annotations[entityMapper_1.ANNOTATION_FLEET_STATUS]).toBe("Ready");
expect(annotations[entityMapper_1.ANNOTATION_FLEET_CLUSTER]).toBe("prod-cluster");
});
it("should include message in annotations", () => {
const bd = createMockBundleDeployment();
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleDeploymentToResource)(bd, "prod-cluster", context);
const annotations = entity.metadata.annotations;
expect(annotations["fleet.cattle.io/message"]).toBe("Deployed successfully");
});
it("should include original and target identifiers", () => {
const bd = createMockBundleDeployment();
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleDeploymentToResource)(bd, "prod-cluster", context);
const annotations = entity.metadata.annotations;
expect(annotations["fleet.cattle.io/original-name"]).toBe("my-app-main-prod-cluster");
expect(annotations["fleet.cattle.io/target-cluster-id"]).toBe("prod-cluster");
});
it("should depend on parent Bundle Component", () => {
const bd = createMockBundleDeployment();
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleDeploymentToResource)(bd, "prod-cluster", context);
expect(getSpec(entity).dependsOn).toContain("component:fleet-default/my-app-main");
expect(getSpec(entity).dependsOn).toContain("resource:fleet-default/prod-cluster");
});
it("should add techdocs entity annotation when parent System is known", () => {
const bd = createMockBundleDeployment();
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleDeploymentToResource)(bd, "prod-cluster", context, "system:fleet-default/my-app");
const annotations = entity.metadata.annotations;
expect(annotations["backstage.io/techdocs-entity"]).toBe("system:fleet-default/my-app");
});
it("should include cluster tag", () => {
const bd = createMockBundleDeployment();
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleDeploymentToResource)(bd, "prod-cluster", context);
expect(entity.metadata.tags).toContain("cluster-prod-cluster");
});
it("should honor friendly cluster names for dependsOn and tags", () => {
const bd = createMockBundleDeployment();
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleDeploymentToResource)(bd, "c-abc123", context, undefined, "staging-000");
expect(getSpec(entity).dependsOn).toContain("resource:fleet-default/staging-000");
expect(entity.metadata.tags).toContain("cluster-staging-000");
expect(entity.metadata.description).toContain("staging-000");
});
it("should truncate long messages", () => {
const longMessage = "x".repeat(600);
const bd = createMockBundleDeployment({
status: {
display: {
state: "Ready",
message: longMessage,
},
},
});
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleDeploymentToResource)(bd, "prod-cluster", context);
const annotations = entity.metadata.annotations;
expect(annotations["fleet.cattle.io/message"].length).toBe(500);
});
it("should shorten long names with a hash suffix", () => {
const longCluster = "very-long-cluster-name".repeat(4);
const bd = createMockBundleDeployment({
metadata: { name: "bundle", namespace: "fleet-default", labels: {} },
});
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleDeploymentToResource)(bd, longCluster, context);
expect(entity.metadata.name.length).toBeLessThanOrEqual(50);
expect(entity.metadata.name).toMatch(/-[a-f0-9]{6}$/);
const annotations = entity.metadata.annotations;
expect(annotations["fleet.cattle.io/original-name"]).toBe(`bundle-${longCluster}`);
});
it("should handle missing bundle name label", () => {
const bd = createMockBundleDeployment({
metadata: {
name: "orphan-deployment",
namespace: "cluster-fleet-default-prod",
labels: {},
},
});
const context = createMockContext();
const entity = (0, entityMapper_1.mapBundleDeploymentToResource)(bd, "prod", context);
expect(getSpec(entity).dependsOn).toContain("resource:fleet-default/prod");
});
});
// ============================================================================"
// mapClusterToResource Tests
// ============================================================================"
describe("mapClusterToResource", () => {
it("should create cluster Resource with kubernetes-id", () => {
const context = createMockContext();
const entity = (0, entityMapper_1.mapClusterToResource)("prod-cluster", "staging-000", "fleet-default", context);
expect(entity.kind).toBe("Resource");
expect(entity.metadata.name).toBe("staging-000");
expect(entity.metadata.namespace).toBe("fleet-default");
const annotations = entity.metadata.annotations;
expect(annotations["backstage.io/kubernetes-id"]).toBe("prod-cluster");
});
});
// ============================================================================
// mapApiDefinitionToApi Tests
// ============================================================================
describe("mapApiDefinitionToApi", () => {
it("should create an API entity", () => {
const apiDef = { name: "my-api", type: "openapi" };
const context = createMockContext({
fleetYaml: createMockFleetYaml(),
});
const entity = (0, entityMapper_1.mapApiDefinitionToApi)(apiDef, "my-app", context);
expect(entity.kind).toBe("API");
expect(entity.metadata.name).toBe("my-api");
});
it("should use default namespace from fleet.yaml", () => {
const apiDef = { name: "my-api" };
const context = createMockContext({
fleetYaml: createMockFleetYaml({ defaultNamespace: "custom-ns" }),
});
const entity = (0, entityMapper_1.mapApiDefinitionToApi)(apiDef, "my-app", context);
expect(entity.metadata.namespace).toBe("custom-ns");
});
it("should set API type from definition", () => {
const apiDef = { name: "my-api", type: "graphql" };
const context = createMockContext();
const entity = (0, entityMapper_1.mapApiDefinitionToApi)(apiDef, "my-app", context);
expect(getSpec(entity).type).toBe("graphql");
});
it("should use openapi as default type", () => {
const apiDef = { name: "my-api" };
const context = createMockContext();
const entity = (0, entityMapper_1.mapApiDefinitionToApi)(apiDef, "my-app", context);
expect(getSpec(entity).type).toBe("openapi");
});
it("should include definition when provided", () => {
const apiDef = {
name: "my-api",
definition: "openapi: 3.0.0\ninfo:\n title: Test",
};
const context = createMockContext();
const entity = (0, entityMapper_1.mapApiDefinitionToApi)(apiDef, "my-app", context);
expect(getSpec(entity).definition).toBe("openapi: 3.0.0\ninfo:\n title: Test");
});
it("should reference definitionUrl when no definition", () => {
const apiDef = {
name: "my-api",
definitionUrl: "https://example.com/api.yaml",
};
const context = createMockContext();
const entity = (0, entityMapper_1.mapApiDefinitionToApi)(apiDef, "my-app", context);
expect(getSpec(entity).definition).toContain("https://example.com/api.yaml");
});
it("should include source GitRepo annotation", () => {
const apiDef = { name: "my-api" };
const context = createMockContext();
const entity = (0, entityMapper_1.mapApiDefinitionToApi)(apiDef, "my-app", context);
const annotations = entity.metadata.annotations;
expect(annotations["fleet.cattle.io/source-gitrepo"]).toBe("my-app");
});
it("should use fleet.yaml owner", () => {
const apiDef = { name: "my-api" };
const context = createMockContext({
fleetYaml: createMockFleetYaml(),
});
const entity = (0, entityMapper_1.mapApiDefinitionToApi)(apiDef, "my-app", context);
expect(getSpec(entity).owner).toBe("team-platform");
});
it("should include description from API definition", () => {
const apiDef = { name: "my-api", description: "My custom API" };
const context = createMockContext();
const entity = (0, entityMapper_1.mapApiDefinitionToApi)(apiDef, "my-app", context);
expect(entity.metadata.description).toBe("My custom API");
});
});
// ============================================================================
// extractBundleMetadata Tests
// ============================================================================
describe("extractBundleMetadata", () => {
it("should extract metadata from bundle labels", () => {
const bundle = createMockBundle();
const metadata = (0, entityMapper_1.extractBundleMetadata)(bundle);
expect(metadata.gitRepoName).toBe("my-app");
expect(metadata.bundlePath).toBe(".");
expect(metadata.commitId).toBe("abc123");
});
it("should handle missing labels", () => {
const bundle = {
metadata: { name: "test", labels: {} },
};
const metadata = (0, entityMapper_1.extractBundleMetadata)(bundle);
expect(metadata.gitRepoName).toBeUndefined();
expect(metadata.bundlePath).toBeUndefined();
expect(metadata.commitId).toBeUndefined();
});
it("should handle missing metadata", () => {
const bundle = {};
const metadata = (0, entityMapper_1.extractBundleMetadata)(bundle);
expect(metadata.gitRepoName).toBeUndefined();
});
});
// ============================================================================
// createEmptyBatch and flattenBatch Tests
// ============================================================================
describe("createEmptyBatch", () => {
it("should create an empty batch", () => {
const batch = (0, entityMapper_1.createEmptyBatch)();
expect(batch.systems).toEqual([]);
expect(batch.components).toEqual([]);
expect(batch.resources).toEqual([]);
expect(batch.apis).toEqual([]);
});
});
describe("flattenBatch", () => {
it("should flatten all entity arrays", () => {
const batch = (0, entityMapper_1.createEmptyBatch)();
batch.systems.push({ kind: "System" });
batch.components.push({ kind: "Component" });
batch.resources.push({ kind: "Resource" });
batch.apis.push({ kind: "API" });
const entities = (0, entityMapper_1.flattenBatch)(batch);
expect(entities).toHaveLength(4);
expect(entities[0].kind).toBe("System");
expect(entities[1].kind).toBe("Component");
expect(entities[2].kind).toBe("Resource");
expect(entities[3].kind).toBe("API");
});
it("should return empty array for empty batch", () => {
const batch = (0, entityMapper_1.createEmptyBatch)();
const entities = (0, entityMapper_1.flattenBatch)(batch);
expect(entities).toEqual([]);
});
});