From 618432550767e35ce5d3ae47c4620aeeb87fb98a Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Thu, 9 Feb 2023 10:45:42 +0300 Subject: [PATCH] Add tests for getNamespaceTree() function Signed-off-by: Alex Andreev --- .../common/k8s-api/endpoints/namespace.api.ts | 2 +- .../+namespaces/namespace-store.test.ts | 205 ++++++++++++++++++ 2 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/renderer/components/+namespaces/namespace-store.test.ts diff --git a/packages/core/src/common/k8s-api/endpoints/namespace.api.ts b/packages/core/src/common/k8s-api/endpoints/namespace.api.ts index c27a233d45..04f4501313 100644 --- a/packages/core/src/common/k8s-api/endpoints/namespace.api.ts +++ b/packages/core/src/common/k8s-api/endpoints/namespace.api.ts @@ -39,7 +39,7 @@ export class Namespace extends KubeObject< } isChildOf(parentName: string) { - this.getLabels().find(label => label === `${parentName}.tree.hnc.x-k8s.io/depth=1`); + return this.getLabels().find(label => label === `${parentName}.tree.hnc.x-k8s.io/depth=1`); } isControlledByHNC() { diff --git a/packages/core/src/renderer/components/+namespaces/namespace-store.test.ts b/packages/core/src/renderer/components/+namespaces/namespace-store.test.ts new file mode 100644 index 0000000000..81793eec75 --- /dev/null +++ b/packages/core/src/renderer/components/+namespaces/namespace-store.test.ts @@ -0,0 +1,205 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { DiContainer } from "@ogre-tools/injectable"; +import { observable } from "mobx"; +import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; +import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import { Namespace } from "../../../common/k8s-api/endpoints"; +import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable"; +import createClusterInjectable from "../../cluster/create-cluster.injectable"; +import { getDiForUnitTesting } from "../../getDiForUnitTesting"; +import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; +import type { NamespaceStore } from "./store"; +import namespaceStoreInjectable from "./store.injectable"; + +function createNamespace(name: string, labels?: Record, annotations?: Record): Namespace { + return new Namespace({ + apiVersion: "v1", + kind: "Namespace", + metadata: { + name, + resourceVersion: "1", + selfLink: `/api/v1/namespaces/${name}`, + uid: `${name}`, + labels: { + ...labels, + }, + annotations: { + ...annotations, + }, + }, + }); +} + +const singleRoot = createNamespace("single-root", { + "hnc.x-k8s.io/included-namespace": "true", +}); + +const acmeGroup = createNamespace("acme-org", { + "hnc.x-k8s.io/included-namespace": "true", +}); + +const orgA = createNamespace("org-a", { + "hnc.x-k8s.io/included-namespace": "true", +}); + +const teamA = createNamespace("team-a", { + "hnc.x-k8s.io/included-namespace": "true", + "acme-org.tree.hnc.x-k8s.io/depth": "1", + "kubernetes.io/metadata.name": "team-a", + "team-a.tree.hnc.x-k8s.io/depth": "0", +}); + +const teamB = createNamespace("team-b", { + "hnc.x-k8s.io/included-namespace": "true", + "acme-org.tree.hnc.x-k8s.io/depth": "1", + "kubernetes.io/metadata.name": "team-b", + "team-b.tree.hnc.x-k8s.io/depth": "0", +}); + +const teamC = createNamespace("team-c", { + "hnc.x-k8s.io/included-namespace": "true", + "org-a.tree.hnc.x-k8s.io/depth": "1", + "kubernetes.io/metadata.name": "team-c", + "team-c.tree.hnc.x-k8s.io/depth": "0", +}); + +const service1 = createNamespace("service-1", { + "hnc.x-k8s.io/included-namespace": "true", + "org-a.tree.hnc.x-k8s.io/depth": "1", + "kubernetes.io/metadata.name": "team-c", + "service-1.tree.hnc.x-k8s.io/depth": "0", +}, { + "hnc.x-k8s.io/subnamespace-of": "org-a", +}); + +const levelsDeep = createNamespace("levels-deep", { + "hnc.x-k8s.io/included-namespace": "true", +}); + +const levelDeepChildA = createNamespace("level-deep-child-a", { + "hnc.x-k8s.io/included-namespace": "true", + "levels-deep.tree.hnc.x-k8s.io/depth": "1", + "level-deep-child-a.tree.hnc.x-k8s.io/depth": "0", +}); + +const levelDeepChildB = createNamespace("level-deep-child-b", { + "hnc.x-k8s.io/included-namespace": "true", + "levels-deep.tree.hnc.x-k8s.io/depth": "1", + "level-deep-child-b.tree.hnc.x-k8s.io/depth": "0", +}); + +const levelDeepSubChildA = createNamespace("level-deep-subchild-a", { + "hnc.x-k8s.io/included-namespace": "true", + "levels-deep.tree.hnc.x-k8s.io/depth": "2", + "level-deep-child-b.tree.hnc.x-k8s.io/depth": "1", + "level-deep-subchild-a.tree.hnc.x-k8s.io/depth": "0", +}); + + +describe("NamespaceStore", () => { + let di: DiContainer; + let namespaceStore: NamespaceStore; + + beforeEach(async () => { + di = getDiForUnitTesting({ doGeneralOverrides: true }); + + di.override(directoryForUserDataInjectable, () => "/some-user-store-path"); + di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); + di.override(storesAndApisCanBeCreatedInjectable, () => true); + + const createCluster = di.inject(createClusterInjectable); + + di.override(hostedClusterInjectable, () => createCluster({ + contextName: "some-context-name", + id: "some-cluster-id", + kubeConfigPath: "/some-path-to-a-kubeconfig", + }, { + clusterServerUrl: "https://localhost:8080", + })); + + namespaceStore = di.inject(namespaceStoreInjectable); + + namespaceStore.items = observable.array([ + acmeGroup, + orgA, + teamA, + teamB, + teamC, + service1, + levelsDeep, + levelDeepChildA, + levelDeepChildB, + levelDeepSubChildA, + ]); + }); + + it("returns tree for single node", () => { + const tree = namespaceStore.getNamespaceTree(service1); + + expect(tree).toEqual({ + id: "service-1", + namespace: service1, + children: [], + }); + }); + + it("returns tree for namespace not listed in store", () => { + const tree = namespaceStore.getNamespaceTree(singleRoot); + + expect(tree).toEqual({ + id: "single-root", + namespace: singleRoot, + children: [], + }); + }); + + it("return tree for namespace with children", () => { + const tree = namespaceStore.getNamespaceTree(acmeGroup); + + expect(tree).toEqual({ + id: "acme-org", + namespace: acmeGroup, + children: [ + { + id: "team-a", + namespace: teamA, + children: [], + }, + { + id: "team-b", + namespace: teamB, + children: [], + }, + ], + }); + }); + + it("return tree for namespace with deep nested children", () => { + const tree = namespaceStore.getNamespaceTree(levelsDeep); + + expect(tree).toEqual({ + id: "levels-deep", + namespace: levelsDeep, + children: [ + { + id: "level-deep-child-a", + namespace: levelDeepChildA, + children: [], + }, + { + id: "level-deep-child-b", + namespace: levelDeepChildB, + children: [{ + id: "level-deep-subchild-a", + namespace: levelDeepSubChildA, + children: [], + }], + }, + ], + }); + }); +});