Cascade different refactorings to eliminate global shared state from app paths

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
Janne SavolainenandMikko Aspiala committed 2022-01-05 13:26:49 +02:00
1 parent 908a40975e
commit 9d33fff906
336 files changed
+9105 -3307

No files matched your search

+13 -17
View File
@@ -20,29 +20,18 @@
*/
import mockFs from "mock-fs";
import { AppPaths } from "../app-paths";
import { BaseStore } from "../base-store";
import { action, comparer, makeObservable, observable, toJS } from "mobx";
import { readFileSync } from "fs";
import { getDisForUnitTesting } from "../../test-utils/get-dis-for-unit-testing";
AppPaths.init();
import directoryForUserDataInjectable
from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
jest.mock("electron", () => ({
app: {
getVersion: () => "99.99.99",
getName: () => "lens",
setName: jest.fn(),
setPath: jest.fn(),
getPath: () => "tmp",
getLocale: () => "en",
setLoginItemSettings: jest.fn(),
},
ipcMain: {
handle: jest.fn(),
on: jest.fn(),
removeAllListeners: jest.fn(),
off: jest.fn(),
send: jest.fn(),
},
}));
@@ -105,10 +94,17 @@ describe("BaseStore", () => {
let store: TestStore;
beforeEach(async () => {
const dis = getDisForUnitTesting({ doGeneralOverrides: true });
dis.mainDi.override(directoryForUserDataInjectable, () => "some-user-data-directory");
await dis.runSetups();
store = undefined;
TestStore.resetInstance();
const mockOpts = {
"tmp": {
"some-user-data-directory": {
"test-store.json": JSON.stringify({}),
},
};
@@ -130,7 +126,7 @@ describe("BaseStore", () => {
a: "foo", b: "bar", c: "hello",
});
const data = JSON.parse(readFileSync("tmp/test-store.json").toString());
const data = JSON.parse(readFileSync("some-user-data-directory/test-store.json").toString());
expect(data).toEqual({ a: "foo", b: "bar", c: "hello" });
});
@@ -153,7 +149,7 @@ describe("BaseStore", () => {
expect(fileSpy).toHaveBeenCalledTimes(2);
const data = JSON.parse(readFileSync("tmp/test-store.json").toString());
const data = JSON.parse(readFileSync("some-user-data-directory/test-store.json").toString());
expect(data).toEqual({ a: "a", b: "b", c: "" });
});
+484 -428
View File
@@ -24,17 +24,29 @@ import mockFs from "mock-fs";
import yaml from "js-yaml";
import path from "path";
import fse from "fs-extra";
import { Cluster } from "../../main/cluster";
import { ClusterStore } from "../cluster-store";
import type { Cluster } from "../cluster/cluster";
import { ClusterStore } from "../cluster-store/cluster-store";
import { Console } from "console";
import { stdout, stderr } from "process";
import type { ClusterId } from "../cluster-types";
import { getCustomKubeConfigPath } from "../utils";
import { AppPaths } from "../app-paths";
import getCustomKubeConfigDirectoryInjectable from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
import clusterStoreInjectable from "../cluster-store/cluster-store.injectable";
import type { ClusterModel } from "../cluster-types";
import type {
DependencyInjectionContainer,
} from "@ogre-tools/injectable";
import { getDisForUnitTesting } from "../../test-utils/get-dis-for-unit-testing";
import { createClusterInjectionToken } from "../cluster/create-cluster-injection-token";
import directoryForUserDataInjectable
from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
console = new Console(stdout, stderr);
const testDataIcon = fs.readFileSync("test-data/cluster-store-migration-icon.png");
const testDataIcon = fs.readFileSync(
"test-data/cluster-store-migration-icon.png",
);
const kubeconfig = `
apiVersion: v1
clusters:
@@ -59,25 +71,17 @@ users:
token: kubeconfig-user-q4lm4:xxxyyyy
`;
function embed(clusterId: ClusterId, contents: any): string {
const absPath = getCustomKubeConfigPath(clusterId);
const embed = (directoryName: string, contents: any): string => {
fse.ensureDirSync(path.dirname(directoryName));
fse.writeFileSync(directoryName, contents, {
encoding: "utf-8",
mode: 0o600,
});
fse.ensureDirSync(path.dirname(absPath));
fse.writeFileSync(absPath, contents, { encoding: "utf-8", mode: 0o600 });
return absPath;
}
return directoryName;
};
jest.mock("electron", () => ({
app: {
getVersion: () => "99.99.99",
getName: () => "lens",
setName: jest.fn(),
setPath: jest.fn(),
getPath: () => "tmp",
getLocale: () => "en",
setLoginItemSettings: jest.fn(),
},
ipcMain: {
handle: jest.fn(),
on: jest.fn(),
@@ -87,157 +91,194 @@ jest.mock("electron", () => ({
},
}));
AppPaths.init();
describe("cluster-store", () => {
let mainDi: DependencyInjectionContainer;
let clusterStore: ClusterStore;
let createCluster: (model: ClusterModel) => Cluster;
describe("empty config", () => {
beforeEach(async () => {
ClusterStore.getInstance(false)?.unregisterIpcListener();
ClusterStore.resetInstance();
const mockOpts = {
"tmp": {
"lens-cluster-store.json": JSON.stringify({}),
},
};
const dis = getDisForUnitTesting({ doGeneralOverrides: true });
mockFs(mockOpts);
mockFs();
ClusterStore.createInstance();
mainDi = dis.mainDi;
mainDi.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
await dis.runSetups();
createCluster = mainDi.inject(createClusterInjectionToken);
});
afterEach(() => {
mockFs.restore();
});
describe("empty config", () => {
let getCustomKubeConfigDirectory: (directoryName: string) => string;
describe("with foo cluster added", () => {
beforeEach(() => {
ClusterStore.getInstance().addCluster(
new Cluster({
beforeEach(async () => {
getCustomKubeConfigDirectory = mainDi.inject(
getCustomKubeConfigDirectoryInjectable,
);
// TODO: Remove these by removing Singleton base-class from BaseStore
ClusterStore.getInstance(false)?.unregisterIpcListener();
ClusterStore.resetInstance();
const mockOpts = {
"some-directory-for-user-data": {
"lens-cluster-store.json": JSON.stringify({}),
},
};
mockFs(mockOpts);
clusterStore = mainDi.inject(clusterStoreInjectable);
});
afterEach(() => {
mockFs.restore();
});
describe("with foo cluster added", () => {
beforeEach(() => {
const cluster = createCluster({
id: "foo",
contextName: "foo",
preferences: {
terminalCWD: "/tmp",
terminalCWD: "/some-directory-for-user-data",
icon: "data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5",
clusterName: "minikube",
},
kubeConfigPath: embed("foo", kubeconfig),
}),
);
});
kubeConfigPath: embed(
getCustomKubeConfigDirectory("foo"),
kubeconfig,
),
});
it("adds new cluster to store", async () => {
const storedCluster = ClusterStore.getInstance().getById("foo");
expect(storedCluster.id).toBe("foo");
expect(storedCluster.preferences.terminalCWD).toBe("/tmp");
expect(storedCluster.preferences.icon).toBe("data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5");
});
});
describe("with prod and dev clusters added", () => {
beforeEach(() => {
const store = ClusterStore.getInstance();
store.addCluster({
id: "prod",
contextName: "foo",
preferences: {
clusterName: "prod",
},
kubeConfigPath: embed("prod", kubeconfig),
clusterStore.addCluster(cluster);
});
store.addCluster({
id: "dev",
contextName: "foo2",
preferences: {
clusterName: "dev",
},
kubeConfigPath: embed("dev", kubeconfig),
it("adds new cluster to store", async () => {
const storedCluster = clusterStore.getById("foo");
expect(storedCluster.id).toBe("foo");
expect(storedCluster.preferences.terminalCWD).toBe("/some-directory-for-user-data");
expect(storedCluster.preferences.icon).toBe(
"data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5",
);
});
});
it("check if store can contain multiple clusters", () => {
expect(ClusterStore.getInstance().hasClusters()).toBeTruthy();
expect(ClusterStore.getInstance().clusters.size).toBe(2);
});
describe("with prod and dev clusters added", () => {
beforeEach(() => {
const store = clusterStore;
it("check if cluster's kubeconfig file saved", () => {
const file = embed("boo", "kubeconfig");
expect(fs.readFileSync(file, "utf8")).toBe("kubeconfig");
});
});
});
describe("config with existing clusters", () => {
beforeEach(() => {
ClusterStore.resetInstance();
const mockOpts = {
"temp-kube-config": kubeconfig,
"tmp": {
"lens-cluster-store.json": JSON.stringify({
__internal__: {
migrations: {
version: "99.99.99",
},
store.addCluster({
id: "prod",
contextName: "foo",
preferences: {
clusterName: "prod",
},
clusters: [
{
id: "cluster1",
kubeConfigPath: "./temp-kube-config",
contextName: "foo",
preferences: { terminalCWD: "/foo" },
workspace: "default",
kubeConfigPath: embed(
getCustomKubeConfigDirectory("prod"),
kubeconfig,
),
});
store.addCluster({
id: "dev",
contextName: "foo2",
preferences: {
clusterName: "dev",
},
kubeConfigPath: embed(
getCustomKubeConfigDirectory("dev"),
kubeconfig,
),
});
});
it("check if store can contain multiple clusters", () => {
expect(clusterStore.hasClusters()).toBeTruthy();
expect(clusterStore.clusters.size).toBe(2);
});
it("check if cluster's kubeconfig file saved", () => {
const file = embed(getCustomKubeConfigDirectory("boo"), "kubeconfig");
expect(fs.readFileSync(file, "utf8")).toBe("kubeconfig");
});
});
});
describe("config with existing clusters", () => {
beforeEach(() => {
ClusterStore.resetInstance();
const mockOpts = {
"temp-kube-config": kubeconfig,
"some-directory-for-user-data": {
"lens-cluster-store.json": JSON.stringify({
__internal__: {
migrations: {
version: "99.99.99",
},
},
{
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",
},
],
}),
},
};
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",
},
],
}),
},
};
mockFs(mockOpts);
mockFs(mockOpts);
return ClusterStore.createInstance();
clusterStore = mainDi.inject(clusterStoreInjectable);
});
afterEach(() => {
mockFs.restore();
});
it("allows to retrieve a cluster", () => {
const storedCluster = clusterStore.getById("cluster1");
expect(storedCluster.id).toBe("cluster1");
expect(storedCluster.preferences.terminalCWD).toBe("/foo");
});
it("allows getting all of the clusters", async () => {
const storedClusters = clusterStore.clustersList;
expect(storedClusters.length).toBe(3);
expect(storedClusters[0].id).toBe("cluster1");
expect(storedClusters[0].preferences.terminalCWD).toBe("/foo");
expect(storedClusters[1].id).toBe("cluster2");
expect(storedClusters[1].preferences.terminalCWD).toBe("/foo2");
expect(storedClusters[2].id).toBe("cluster3");
});
});
afterEach(() => {
mockFs.restore();
});
it("allows to retrieve a cluster", () => {
const storedCluster = ClusterStore.getInstance().getById("cluster1");
expect(storedCluster.id).toBe("cluster1");
expect(storedCluster.preferences.terminalCWD).toBe("/foo");
});
it("allows getting all of the clusters", async () => {
const storedClusters = ClusterStore.getInstance().clustersList;
expect(storedClusters.length).toBe(3);
expect(storedClusters[0].id).toBe("cluster1");
expect(storedClusters[0].preferences.terminalCWD).toBe("/foo");
expect(storedClusters[1].id).toBe("cluster2");
expect(storedClusters[1].preferences.terminalCWD).toBe("/foo2");
expect(storedClusters[2].id).toBe("cluster3");
});
});
describe("config with invalid cluster kubeconfig", () => {
beforeEach(() => {
const invalidKubeconfig = `
describe("config with invalid cluster kubeconfig", () => {
beforeEach(() => {
const invalidKubeconfig = `
apiVersion: v1
clusters:
- cluster:
@@ -257,302 +298,317 @@ users:
token: kubeconfig-user-q4lm4:xxxyyyy
`;
ClusterStore.resetInstance();
const mockOpts = {
"invalid-kube-config": invalidKubeconfig,
"valid-kube-config": kubeconfig,
"tmp": {
"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",
},
ClusterStore.resetInstance();
],
}),
},
};
const mockOpts = {
"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",
},
],
}),
},
};
mockFs(mockOpts);
mockFs(mockOpts);
return ClusterStore.createInstance();
clusterStore = mainDi.inject(clusterStoreInjectable);
});
afterEach(() => {
mockFs.restore();
});
it("does not enable clusters with invalid kubeconfig", () => {
const storedClusters = clusterStore.clustersList;
expect(storedClusters.length).toBe(1);
});
});
afterEach(() => {
mockFs.restore();
describe("pre 2.0 config with an existing cluster", () => {
beforeEach(() => {
ClusterStore.resetInstance();
const mockOpts = {
"some-directory-for-user-data": {
"lens-cluster-store.json": JSON.stringify({
__internal__: {
migrations: {
version: "1.0.0",
},
},
cluster1: minimalValidKubeConfig,
}),
},
};
mockFs(mockOpts);
clusterStore = mainDi.inject(clusterStoreInjectable);
});
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")).toContain(`"contexts":[`);
});
});
it("does not enable clusters with invalid kubeconfig", () => {
const storedClusters = ClusterStore.getInstance().clustersList;
describe("pre 2.6.0 config with a cluster that has arrays in auth config", () => {
beforeEach(() => {
ClusterStore.resetInstance();
const mockOpts = {
"some-directory-for-user-data": {
"lens-cluster-store.json": JSON.stringify({
__internal__: {
migrations: {
version: "2.4.1",
},
},
cluster1: {
kubeConfig: JSON.stringify({
apiVersion: "v1",
clusters: [
{
cluster: {
server: "https://10.211.55.6:8443",
},
name: "minikube",
},
],
contexts: [
{
context: {
cluster: "minikube",
user: "minikube",
name: "minikube",
},
name: "minikube",
},
],
"current-context": "minikube",
kind: "Config",
preferences: {},
users: [
{
name: "minikube",
user: {
"client-certificate": "/Users/foo/.minikube/client.crt",
"client-key": "/Users/foo/.minikube/client.key",
"auth-provider": {
config: {
"access-token": ["should be string"],
expiry: ["should be string"],
},
},
},
},
],
}),
},
}),
},
};
expect(storedClusters.length).toBe(1);
mockFs(mockOpts);
clusterStore = mainDi.inject(clusterStoreInjectable);
});
afterEach(() => {
mockFs.restore();
});
it("replaces array format access token and expiry into string", async () => {
const file = clusterStore.clustersList[0].kubeConfigPath;
const config = fs.readFileSync(file, "utf8");
const kc = yaml.load(config) as Record<string, any>;
expect(kc.users[0].user["auth-provider"].config["access-token"]).toBe(
"should be string",
);
expect(kc.users[0].user["auth-provider"].config["expiry"]).toBe(
"should be string",
);
});
});
describe("pre 2.6.0 config with a cluster icon", () => {
beforeEach(() => {
ClusterStore.resetInstance();
const mockOpts = {
"some-directory-for-user-data": {
"lens-cluster-store.json": JSON.stringify({
__internal__: {
migrations: {
version: "2.4.1",
},
},
cluster1: {
kubeConfig: minimalValidKubeConfig,
icon: "icon_path",
preferences: {
terminalCWD: "/some-directory-for-user-data",
},
},
}),
icon_path: testDataIcon,
},
};
mockFs(mockOpts);
clusterStore = mainDi.inject(clusterStoreInjectable);
});
afterEach(() => {
mockFs.restore();
});
it("moves the icon into preferences", async () => {
const storedClusterData = clusterStore.clustersList[0];
expect(storedClusterData.hasOwnProperty("icon")).toBe(false);
expect(storedClusterData.preferences.hasOwnProperty("icon")).toBe(true);
expect(
storedClusterData.preferences.icon.startsWith("data:;base64,"),
).toBe(true);
});
});
describe("for a pre 2.7.0-beta.0 config without a workspace", () => {
beforeEach(() => {
ClusterStore.resetInstance();
const mockOpts = {
"some-directory-for-user-data": {
"lens-cluster-store.json": JSON.stringify({
__internal__: {
migrations: {
version: "2.6.6",
},
},
cluster1: {
kubeConfig: minimalValidKubeConfig,
preferences: {
terminalCWD: "/some-directory-for-user-data",
},
},
}),
},
};
mockFs(mockOpts);
clusterStore = mainDi.inject(clusterStoreInjectable);
});
afterEach(() => {
mockFs.restore();
});
});
describe("pre 3.6.0-beta.1 config with an existing cluster", () => {
beforeEach(() => {
ClusterStore.resetInstance();
const mockOpts = {
"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,
},
};
mockFs(mockOpts);
clusterStore = mainDi.inject(clusterStoreInjectable);
});
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);
});
it("migrates to modern format with icon not in file", async () => {
const { icon } = clusterStore.clustersList[0].preferences;
expect(icon.startsWith("data:;base64,")).toBe(true);
});
});
});
const minimalValidKubeConfig = JSON.stringify({
apiVersion: "v1",
clusters: [{
name: "minikube",
cluster: {
server: "https://192.168.64.3:8443",
clusters: [
{
name: "minikube",
cluster: {
server: "https://192.168.64.3:8443",
},
},
}],
],
"current-context": "minikube",
contexts: [{
context: {
cluster: "minikube",
user: "minikube",
contexts: [
{
context: {
cluster: "minikube",
user: "minikube",
},
name: "minikube",
},
name: "minikube",
}],
users: [{
name: "minikube",
user: {
"client-certificate": "/Users/foo/.minikube/client.crt",
"client-key": "/Users/foo/.minikube/client.key",
],
users: [
{
name: "minikube",
user: {
"client-certificate": "/Users/foo/.minikube/client.crt",
"client-key": "/Users/foo/.minikube/client.key",
},
},
}],
],
kind: "Config",
preferences: {},
});
describe("pre 2.0 config with an existing cluster", () => {
beforeEach(() => {
ClusterStore.resetInstance();
const mockOpts = {
"tmp": {
"lens-cluster-store.json": JSON.stringify({
__internal__: {
migrations: {
version: "1.0.0",
},
},
cluster1: minimalValidKubeConfig,
}),
},
};
mockFs(mockOpts);
return ClusterStore.createInstance();
});
afterEach(() => {
mockFs.restore();
});
it("migrates to modern format with kubeconfig in a file", async () => {
const config = ClusterStore.getInstance().clustersList[0].kubeConfigPath;
expect(fs.readFileSync(config, "utf8")).toContain(`"contexts":[`);
});
});
describe("pre 2.6.0 config with a cluster that has arrays in auth config", () => {
beforeEach(() => {
ClusterStore.resetInstance();
const mockOpts = {
"tmp": {
"lens-cluster-store.json": JSON.stringify({
__internal__: {
migrations: {
version: "2.4.1",
},
},
cluster1: {
kubeConfig: JSON.stringify({
apiVersion: "v1",
clusters: [{
cluster: {
server: "https://10.211.55.6:8443",
},
name: "minikube",
}],
contexts: [{
context: {
cluster: "minikube",
user: "minikube",
name: "minikube",
},
name: "minikube",
}],
"current-context": "minikube",
kind: "Config",
preferences: {},
users: [{
name: "minikube",
user: {
"client-certificate": "/Users/foo/.minikube/client.crt",
"client-key": "/Users/foo/.minikube/client.key",
"auth-provider": {
config: {
"access-token": [
"should be string",
],
expiry: [
"should be string",
],
},
},
},
}],
}),
},
}),
},
};
mockFs(mockOpts);
return ClusterStore.createInstance();
});
afterEach(() => {
mockFs.restore();
});
it("replaces array format access token and expiry into string", async () => {
const file = ClusterStore.getInstance().clustersList[0].kubeConfigPath;
const config = fs.readFileSync(file, "utf8");
const kc = yaml.load(config) as Record<string, any>;
expect(kc.users[0].user["auth-provider"].config["access-token"]).toBe("should be string");
expect(kc.users[0].user["auth-provider"].config["expiry"]).toBe("should be string");
});
});
describe("pre 2.6.0 config with a cluster icon", () => {
beforeEach(() => {
ClusterStore.resetInstance();
const mockOpts = {
"tmp": {
"lens-cluster-store.json": JSON.stringify({
__internal__: {
migrations: {
version: "2.4.1",
},
},
cluster1: {
kubeConfig: minimalValidKubeConfig,
icon: "icon_path",
preferences: {
terminalCWD: "/tmp",
},
},
}),
"icon_path": testDataIcon,
},
};
mockFs(mockOpts);
return ClusterStore.createInstance();
});
afterEach(() => {
mockFs.restore();
});
it("moves the icon into preferences", async () => {
const storedClusterData = ClusterStore.getInstance().clustersList[0];
expect(storedClusterData.hasOwnProperty("icon")).toBe(false);
expect(storedClusterData.preferences.hasOwnProperty("icon")).toBe(true);
expect(storedClusterData.preferences.icon.startsWith("data:;base64,")).toBe(true);
});
});
describe("for a pre 2.7.0-beta.0 config without a workspace", () => {
beforeEach(() => {
ClusterStore.resetInstance();
const mockOpts = {
"tmp": {
"lens-cluster-store.json": JSON.stringify({
__internal__: {
migrations: {
version: "2.6.6",
},
},
cluster1: {
kubeConfig: minimalValidKubeConfig,
preferences: {
terminalCWD: "/tmp",
},
},
}),
},
};
mockFs(mockOpts);
return ClusterStore.createInstance();
});
afterEach(() => {
mockFs.restore();
});
});
describe("pre 3.6.0-beta.1 config with an existing cluster", () => {
beforeEach(() => {
ClusterStore.resetInstance();
const mockOpts = {
"tmp": {
"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,
},
};
mockFs(mockOpts);
return ClusterStore.createInstance();
});
afterEach(() => {
mockFs.restore();
});
it("migrates to modern format with kubeconfig in a file", async () => {
const config = ClusterStore.getInstance().clustersList[0].kubeConfigPath;
expect(fs.readFileSync(config, "utf8")).toBe(minimalValidKubeConfig);
});
it("migrates to modern format with icon not in file", async () => {
const { icon } = ClusterStore.getInstance().clustersList[0].preferences;
expect(icon.startsWith("data:;base64,")).toBe(true);
});
});
+1 -1
View File
@@ -19,7 +19,7 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { appEventBus, AppEvent } from "../event-bus";
import { appEventBus, AppEvent } from "../app-event-bus/event-bus";
import { Console } from "console";
import { stdout, stderr } from "process";
+13 -25
View File
@@ -23,10 +23,11 @@ import { anyObject } from "jest-mock-extended";
import { merge } from "lodash";
import mockFs from "mock-fs";
import logger from "../../main/logger";
import { AppPaths } from "../app-paths";
import type { CatalogEntity, CatalogEntityData, CatalogEntityKindData } from "../catalog";
import { ClusterStore } from "../cluster-store";
import { HotbarStore } from "../hotbar-store";
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
import directoryForUserDataInjectable
from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
jest.mock("../../main/catalog/catalog-entity-registry", () => ({
catalogEntityRegistry: {
@@ -109,37 +110,24 @@ const awsCluster = getMockCatalogEntity({
},
});
jest.mock("electron", () => ({
app: {
getVersion: () => "99.99.99",
getName: () => "lens",
setName: jest.fn(),
setPath: jest.fn(),
getPath: () => "tmp",
getLocale: () => "en",
setLoginItemSettings: jest.fn(),
},
ipcMain: {
on: jest.fn(),
handle: jest.fn(),
},
}));
AppPaths.init();
describe("HotbarStore", () => {
beforeEach(() => {
beforeEach(async () => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
await di.runSetups();
mockFs({
"tmp": {
"some-directory-for-user-data": {
"lens-hotbar-store.json": JSON.stringify({}),
},
});
ClusterStore.createInstance();
HotbarStore.createInstance();
});
afterEach(() => {
ClusterStore.resetInstance();
HotbarStore.resetInstance();
mockFs.restore();
});
@@ -339,7 +327,7 @@ describe("HotbarStore", () => {
beforeEach(() => {
HotbarStore.resetInstance();
const mockOpts = {
"tmp": {
"some-directory-for-user-data": {
"lens-hotbar-store.json": JSON.stringify({
__internal__: {
migrations: {
-107
View File
@@ -1,107 +0,0 @@
/**
* Copyright (c) 2021 OpenLens Authors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { SearchStore } from "../search-store";
import { Console } from "console";
import { stdout, stderr } from "process";
jest.mock("electron", () => ({
app: {
getPath: () => "/foo",
},
}));
console = new Console(stdout, stderr);
let searchStore: SearchStore = null;
const logs = [
"1:M 30 Oct 2020 16:17:41.553 # Connection with replica 172.17.0.12:6379 lost",
"1:M 30 Oct 2020 16:17:41.623 * Replica 172.17.0.12:6379 asks for synchronization",
"1:M 30 Oct 2020 16:17:41.623 * Starting Partial resynchronization request from 172.17.0.12:6379 accepted. Sending 0 bytes of backlog starting from offset 14407.",
];
describe("search store tests", () => {
beforeEach(async () => {
searchStore = new SearchStore();
});
it("does nothing with empty search query", () => {
searchStore.onSearch([], "");
expect(searchStore.occurrences).toEqual([]);
});
it("doesn't break if no text provided", () => {
searchStore.onSearch(null, "replica");
expect(searchStore.occurrences).toEqual([]);
searchStore.onSearch([], "replica");
expect(searchStore.occurrences).toEqual([]);
});
it("find 3 occurrences across 3 lines", () => {
searchStore.onSearch(logs, "172");
expect(searchStore.occurrences).toEqual([0, 1, 2]);
});
it("find occurrences within 1 line (case-insensitive)", () => {
searchStore.onSearch(logs, "Starting");
expect(searchStore.occurrences).toEqual([2, 2]);
});
it("sets overlay index equal to first occurrence", () => {
searchStore.onSearch(logs, "Replica");
expect(searchStore.activeOverlayIndex).toBe(0);
});
it("set overlay index to next occurrence", () => {
searchStore.onSearch(logs, "172");
searchStore.setNextOverlayActive();
expect(searchStore.activeOverlayIndex).toBe(1);
});
it("sets overlay to last occurrence", () => {
searchStore.onSearch(logs, "172");
searchStore.setPrevOverlayActive();
expect(searchStore.activeOverlayIndex).toBe(2);
});
it("gets line index where overlay is located", () => {
searchStore.onSearch(logs, "synchronization");
expect(searchStore.activeOverlayLine).toBe(1);
});
it("escapes string for using in regex", () => {
const regex = SearchStore.escapeRegex("some.interesting-query\\#?()[]");
expect(regex).toBe("some\\.interesting\\-query\\\\\\#\\?\\(\\)\\[\\]");
});
it("gets active find number", () => {
searchStore.onSearch(logs, "172");
searchStore.setNextOverlayActive();
expect(searchStore.activeFind).toBe(2);
});
it("gets total finds number", () => {
searchStore.onSearch(logs, "Starting");
expect(searchStore.totalFinds).toBe(2);
});
});
+45 -36
View File
@@ -43,18 +43,39 @@ import { SemVer } from "semver";
import electron from "electron";
import { stdout, stderr } from "process";
import { ThemeStore } from "../../renderer/theme.store";
import type { ClusterStoreModel } from "../cluster-store";
import { AppPaths } from "../app-paths";
import type { ClusterStoreModel } from "../cluster-store/cluster-store";
import { getDisForUnitTesting } from "../../test-utils/get-dis-for-unit-testing";
import userStoreInjectable from "../user-store/user-store.injectable";
import type { DependencyInjectionContainer } from "@ogre-tools/injectable";
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
console = new Console(stdout, stderr);
AppPaths.init();
describe("user store tests", () => {
let userStore: UserStore;
let mainDi: DependencyInjectionContainer;
beforeEach(async () => {
const dis = getDisForUnitTesting({ doGeneralOverrides: true });
mockFs();
mainDi = dis.mainDi;
mainDi.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
await dis.runSetups();
});
afterEach(() => {
mockFs.restore();
});
describe("for an empty config", () => {
beforeEach(() => {
mockFs({ tmp: { "config.json": "{}", "kube_config": "{}" }});
mockFs({ "some-directory-for-user-data": { "config.json": "{}", "kube_config": "{}" }});
(UserStore.createInstance() as any).refreshNewContexts = jest.fn(() => Promise.resolve());
userStore = mainDi.inject(userStoreInjectable);
});
afterEach(() => {
@@ -63,46 +84,38 @@ describe("user store tests", () => {
});
it("allows setting and retrieving lastSeenAppVersion", () => {
const us = UserStore.getInstance();
us.lastSeenAppVersion = "1.2.3";
expect(us.lastSeenAppVersion).toBe("1.2.3");
userStore.lastSeenAppVersion = "1.2.3";
expect(userStore.lastSeenAppVersion).toBe("1.2.3");
});
it("allows setting and getting preferences", () => {
const us = UserStore.getInstance();
userStore.httpsProxy = "abcd://defg";
us.httpsProxy = "abcd://defg";
expect(userStore.httpsProxy).toBe("abcd://defg");
expect(userStore.colorTheme).toBe(ThemeStore.defaultTheme);
expect(us.httpsProxy).toBe("abcd://defg");
expect(us.colorTheme).toBe(ThemeStore.defaultTheme);
us.colorTheme = "light";
expect(us.colorTheme).toBe("light");
userStore.colorTheme = "light";
expect(userStore.colorTheme).toBe("light");
});
it("correctly resets theme to default value", async () => {
const us = UserStore.getInstance();
us.colorTheme = "some other theme";
us.resetTheme();
expect(us.colorTheme).toBe(ThemeStore.defaultTheme);
userStore.colorTheme = "some other theme";
userStore.resetTheme();
expect(userStore.colorTheme).toBe(ThemeStore.defaultTheme);
});
it("correctly calculates if the last seen version is an old release", () => {
const us = UserStore.getInstance();
expect(userStore.isNewVersion).toBe(true);
expect(us.isNewVersion).toBe(true);
us.lastSeenAppVersion = (new SemVer(electron.app.getVersion())).inc("major").format();
expect(us.isNewVersion).toBe(false);
userStore.lastSeenAppVersion = (new SemVer(electron.app.getVersion())).inc("major").format();
expect(userStore.isNewVersion).toBe(false);
});
});
describe("migrations", () => {
beforeEach(() => {
mockFs({
"tmp": {
"some-directory-for-user-data": {
"config.json": JSON.stringify({
user: { username: "foobar" },
preferences: { colorTheme: "light" },
@@ -112,7 +125,7 @@ describe("user store tests", () => {
clusters: [
{
id: "foobar",
kubeConfigPath: "tmp/extension_data/foo/bar",
kubeConfigPath: "some-directory-for-user-data/extension_data/foo/bar",
},
{
id: "barfoo",
@@ -129,7 +142,7 @@ describe("user store tests", () => {
},
});
UserStore.createInstance();
userStore = mainDi.inject(userStoreInjectable);
});
afterEach(() => {
@@ -138,16 +151,12 @@ describe("user store tests", () => {
});
it("sets last seen app version to 0.0.0", () => {
const us = UserStore.getInstance();
expect(us.lastSeenAppVersion).toBe("0.0.0");
expect(userStore.lastSeenAppVersion).toBe("0.0.0");
});
it.only("skips clusters for adding to kube-sync with files under extension_data/", () => {
const us = UserStore.getInstance();
expect(us.syncKubeconfigEntries.has("tmp/extension_data/foo/bar")).toBe(false);
expect(us.syncKubeconfigEntries.has("some/other/path")).toBe(true);
expect(userStore.syncKubeconfigEntries.has("some-directory-for-user-data/extension_data/foo/bar")).toBe(false);
expect(userStore.syncKubeconfigEntries.has("some/other/path")).toBe(true);
});
});
});