1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Remove use of mockFs in cluster store tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2023-01-04 14:05:22 -05:00
parent b4f02a7341
commit 4618c2feaf
6 changed files with 195 additions and 221 deletions

View File

@ -3,34 +3,33 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import fs from "fs";
import mockFs from "mock-fs";
import path from "path";
import fse from "fs-extra";
import type { ClusterStore } from "../cluster-store/cluster-store";
import { Console } from "console";
import { stdout, stderr } from "process";
import getCustomKubeConfigDirectoryInjectable from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
import type { GetCustomKubeConfigFilePath } from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
import getCustomKubeConfigFilePathInjectable from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
import clusterStoreInjectable from "../cluster-store/cluster-store.injectable";
import type { DiContainer } from "@ogre-tools/injectable";
import type { CreateCluster } from "../cluster/create-cluster-injection-token";
import { createClusterInjectionToken } from "../cluster/create-cluster-injection-token";
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable";
import assert from "assert";
import directoryForTempInjectable from "../app-paths/directory-for-temp/directory-for-temp.injectable";
import kubectlBinaryNameInjectable from "../../main/kubectl/binary-name.injectable";
import kubectlDownloadingNormalizedArchInjectable from "../../main/kubectl/normalized-arch.injectable";
import normalizedPlatformInjectable from "../vars/normalized-platform.injectable";
import fsInjectable from "../fs/fs.injectable";
import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable";
import type { WriteJsonSync } from "../fs/write-json-sync.injectable";
import writeJsonSyncInjectable from "../fs/write-json-sync.injectable";
import type { ReadFileSync } from "../fs/read-file-sync.injectable";
import readFileSyncInjectable from "../fs/read-file-sync.injectable";
import { readFileSync } from "fs";
import type { WriteFileSync } from "../fs/write-file-sync.injectable";
import writeFileSyncInjectable from "../fs/write-file-sync.injectable";
import type { WriteBufferSync } from "../fs/write-buffer-sync.injectable";
import writeBufferSyncInjectable from "../fs/write-buffer-sync.injectable";
console = new Console(stdout, stderr);
const testDataIcon = fs.readFileSync(
"test-data/cluster-store-migration-icon.png",
);
// NOTE: this is intended to read the actual file system
const testDataIcon = readFileSync("test-data/cluster-store-migration-icon.png");
const clusterServerUrl = "https://localhost";
const kubeconfig = `
apiVersion: v1
@ -56,75 +55,41 @@ users:
token: kubeconfig-user-q4lm4:xxxyyyy
`;
const embed = (directoryName: string, contents: any): string => {
fse.ensureDirSync(path.dirname(directoryName));
fse.writeFileSync(directoryName, contents, {
encoding: "utf-8",
mode: 0o600,
});
return directoryName;
};
jest.mock("electron", () => ({
ipcMain: {
handle: jest.fn(),
on: jest.fn(),
removeAllListeners: jest.fn(),
off: jest.fn(),
send: jest.fn(),
},
}));
describe("cluster-store", () => {
let mainDi: DiContainer;
let di: DiContainer;
let clusterStore: ClusterStore;
let createCluster: CreateCluster;
let writeJsonSync: WriteJsonSync;
let writeFileSync: WriteFileSync;
let writeBufferSync: WriteBufferSync;
let readFileSync: ReadFileSync;
let getCustomKubeConfigFilePath: GetCustomKubeConfigFilePath;
let writeFileSyncAndReturnPath: (filePath: string, contents: string) => string;
beforeEach(async () => {
mainDi = getDiForUnitTesting({ doGeneralOverrides: true });
di = getDiForUnitTesting({ doGeneralOverrides: true });
mockFs();
mainDi.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
mainDi.override(directoryForTempInjectable, () => "some-temp-directory");
mainDi.override(kubectlBinaryNameInjectable, () => "kubectl");
mainDi.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
mainDi.override(normalizedPlatformInjectable, () => "darwin");
mainDi.permitSideEffects(getConfigurationFileModelInjectable);
mainDi.unoverride(getConfigurationFileModelInjectable);
mainDi.permitSideEffects(fsInjectable);
});
afterEach(() => {
mockFs.restore();
di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data");
di.override(directoryForTempInjectable, () => "/some-temp-directory");
di.override(kubectlBinaryNameInjectable, () => "kubectl");
di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
di.override(normalizedPlatformInjectable, () => "darwin");
createCluster = di.inject(createClusterInjectionToken);
getCustomKubeConfigFilePath = di.inject(getCustomKubeConfigFilePathInjectable);
writeJsonSync = di.inject(writeJsonSyncInjectable);
writeFileSync = di.inject(writeFileSyncInjectable);
writeBufferSync = di.inject(writeBufferSyncInjectable);
readFileSync = di.inject(readFileSyncInjectable);
writeFileSyncAndReturnPath = (filePath, contents) => (writeFileSync(filePath, contents), filePath);
});
describe("empty config", () => {
let getCustomKubeConfigDirectory: (directoryName: string) => string;
beforeEach(async () => {
getCustomKubeConfigDirectory = mainDi.inject(getCustomKubeConfigDirectoryInjectable);
mockFs({
"some-directory-for-user-data": {
"lens-cluster-store.json": JSON.stringify({}),
},
});
createCluster = mainDi.inject(createClusterInjectionToken);
clusterStore = mainDi.inject(clusterStoreInjectable);
writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", {});
clusterStore = di.inject(clusterStoreInjectable);
clusterStore.load();
});
afterEach(() => {
mockFs.restore();
});
describe("with foo cluster added", () => {
beforeEach(() => {
const cluster = createCluster({
@ -135,8 +100,8 @@ describe("cluster-store", () => {
icon: "data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5",
clusterName: "minikube",
},
kubeConfigPath: embed(
getCustomKubeConfigDirectory("foo"),
kubeConfigPath: writeFileSyncAndReturnPath(
getCustomKubeConfigFilePath("foo"),
kubeconfig,
),
}, {
@ -169,8 +134,8 @@ describe("cluster-store", () => {
preferences: {
clusterName: "prod",
},
kubeConfigPath: embed(
getCustomKubeConfigDirectory("prod"),
kubeConfigPath: writeFileSyncAndReturnPath(
getCustomKubeConfigFilePath("prod"),
kubeconfig,
),
});
@ -180,8 +145,8 @@ describe("cluster-store", () => {
preferences: {
clusterName: "dev",
},
kubeConfigPath: embed(
getCustomKubeConfigDirectory("dev"),
kubeConfigPath: writeFileSyncAndReturnPath(
getCustomKubeConfigFilePath("dev"),
kubeconfig,
),
});
@ -193,61 +158,49 @@ describe("cluster-store", () => {
});
it("check if cluster's kubeconfig file saved", () => {
const file = embed(getCustomKubeConfigDirectory("boo"), "kubeconfig");
const file = writeFileSyncAndReturnPath(getCustomKubeConfigFilePath("boo"), "kubeconfig");
expect(fs.readFileSync(file, "utf8")).toBe("kubeconfig");
expect(readFileSync(file)).toBe("kubeconfig");
});
});
});
describe("config with existing clusters", () => {
beforeEach(() => {
mockFs({
"temp-kube-config": kubeconfig,
"some-directory-for-user-data": {
"lens-cluster-store.json": JSON.stringify({
__internal__: {
migrations: {
version: "99.99.99",
},
},
clusters: [
{
id: "cluster1",
kubeConfigPath: "./temp-kube-config",
contextName: "foo",
preferences: { terminalCWD: "/foo" },
workspace: "default",
},
{
id: "cluster2",
kubeConfigPath: "./temp-kube-config",
contextName: "foo2",
preferences: { terminalCWD: "/foo2" },
},
{
id: "cluster3",
kubeConfigPath: "./temp-kube-config",
contextName: "foo",
preferences: { terminalCWD: "/foo" },
workspace: "foo",
ownerRef: "foo",
},
],
}),
writeFileSync("/temp-kube-config", kubeconfig);
writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", {
__internal__: {
migrations: {
version: "99.99.99",
},
},
clusters: [
{
id: "cluster1",
kubeConfigPath: "/temp-kube-config",
contextName: "foo",
preferences: { terminalCWD: "/foo" },
workspace: "default",
},
{
id: "cluster2",
kubeConfigPath: "/temp-kube-config",
contextName: "foo2",
preferences: { terminalCWD: "/foo2" },
},
{
id: "cluster3",
kubeConfigPath: "/temp-kube-config",
contextName: "foo",
preferences: { terminalCWD: "/foo" },
workspace: "foo",
ownerRef: "foo",
},
],
});
createCluster = mainDi.inject(createClusterInjectionToken);
clusterStore = mainDi.inject(clusterStoreInjectable);
clusterStore = di.inject(clusterStoreInjectable);
clusterStore.load();
});
afterEach(() => {
mockFs.restore();
});
it("allows to retrieve a cluster", () => {
const storedCluster = clusterStore.getById("cluster1");
@ -271,66 +224,35 @@ describe("cluster-store", () => {
describe("config with invalid cluster kubeconfig", () => {
beforeEach(() => {
const invalidKubeconfig = `
apiVersion: v1
clusters:
- cluster:
server: https://localhost
name: test2
contexts:
- context:
cluster: test
user: test
name: test
current-context: test
kind: Config
preferences: {}
users:
- name: test
user:
token: kubeconfig-user-q4lm4:xxxyyyy
`;
mockFs({
"invalid-kube-config": invalidKubeconfig,
"valid-kube-config": kubeconfig,
"some-directory-for-user-data": {
"lens-cluster-store.json": JSON.stringify({
__internal__: {
migrations: {
version: "99.99.99",
},
},
clusters: [
{
id: "cluster1",
kubeConfigPath: "./invalid-kube-config",
contextName: "test",
preferences: { terminalCWD: "/foo" },
workspace: "foo",
},
{
id: "cluster2",
kubeConfigPath: "./valid-kube-config",
contextName: "foo",
preferences: { terminalCWD: "/foo" },
workspace: "default",
},
],
}),
writeFileSync("/invalid-kube-config", invalidKubeconfig);
writeFileSync("/valid-kube-config", kubeconfig);
writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", {
__internal__: {
migrations: {
version: "99.99.99",
},
},
clusters: [
{
id: "cluster1",
kubeConfigPath: "/invalid-kube-config",
contextName: "test",
preferences: { terminalCWD: "/foo" },
workspace: "foo",
},
{
id: "cluster2",
kubeConfigPath: "/valid-kube-config",
contextName: "foo",
preferences: { terminalCWD: "/foo" },
workspace: "default",
},
],
});
createCluster = mainDi.inject(createClusterInjectionToken);
clusterStore = mainDi.inject(clusterStoreInjectable);
clusterStore = di.inject(clusterStoreInjectable);
clusterStore.load();
});
afterEach(() => {
mockFs.restore();
});
it("does not enable clusters with invalid kubeconfig", () => {
const storedClusters = clusterStore.clustersList;
@ -340,56 +262,69 @@ users:
describe("pre 3.6.0-beta.1 config with an existing cluster", () => {
beforeEach(() => {
mockFs({
"some-directory-for-user-data": {
"lens-cluster-store.json": JSON.stringify({
__internal__: {
migrations: {
version: "3.5.0",
},
},
clusters: [
{
id: "cluster1",
kubeConfig: minimalValidKubeConfig,
contextName: "cluster",
preferences: {
icon: "store://icon_path",
},
},
],
}),
icon_path: testDataIcon,
writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", {
__internal__: {
migrations: {
version: "3.5.0",
},
},
clusters: [
{
id: "cluster1",
kubeConfig: minimalValidKubeConfig,
contextName: "cluster",
preferences: {
icon: "store://icon_path",
},
},
],
});
writeBufferSync("/some-directory-for-user-data/icon_path", testDataIcon);
mainDi.override(storeMigrationVersionInjectable, () => "3.6.0");
di.override(storeMigrationVersionInjectable, () => "3.6.0");
createCluster = mainDi.inject(createClusterInjectionToken);
clusterStore = mainDi.inject(clusterStoreInjectable);
clusterStore = di.inject(clusterStoreInjectable);
clusterStore.load();
});
afterEach(() => {
mockFs.restore();
});
it("migrates to modern format with kubeconfig in a file", async () => {
const config = clusterStore.clustersList[0].kubeConfigPath;
expect(fs.readFileSync(config, "utf8")).toBe(minimalValidKubeConfig);
expect(readFileSync(config)).toBe(minimalValidKubeConfig);
});
it("migrates to modern format with icon not in file", async () => {
const { icon } = clusterStore.clustersList[0].preferences;
assert(icon);
expect(icon.startsWith("data:;base64,")).toBe(true);
expect(clusterStore.clustersList[0].preferences.icon).toMatch(/data:;base64,/);
});
});
});
const invalidKubeconfig = JSON.stringify({
apiVersion: "v1",
clusters: [{
cluster: {
server: "https://localhost",
},
name: "test2",
}],
contexts: [{
context: {
cluster: "test",
user: "test",
},
name: "test",
}],
"current-context": "test",
kind: "Config",
preferences: {},
users: [{
user: {
token: "kubeconfig-user-q4lm4:xxxyyyy",
},
name: "test",
}],
});
const minimalValidKubeConfig = JSON.stringify({
apiVersion: "v1",
clusters: [

View File

@ -6,15 +6,17 @@ import { getInjectable } from "@ogre-tools/injectable";
import directoryForKubeConfigsInjectable from "../directory-for-kube-configs/directory-for-kube-configs.injectable";
import joinPathsInjectable from "../../path/join-paths.injectable";
const getCustomKubeConfigDirectoryInjectable = getInjectable({
export type GetCustomKubeConfigFilePath = (fileName: string) => string;
const getCustomKubeConfigFilePathInjectable = getInjectable({
id: "get-custom-kube-config-directory",
instantiate: (di) => {
instantiate: (di): GetCustomKubeConfigFilePath => {
const directoryForKubeConfigs = di.inject(directoryForKubeConfigsInjectable);
const joinPaths = di.inject(joinPathsInjectable);
return (directoryName: string) => joinPaths(directoryForKubeConfigs, directoryName);
return (fileName) => joinPaths(directoryForKubeConfigs, fileName);
},
});
export default getCustomKubeConfigDirectoryInjectable;
export default getCustomKubeConfigFilePathInjectable;

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import getDirnameOfPathInjectable from "../path/get-dirname.injectable";
import fsInjectable from "./fs.injectable";
export type WriteBufferSync = (filePath: string, contents: Buffer) => void;
const writeBufferSyncInjectable = getInjectable({
id: "write-buffer-sync",
instantiate: (di): WriteBufferSync => {
const {
writeFileSync,
ensureDirSync,
} = di.inject(fsInjectable);
const getDirnameOfPath = di.inject(getDirnameOfPathInjectable);
return (filePath, contents) => {
ensureDirSync(getDirnameOfPath(filePath), {
mode: 0o755,
});
writeFileSync(filePath, contents);
};
},
});
export default writeBufferSyncInjectable;

View File

@ -7,7 +7,7 @@
// convert file path cluster icons to their base64 encoded versions
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
import getCustomKubeConfigDirectoryInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
import getCustomKubeConfigFilePathInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
import type { ClusterModel } from "../../../common/cluster-types";
import readFileSyncInjectable from "../../../common/fs/read-file-sync.injectable";
import { loadConfigFromString } from "../../../common/kube-helpers";
@ -27,7 +27,7 @@ const v360Beta1ClusterStoreMigrationInjectable = getInjectable({
id: "v3.6.0-beta.1-cluster-store-migration",
instantiate: (di) => {
const userDataPath = di.inject(directoryForUserDataInjectable);
const getCustomKubeConfigDirectory = di.inject(getCustomKubeConfigDirectoryInjectable);
const getCustomKubeConfigDirectory = di.inject(getCustomKubeConfigFilePathInjectable);
const readFileSync = di.inject(readFileSyncInjectable);
const readFileBufferSync = di.inject(readFileBufferSyncInjectable);
const joinPaths = di.inject(joinPathsInjectable);
@ -44,8 +44,8 @@ const v360Beta1ClusterStoreMigrationInjectable = getInjectable({
for (const clusterModel of storedClusters) {
/**
* migrate kubeconfig
*/
* migrate kubeconfig
*/
try {
const absPath = getCustomKubeConfigDirectory(clusterModel.id);
@ -80,6 +80,7 @@ const v360Beta1ClusterStoreMigrationInjectable = getInjectable({
delete clusterModel.preferences?.icon;
}
} catch (error) {
console.log(error);
logger.info(`Failed to migrate cluster icon for cluster "${clusterModel.id}"`, error);
delete clusterModel.preferences?.icon;
}

View File

@ -49,10 +49,17 @@ const v500Beta10ClusterStoreMigrationInjectable = getInjectable({
store.set("clusters", clusters);
} catch (error) {
if (isErrnoException(error) && !(error.code === "ENOENT" && error.path?.endsWith("lens-workspace-store.json"))) {
// ignore lens-workspace-store.json being missing
throw error;
// KLUDGE: remove after https://github.com/streamich/memfs/pull/893 is released
if (process.env.JEST_WORKER_ID && (error as any).code === "ENOENT") {
return;
}
if (isErrnoException(error) && error.code === "ENOENT" && error.path?.endsWith("lens-workspace-store.json")) {
// ignore lens-workspace-store.json being missing
return;
}
throw error;
}
},
};

View File

@ -20,7 +20,7 @@ import type { ShowNotification } from "../notifications";
import { SettingLayout } from "../layout/setting-layout";
import { MonacoEditor } from "../monaco-editor";
import { withInjectables } from "@ogre-tools/injectable-react";
import getCustomKubeConfigDirectoryInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
import getCustomKubeConfigFilePathInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
import type { NavigateToCatalog } from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
import navigateToCatalogInjectable from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
import type { EmitAppEvent } from "../../../common/app-event-bus/emit-event.injectable";
@ -163,7 +163,7 @@ class NonInjectedAddCluster extends React.Component<Dependencies> {
export const AddCluster = withInjectables<Dependencies>(NonInjectedAddCluster, {
getProps: (di) => ({
getCustomKubeConfigDirectory: di.inject(getCustomKubeConfigDirectoryInjectable),
getCustomKubeConfigDirectory: di.inject(getCustomKubeConfigFilePathInjectable),
navigateToCatalog: di.inject(navigateToCatalogInjectable),
getDirnameOfPath: di.inject(getDirnameOfPathInjectable),
emitAppEvent: di.inject(emitAppEventInjectable),