UNPKG

@gorizond/catalog-backend-module-fleet

Version:

Backstage catalog backend module for Rancher Fleet GitOps entities

810 lines (809 loc) 38 kB
"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([]); }); });