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

Overhaul FS fakes with full in-memory filesystem

- This increases our confidence in fs related logic

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-12-02 15:41:45 -05:00
parent 6cc89bf154
commit 8d55a3a08d
24 changed files with 159 additions and 116 deletions

View File

@ -1,11 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getGlobalOverride } from "../test-utils/get-global-override";
import deleteFileInjectable from "./delete-file.injectable";
export default getGlobalOverride(deleteFileInjectable, () => async () => {
throw new Error("tried to delete file without override");
});

View File

@ -3,11 +3,49 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import type { ReadOptions } from "fs-extra";
import fse from "fs-extra"; import fse from "fs-extra";
/**
* NOTE: Add corrisponding a corrisponding override of this injecable in `src/test-utils/override-fs-with-fakes.ts`
*/
const fsInjectable = getInjectable({ const fsInjectable = getInjectable({
id: "fs", id: "fs",
instantiate: () => fse, instantiate: () => {
const {
promises: {
readFile,
writeFile,
readdir,
lstat,
rm,
},
readFileSync,
readJson,
writeJson,
readJsonSync,
writeFileSync,
writeJsonSync,
pathExistsSync,
pathExists,
} = fse;
return {
readFile,
readJson: readJson as (file: string, options?: ReadOptions | BufferEncoding) => Promise<any>,
writeFile,
writeJson,
pathExists,
readdir,
readFileSync,
readJsonSync,
writeFileSync,
writeJsonSync,
pathExistsSync,
lstat,
rm,
};
},
causesSideEffects: true, causesSideEffects: true,
}); });

View File

@ -5,11 +5,9 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import fsInjectable from "./fs.injectable"; import fsInjectable from "./fs.injectable";
export type DeleteFile = (filePath: string) => Promise<void>; const pathExistsSyncInjectable = getInjectable({
id: "path-exists-sync",
const deleteFileInjectable = getInjectable({ instantiate: (di) => di.inject(fsInjectable).pathExistsSync,
id: "delete-file",
instantiate: (di): DeleteFile => di.inject(fsInjectable).unlink,
}); });
export default deleteFileInjectable; export default pathExistsSyncInjectable;

View File

@ -14,18 +14,16 @@ export interface ReadDirectory {
( (
path: string, path: string,
options?: options?:
| { encoding: BufferEncoding | string | null; withFileTypes?: false | undefined } | { encoding: BufferEncoding; withFileTypes?: false | undefined }
| BufferEncoding | BufferEncoding
| string
| null,
): Promise<string[]>; ): Promise<string[]>;
( (
path: string, path: string,
options?: { encoding?: BufferEncoding | string | null | undefined; withFileTypes?: false | undefined }, options?: { encoding?: BufferEncoding; withFileTypes?: false | undefined },
): Promise<string[] | Buffer[]>; ): Promise<string[] | Buffer[]>;
( (
path: string, path: string,
options: { encoding?: BufferEncoding | string | null | undefined; withFileTypes: true }, options: { encoding?: BufferEncoding; withFileTypes: true },
): Promise<Dirent[]>; ): Promise<Dirent[]>;
} }

View File

@ -4,8 +4,8 @@
*/ */
import { getGlobalOverride } from "../test-utils/get-global-override"; import { getGlobalOverride } from "../test-utils/get-global-override";
import removePathInjectable from "./remove-path.injectable"; import removePathInjectable from "./remove.injectable";
export default getGlobalOverride(removePathInjectable, () => async () => { export default getGlobalOverride(removePathInjectable, () => async () => {
throw new Error("tried to remove a path without override"); throw new Error("tried to remove path without override");
}); });

View File

@ -5,11 +5,11 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import fsInjectable from "./fs.injectable"; import fsInjectable from "./fs.injectable";
export type RemovePath = (path: string) => Promise<void>; export type RemovePath = (filePath: string) => Promise<void>;
const removePathInjectable = getInjectable({ const removePathInjectable = getInjectable({
id: "remove-path", id: "remove-path",
instantiate: (di): RemovePath => di.inject(fsInjectable).remove, instantiate: (di): RemovePath => di.inject(fsInjectable).rm,
}); });
export default removePathInjectable; export default removePathInjectable;

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 WriteFileSync = (filePath: string, contents: string) => void;
const writeFileSyncInjectable = getInjectable({
id: "write-file-sync",
instantiate: (di): WriteFileSync => {
const {
writeFileSync,
ensureDirSync,
} = di.inject(fsInjectable);
const getDirnameOfPath = di.inject(getDirnameOfPathInjectable);
return (filePath, contents) => {
ensureDirSync(getDirnameOfPath(filePath), {
mode: 0o755,
});
writeFileSync(filePath, contents);
};
},
});
export default writeFileSyncInjectable;

View File

@ -27,7 +27,7 @@ import getBasenameOfPathInjectable from "../../common/path/get-basename.injectab
import getDirnameOfPathInjectable from "../../common/path/get-dirname.injectable"; import getDirnameOfPathInjectable from "../../common/path/get-dirname.injectable";
import getRelativePathInjectable from "../../common/path/get-relative-path.injectable"; import getRelativePathInjectable from "../../common/path/get-relative-path.injectable";
import joinPathsInjectable from "../../common/path/join-paths.injectable"; import joinPathsInjectable from "../../common/path/join-paths.injectable";
import removePathInjectable from "../../common/fs/remove-path.injectable"; import removePathInjectable from "../../common/fs/remove.injectable";
import homeDirectoryPathInjectable from "../../common/os/home-directory-path.injectable"; import homeDirectoryPathInjectable from "../../common/os/home-directory-path.injectable";
const extensionDiscoveryInjectable = getInjectable({ const extensionDiscoveryInjectable = getInjectable({

View File

@ -15,7 +15,7 @@ import readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
import pathExistsInjectable from "../../common/fs/path-exists.injectable"; import pathExistsInjectable from "../../common/fs/path-exists.injectable";
import watchInjectable from "../../common/fs/watch/watch.injectable"; import watchInjectable from "../../common/fs/watch/watch.injectable";
import extensionApiVersionInjectable from "../../common/vars/extension-api-version.injectable"; import extensionApiVersionInjectable from "../../common/vars/extension-api-version.injectable";
import removePathInjectable from "../../common/fs/remove-path.injectable"; import removePathInjectable from "../../common/fs/remove.injectable";
import type { JoinPaths } from "../../common/path/join-paths.injectable"; import type { JoinPaths } from "../../common/path/join-paths.injectable";
import joinPathsInjectable from "../../common/path/join-paths.injectable"; import joinPathsInjectable from "../../common/path/join-paths.injectable";
import homeDirectoryPathInjectable from "../../common/os/home-directory-path.injectable"; import homeDirectoryPathInjectable from "../../common/os/home-directory-path.injectable";

View File

@ -30,7 +30,7 @@ import type { JoinPaths } from "../../common/path/join-paths.injectable";
import type { GetBasenameOfPath } from "../../common/path/get-basename.injectable"; import type { GetBasenameOfPath } from "../../common/path/get-basename.injectable";
import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable"; import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable";
import type { GetRelativePath } from "../../common/path/get-relative-path.injectable"; import type { GetRelativePath } from "../../common/path/get-relative-path.injectable";
import type { RemovePath } from "../../common/fs/remove-path.injectable"; import type { RemovePath } from "../../common/fs/remove.injectable";
import type TypedEventEmitter from "typed-emitter"; import type TypedEventEmitter from "typed-emitter";
interface Dependencies { interface Dependencies {

View File

@ -6,7 +6,7 @@ import emitAppEventInjectable from "../../../../common/app-event-bus/emit-event.
import clusterFramesInjectable from "../../../../common/cluster-frames.injectable"; import clusterFramesInjectable from "../../../../common/cluster-frames.injectable";
import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable"; import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
import directoryForLensLocalStorageInjectable from "../../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable"; import directoryForLensLocalStorageInjectable from "../../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable";
import deleteFileInjectable from "../../../../common/fs/delete-file.injectable"; import removePathInjectable from "../../../../common/fs/remove.injectable";
import joinPathsInjectable from "../../../../common/path/join-paths.injectable"; import joinPathsInjectable from "../../../../common/path/join-paths.injectable";
import { noop } from "../../../../common/utils"; import { noop } from "../../../../common/utils";
import { getRequestChannelListenerInjectable } from "../../../../main/utils/channel/channel-listeners/listener-tokens"; import { getRequestChannelListenerInjectable } from "../../../../main/utils/channel/channel-listeners/listener-tokens";
@ -20,7 +20,7 @@ const deleteClusterChannelListenerInjectable = getRequestChannelListenerInjectab
const clusterFrames = di.inject(clusterFramesInjectable); const clusterFrames = di.inject(clusterFramesInjectable);
const joinPaths = di.inject(joinPathsInjectable); const joinPaths = di.inject(joinPathsInjectable);
const directoryForLensLocalStorage = di.inject(directoryForLensLocalStorageInjectable); const directoryForLensLocalStorage = di.inject(directoryForLensLocalStorageInjectable);
const deleteFile = di.inject(deleteFileInjectable); const deleteFile = di.inject(removePathInjectable);
return async (clusterId) => { return async (clusterId) => {
emitAppEvent({ name: "cluster", action: "remove" }); emitAppEvent({ name: "cluster", action: "remove" });

View File

@ -25,8 +25,8 @@ import type { WriteFile } from "../../common/fs/write-file.injectable";
import writeFileInjectable from "../../common/fs/write-file.injectable"; import writeFileInjectable from "../../common/fs/write-file.injectable";
import type { PathExists } from "../../common/fs/path-exists.injectable"; import type { PathExists } from "../../common/fs/path-exists.injectable";
import pathExistsInjectable from "../../common/fs/path-exists.injectable"; import pathExistsInjectable from "../../common/fs/path-exists.injectable";
import type { DeleteFile } from "../../common/fs/delete-file.injectable"; import type { RemovePath } from "../../common/fs/remove.injectable";
import deleteFileInjectable from "../../common/fs/delete-file.injectable"; import removePathInjectable from "../../common/fs/remove.injectable";
const clusterServerUrl = "https://192.168.64.3:8443"; const clusterServerUrl = "https://192.168.64.3:8443";
@ -36,7 +36,7 @@ describe("kubeconfig manager tests", () => {
let di: DiContainer; let di: DiContainer;
let loggerMock: jest.Mocked<Logger>; let loggerMock: jest.Mocked<Logger>;
let readFileMock: AsyncFnMock<ReadFile>; let readFileMock: AsyncFnMock<ReadFile>;
let deleteFileMock: AsyncFnMock<DeleteFile>; let deleteFileMock: AsyncFnMock<RemovePath>;
let writeFileMock: AsyncFnMock<WriteFile>; let writeFileMock: AsyncFnMock<WriteFile>;
let pathExistsMock: AsyncFnMock<PathExists>; let pathExistsMock: AsyncFnMock<PathExists>;
let kubeConfManager: KubeconfigManager; let kubeConfManager: KubeconfigManager;
@ -58,7 +58,7 @@ describe("kubeconfig manager tests", () => {
pathExistsMock = asyncFn(); pathExistsMock = asyncFn();
di.override(pathExistsInjectable, () => pathExistsMock); di.override(pathExistsInjectable, () => pathExistsMock);
deleteFileMock = asyncFn(); deleteFileMock = asyncFn();
di.override(deleteFileInjectable, () => deleteFileMock); di.override(removePathInjectable, () => deleteFileMock);
loggerMock = { loggerMock = {
warn: jest.fn(), warn: jest.fn(),

View File

@ -6,7 +6,6 @@
// Move embedded kubeconfig into separate file and add reference to it to cluster settings // Move embedded kubeconfig into separate file and add reference to it to cluster settings
// convert file path cluster icons to their base64 encoded versions // convert file path cluster icons to their base64 encoded versions
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 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 getCustomKubeConfigDirectoryInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
import type { ClusterModel } from "../../../common/cluster-types"; import type { ClusterModel } from "../../../common/cluster-types";
@ -20,24 +19,20 @@ interface Pre360ClusterModel extends ClusterModel {
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { clusterStoreMigrationInjectionToken } from "../../../common/cluster-store/migration-token"; import { clusterStoreMigrationInjectionToken } from "../../../common/cluster-store/migration-token";
import fsInjectable from "../../../common/fs/fs.injectable";
import readFileBufferSyncInjectable from "../../../common/fs/read-file-buffer-sync.injectable"; import readFileBufferSyncInjectable from "../../../common/fs/read-file-buffer-sync.injectable";
import loggerInjectable from "../../../common/logger.injectable"; import loggerInjectable from "../../../common/logger.injectable";
import writeFileSyncInjectable from "../../../common/fs/write-file-sync.injectable";
const v360Beta1ClusterStoreMigrationInjectable = getInjectable({ const v360Beta1ClusterStoreMigrationInjectable = getInjectable({
id: "v3.6.0-beta.1-cluster-store-migration", id: "v3.6.0-beta.1-cluster-store-migration",
instantiate: (di) => { instantiate: (di) => {
const userDataPath = di.inject(directoryForUserDataInjectable); const userDataPath = di.inject(directoryForUserDataInjectable);
const kubeConfigsPath = di.inject(directoryForKubeConfigsInjectable);
const getCustomKubeConfigDirectory = di.inject(getCustomKubeConfigDirectoryInjectable); const getCustomKubeConfigDirectory = di.inject(getCustomKubeConfigDirectoryInjectable);
const readFileSync = di.inject(readFileSyncInjectable); const readFileSync = di.inject(readFileSyncInjectable);
const readFileBufferSync = di.inject(readFileBufferSyncInjectable); const readFileBufferSync = di.inject(readFileBufferSyncInjectable);
const joinPaths = di.inject(joinPathsInjectable); const joinPaths = di.inject(joinPathsInjectable);
const logger = di.inject(loggerInjectable); const logger = di.inject(loggerInjectable);
const { const writeFileSync = di.inject(writeFileSyncInjectable);
ensureDirSync,
writeFileSync,
} = di.inject(fsInjectable);
return { return {
version: "3.6.0-beta.1", version: "3.6.0-beta.1",
@ -45,8 +40,6 @@ const v360Beta1ClusterStoreMigrationInjectable = getInjectable({
const storedClusters = (store.get("clusters") ?? []) as Pre360ClusterModel[]; const storedClusters = (store.get("clusters") ?? []) as Pre360ClusterModel[];
const migratedClusters: ClusterModel[] = []; const migratedClusters: ClusterModel[] = [];
ensureDirSync(kubeConfigsPath);
logger.info("Number of clusters to migrate: ", storedClusters.length); logger.info("Number of clusters to migrate: ", storedClusters.length);
for (const clusterModel of storedClusters) { for (const clusterModel of storedClusters) {
@ -61,7 +54,7 @@ const v360Beta1ClusterStoreMigrationInjectable = getInjectable({
} }
// take the embedded kubeconfig and dump it into a file // take the embedded kubeconfig and dump it into a file
writeFileSync(absPath, clusterModel.kubeConfig, { encoding: "utf-8", mode: 0o600 }); writeFileSync(absPath, clusterModel.kubeConfig);
clusterModel.kubeConfigPath = absPath; clusterModel.kubeConfigPath = absPath;
clusterModel.contextName = loadConfigFromString(readFileSync(absPath)).config.getCurrentContext(); clusterModel.contextName = loadConfigFromString(readFileSync(absPath)).config.getCurrentContext();

View File

@ -10,8 +10,8 @@ import applicationInformationInjectable from "../../../common/vars/application-i
import { clusterStoreMigrationInjectionToken } from "../../../common/cluster-store/migration-token"; import { clusterStoreMigrationInjectionToken } from "../../../common/cluster-store/migration-token";
import loggerInjectable from "../../../common/logger.injectable"; import loggerInjectable from "../../../common/logger.injectable";
import isSnapPackageInjectable from "../../../common/vars/is-snap-package.injectable"; import isSnapPackageInjectable from "../../../common/vars/is-snap-package.injectable";
import fsInjectable from "../../../common/fs/fs.injectable";
import type { ClusterModel } from "../../../common/cluster-types"; import type { ClusterModel } from "../../../common/cluster-types";
import pathExistsSyncInjectable from "../../../common/fs/path-exists-sync.injectable";
const clusterStoreSnapMigrationInjectable = getInjectable({ const clusterStoreSnapMigrationInjectable = getInjectable({
id: "cluster-store-snap-migration", id: "cluster-store-snap-migration",
@ -19,7 +19,7 @@ const clusterStoreSnapMigrationInjectable = getInjectable({
const { version } = di.inject(applicationInformationInjectable); const { version } = di.inject(applicationInformationInjectable);
const logger = di.inject(loggerInjectable); const logger = di.inject(loggerInjectable);
const isSnapPackage = di.inject(isSnapPackageInjectable); const isSnapPackage = di.inject(isSnapPackageInjectable);
const { existsSync } = di.inject(fsInjectable); const pathExistsSync = di.inject(pathExistsSyncInjectable);
return { return {
version, // Run always after upgrade version, // Run always after upgrade
@ -39,7 +39,7 @@ const clusterStoreSnapMigrationInjectable = getInjectable({
/** /**
* replace snap version with 'current' in kubeconfig path * replace snap version with 'current' in kubeconfig path
*/ */
if (!existsSync(cluster.kubeConfigPath)) { if (!pathExistsSync(cluster.kubeConfigPath)) {
const kubeconfigPath = cluster.kubeConfigPath.replace(/\/snap\/kontena-lens\/[0-9]*\//, "/snap/kontena-lens/current/"); const kubeconfigPath = cluster.kubeConfigPath.replace(/\/snap\/kontena-lens\/[0-9]*\//, "/snap/kontena-lens/current/");
cluster.kubeConfigPath = kubeconfigPath; cluster.kubeConfigPath = kubeconfigPath;

View File

@ -8,7 +8,7 @@ import loggerInjectable from "../../../common/logger.injectable";
import tempy from "tempy"; import tempy from "tempy";
import getHelmReleaseInjectable from "./get-helm-release.injectable"; import getHelmReleaseInjectable from "./get-helm-release.injectable";
import writeFileInjectable from "../../../common/fs/write-file.injectable"; import writeFileInjectable from "../../../common/fs/write-file.injectable";
import removePathInjectable from "../../../common/fs/remove-path.injectable"; import removePathInjectable from "../../../common/fs/remove.injectable";
import execHelmInjectable from "../exec-helm/exec-helm.injectable"; import execHelmInjectable from "../exec-helm/exec-helm.injectable";
export interface UpdateChartArgs { export interface UpdateChartArgs {

View File

@ -11,9 +11,9 @@ import loggerInjectable from "../../common/logger.injectable";
import lensProxyPortInjectable from "../lens-proxy/lens-proxy-port.injectable"; import lensProxyPortInjectable from "../lens-proxy/lens-proxy-port.injectable";
import joinPathsInjectable from "../../common/path/join-paths.injectable"; import joinPathsInjectable from "../../common/path/join-paths.injectable";
import getDirnameOfPathInjectable from "../../common/path/get-dirname.injectable"; import getDirnameOfPathInjectable from "../../common/path/get-dirname.injectable";
import deleteFileInjectable from "../../common/fs/delete-file.injectable";
import pathExistsInjectable from "../../common/fs/path-exists.injectable"; import pathExistsInjectable from "../../common/fs/path-exists.injectable";
import writeFileInjectable from "../../common/fs/write-file.injectable"; import writeFileInjectable from "../../common/fs/write-file.injectable";
import removePathInjectable from "../../common/fs/remove.injectable";
export interface KubeConfigManagerInstantiationParameter { export interface KubeConfigManagerInstantiationParameter {
cluster: Cluster; cluster: Cluster;
@ -31,7 +31,7 @@ const createKubeconfigManagerInjectable = getInjectable({
lensProxyPort: di.inject(lensProxyPortInjectable), lensProxyPort: di.inject(lensProxyPortInjectable),
joinPaths: di.inject(joinPathsInjectable), joinPaths: di.inject(joinPathsInjectable),
getDirnameOfPath: di.inject(getDirnameOfPathInjectable), getDirnameOfPath: di.inject(getDirnameOfPathInjectable),
deleteFile: di.inject(deleteFileInjectable), removePath: di.inject(removePathInjectable),
pathExists: di.inject(pathExistsInjectable), pathExists: di.inject(pathExistsInjectable),
writeFile: di.inject(writeFileInjectable), writeFile: di.inject(writeFileInjectable),
}; };

View File

@ -13,7 +13,7 @@ import type { Logger } from "../../common/logger";
import type { JoinPaths } from "../../common/path/join-paths.injectable"; import type { JoinPaths } from "../../common/path/join-paths.injectable";
import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable"; import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable";
import type { PathExists } from "../../common/fs/path-exists.injectable"; import type { PathExists } from "../../common/fs/path-exists.injectable";
import type { DeleteFile } from "../../common/fs/delete-file.injectable"; import type { RemovePath } from "../../common/fs/remove.injectable";
import type { WriteFile } from "../../common/fs/write-file.injectable"; import type { WriteFile } from "../../common/fs/write-file.injectable";
export interface KubeconfigManagerDependencies { export interface KubeconfigManagerDependencies {
@ -23,7 +23,7 @@ export interface KubeconfigManagerDependencies {
joinPaths: JoinPaths; joinPaths: JoinPaths;
getDirnameOfPath: GetDirnameOfPath; getDirnameOfPath: GetDirnameOfPath;
pathExists: PathExists; pathExists: PathExists;
deleteFile: DeleteFile; removePath: RemovePath;
writeFile: WriteFile; writeFile: WriteFile;
} }
@ -65,7 +65,7 @@ export class KubeconfigManager {
this.dependencies.logger.info(`[KUBECONFIG-MANAGER]: Deleting temporary kubeconfig: ${this.tempFilePath}`); this.dependencies.logger.info(`[KUBECONFIG-MANAGER]: Deleting temporary kubeconfig: ${this.tempFilePath}`);
try { try {
await this.dependencies.deleteFile(this.tempFilePath); await this.dependencies.removePath(this.tempFilePath);
} catch (error) { } catch (error) {
if (isErrnoException(error) && error.code !== "ENOENT") { if (isErrnoException(error) && error.code !== "ENOENT") {
throw error; throw error;

View File

@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import emitAppEventInjectable from "../../common/app-event-bus/emit-event.injectable"; import emitAppEventInjectable from "../../common/app-event-bus/emit-event.injectable";
import type { Cluster } from "../../common/cluster/cluster"; import type { Cluster } from "../../common/cluster/cluster";
import deleteFileInjectable from "../../common/fs/delete-file.injectable"; import removePathInjectable from "../../common/fs/remove.injectable";
import execFileInjectable from "../../common/fs/exec-file.injectable"; import execFileInjectable from "../../common/fs/exec-file.injectable";
import writeFileInjectable from "../../common/fs/write-file.injectable"; import writeFileInjectable from "../../common/fs/write-file.injectable";
import loggerInjectable from "../../common/logger.injectable"; import loggerInjectable from "../../common/logger.injectable";
@ -19,7 +19,7 @@ const createResourceApplierInjectable = getInjectable({
id: "create-resource-applier", id: "create-resource-applier",
instantiate: (di): CreateResourceApplier => { instantiate: (di): CreateResourceApplier => {
const deps: ResourceApplierDependencies = { const deps: ResourceApplierDependencies = {
deleteFile: di.inject(deleteFileInjectable), deleteFile: di.inject(removePathInjectable),
emitAppEvent: di.inject(emitAppEventInjectable), emitAppEvent: di.inject(emitAppEventInjectable),
execFile: di.inject(execFileInjectable), execFile: di.inject(execFileInjectable),
joinPaths: di.inject(joinPathsInjectable), joinPaths: di.inject(joinPathsInjectable),

View File

@ -11,7 +11,7 @@ import type { KubernetesObject } from "@kubernetes/client-node";
import type { EmitAppEvent } from "../../common/app-event-bus/emit-event.injectable"; import type { EmitAppEvent } from "../../common/app-event-bus/emit-event.injectable";
import type { Logger } from "../../common/logger"; import type { Logger } from "../../common/logger";
import type { WriteFile } from "../../common/fs/write-file.injectable"; import type { WriteFile } from "../../common/fs/write-file.injectable";
import type { DeleteFile } from "../../common/fs/delete-file.injectable"; import type { RemovePath } from "../../common/fs/remove.injectable";
import type { ExecFile } from "../../common/fs/exec-file.injectable"; import type { ExecFile } from "../../common/fs/exec-file.injectable";
import type { JoinPaths } from "../../common/path/join-paths.injectable"; import type { JoinPaths } from "../../common/path/join-paths.injectable";
import type { AsyncResult } from "../../common/utils/async-result"; import type { AsyncResult } from "../../common/utils/async-result";
@ -19,7 +19,7 @@ import type { AsyncResult } from "../../common/utils/async-result";
export interface ResourceApplierDependencies { export interface ResourceApplierDependencies {
emitAppEvent: EmitAppEvent; emitAppEvent: EmitAppEvent;
writeFile: WriteFile; writeFile: WriteFile;
deleteFile: DeleteFile; deleteFile: RemovePath;
execFile: ExecFile; execFile: ExecFile;
joinPaths: JoinPaths; joinPaths: JoinPaths;
readonly logger: Logger; readonly logger: Logger;

View File

@ -15,8 +15,8 @@ import { getInjectable } from "@ogre-tools/injectable";
import { userStoreMigrationInjectionToken } from "../../../common/user-store/migrations-token"; import { userStoreMigrationInjectionToken } from "../../../common/user-store/migrations-token";
import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable"; import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable";
import homeDirectoryPathInjectable from "../../../common/os/home-directory-path.injectable"; import homeDirectoryPathInjectable from "../../../common/os/home-directory-path.injectable";
import fsInjectable from "../../../common/fs/fs.injectable";
import loggerInjectable from "../../../common/logger.injectable"; import loggerInjectable from "../../../common/logger.injectable";
import pathExistsSyncInjectable from "../../../common/fs/path-exists-sync.injectable";
const v503Beta1UserStoreMigrationInjectable = getInjectable({ const v503Beta1UserStoreMigrationInjectable = getInjectable({
id: "v5.0.3-beta.1-user-store-migration", id: "v5.0.3-beta.1-user-store-migration",
@ -29,7 +29,7 @@ const v503Beta1UserStoreMigrationInjectable = getInjectable({
const getDirnameOfPath = di.inject(getDirnameOfPathInjectable); const getDirnameOfPath = di.inject(getDirnameOfPathInjectable);
const readJsonSync = di.inject(readJsonSyncInjectable); const readJsonSync = di.inject(readJsonSyncInjectable);
const homeDirectoryPath = di.inject(homeDirectoryPathInjectable); const homeDirectoryPath = di.inject(homeDirectoryPathInjectable);
const { existsSync } = di.inject(fsInjectable); const pathExistsSync = di.inject(pathExistsSyncInjectable);
return { return {
version: "5.0.3-beta.1", version: "5.0.3-beta.1",
@ -63,7 +63,7 @@ const v503Beta1UserStoreMigrationInjectable = getInjectable({
continue; continue;
} }
if (!existsSync(cluster.kubeConfigPath)) { if (!pathExistsSync(cluster.kubeConfigPath)) {
logger.info(`Skipping ${cluster.id} because kubeConfigPath no longer exists`); logger.info(`Skipping ${cluster.id} because kubeConfigPath no longer exists`);
continue; continue;
} }

View File

@ -23,8 +23,8 @@ import installExtensionFromInputInjectable from "../install-extension-from-input
import type { ExtensionInstallationStateStore } from "../../../../extensions/extension-installation-state-store/extension-installation-state-store"; import type { ExtensionInstallationStateStore } from "../../../../extensions/extension-installation-state-store/extension-installation-state-store";
import extensionInstallationStateStoreInjectable from "../../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable"; import extensionInstallationStateStoreInjectable from "../../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable";
import { observable, when } from "mobx"; import { observable, when } from "mobx";
import type { DeleteFile } from "../../../../common/fs/delete-file.injectable"; import type { RemovePath } from "../../../../common/fs/remove.injectable";
import deleteFileInjectable from "../../../../common/fs/delete-file.injectable"; import removePathInjectable from "../../../../common/fs/remove.injectable";
import type { DownloadJson } from "../../../../common/fetch/download-json.injectable"; import type { DownloadJson } from "../../../../common/fetch/download-json.injectable";
import type { DownloadBinary } from "../../../../common/fetch/download-binary.injectable"; import type { DownloadBinary } from "../../../../common/fetch/download-binary.injectable";
import downloadJsonInjectable from "../../../../common/fetch/download-json.injectable"; import downloadJsonInjectable from "../../../../common/fetch/download-json.injectable";
@ -36,7 +36,7 @@ describe("Extensions", () => {
let installExtensionFromInput: jest.MockedFunction<InstallExtensionFromInput>; let installExtensionFromInput: jest.MockedFunction<InstallExtensionFromInput>;
let extensionInstallationStateStore: ExtensionInstallationStateStore; let extensionInstallationStateStore: ExtensionInstallationStateStore;
let render: DiRender; let render: DiRender;
let deleteFileMock: jest.MockedFunction<DeleteFile>; let deleteFileMock: jest.MockedFunction<RemovePath>;
let downloadJson: jest.MockedFunction<DownloadJson>; let downloadJson: jest.MockedFunction<DownloadJson>;
let downloadBinary: jest.MockedFunction<DownloadBinary>; let downloadBinary: jest.MockedFunction<DownloadBinary>;
@ -52,7 +52,7 @@ describe("Extensions", () => {
di.override(installExtensionFromInputInjectable, () => installExtensionFromInput); di.override(installExtensionFromInputInjectable, () => installExtensionFromInput);
deleteFileMock = jest.fn(); deleteFileMock = jest.fn();
di.override(deleteFileInjectable, () => deleteFileMock); di.override(removePathInjectable, () => deleteFileMock);
downloadJson = jest.fn().mockImplementation((url) => { throw new Error(`Unexpected call to downloadJson for url=${url}`); }); downloadJson = jest.fn().mockImplementation((url) => { throw new Error(`Unexpected call to downloadJson for url=${url}`); });
di.override(downloadJsonInjectable, () => downloadJson); di.override(downloadJsonInjectable, () => downloadJson);

View File

@ -63,7 +63,7 @@ import type { FakeExtensionOptions } from "./get-extension-fake";
import { getExtensionFakeForMain, getExtensionFakeForRenderer } from "./get-extension-fake"; import { getExtensionFakeForMain, getExtensionFakeForRenderer } from "./get-extension-fake";
import namespaceApiInjectable from "../../../common/k8s-api/endpoints/namespace.api.injectable"; import namespaceApiInjectable from "../../../common/k8s-api/endpoints/namespace.api.injectable";
import { Namespace } from "../../../common/k8s-api/endpoints"; import { Namespace } from "../../../common/k8s-api/endpoints";
import { overrideFsWithFakes } from "../../../test-utils/override-fs-with-fakes"; import { getOverrideFsWithFakes } from "../../../test-utils/override-fs-with-fakes";
import applicationMenuItemCompositeInjectable from "../../../features/application-menu/main/application-menu-item-composite.injectable"; import applicationMenuItemCompositeInjectable from "../../../features/application-menu/main/application-menu-item-composite.injectable";
import { getCompositePaths } from "../../../common/utils/composite/get-composite-paths/get-composite-paths"; import { getCompositePaths } from "../../../common/utils/composite/get-composite-paths/get-composite-paths";
import { discoverFor } from "./discovery-of-html-elements"; import { discoverFor } from "./discovery-of-html-elements";
@ -173,9 +173,9 @@ export const getApplicationBuilder = () => {
const beforeWindowStartCallbacks: Callback[] = []; const beforeWindowStartCallbacks: Callback[] = [];
const afterWindowStartCallbacks: Callback[] = []; const afterWindowStartCallbacks: Callback[] = [];
const fsState = new Map(); const overrideFsWithFakes = getOverrideFsWithFakes();
overrideFsWithFakes(mainDi, fsState); overrideFsWithFakes(mainDi);
let environment = environments.application; let environment = environments.application;
@ -210,7 +210,7 @@ export const getApplicationBuilder = () => {
const windowDi = getRendererDi({ doGeneralOverrides: true }); const windowDi = getRendererDi({ doGeneralOverrides: true });
overrideForWindow(windowDi, windowId); overrideForWindow(windowDi, windowId);
overrideFsWithFakes(windowDi, fsState); overrideFsWithFakes(windowDi);
runInAction(() => { runInAction(() => {
windowDi.register(rendererExtensionsStateInjectable); windowDi.register(rendererExtensionsStateInjectable);

View File

@ -9,7 +9,7 @@ import { createContainer, isInjectable } from "@ogre-tools/injectable";
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
import requestFromChannelInjectable from "./utils/channel/request-from-channel.injectable"; import requestFromChannelInjectable from "./utils/channel/request-from-channel.injectable";
import loggerInjectable from "../common/logger.injectable"; import loggerInjectable from "../common/logger.injectable";
import { overrideFsWithFakes } from "../test-utils/override-fs-with-fakes"; import { getOverrideFsWithFakes } from "../test-utils/override-fs-with-fakes";
import { createMemoryHistory } from "history"; import { createMemoryHistory } from "history";
import focusWindowInjectable from "./navigation/focus-window.injectable"; import focusWindowInjectable from "./navigation/focus-window.injectable";
import terminalSpawningPoolInjectable from "./components/dock/terminal/terminal-spawning-pool.injectable"; import terminalSpawningPoolInjectable from "./components/dock/terminal/terminal-spawning-pool.injectable";
@ -140,7 +140,7 @@ export const getDiForUnitTesting = (
di.override(requestFromChannelInjectable, () => () => Promise.resolve(undefined as never)); di.override(requestFromChannelInjectable, () => () => Promise.resolve(undefined as never));
overrideFsWithFakes(di); getOverrideFsWithFakes()(di);
di.override(focusWindowInjectable, () => () => {}); di.override(focusWindowInjectable, () => () => {});

View File

@ -3,50 +3,48 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import type { DiContainer } from "@ogre-tools/injectable"; import type { DiContainer } from "@ogre-tools/injectable";
import readFileInjectable from "../common/fs/read-file.injectable"; import fsInjectable from "../common/fs/fs.injectable";
import writeJsonFileInjectable from "../common/fs/write-json-file.injectable"; import { createFsFromVolume, Volume } from "memfs";
import readJsonFileInjectable from "../common/fs/read-json-file.injectable"; import type { readJsonSync as readJsonSyncImpl, writeJsonSync as writeJsonSyncImpl } from "fs-extra";
import pathExistsInjectable from "../common/fs/path-exists.injectable";
import deleteFileInjectable from "../common/fs/delete-file.injectable";
import writeJsonSyncInjectable from "../common/fs/write-json-sync.injectable";
import readJsonSyncInjectable from "../common/fs/read-json-sync.injectable";
export const overrideFsWithFakes = (di: DiContainer, state = new Map()) => { export const getOverrideFsWithFakes = () => {
const readFile = readFileFor(state); const root = createFsFromVolume(Volume.fromJSON({}));
di.override(readFileInjectable, () => async (filePath) => readFile(filePath)); const readJsonSync = ((file, opts) => {
di.override(writeJsonFileInjectable, () => ( const options = typeof opts === "string"
async (filePath, contents) => { ? {
state.set(filePath, JSON.stringify(contents)); encoding: opts,
} }
)); : opts;
di.override(readJsonFileInjectable, () => ( const value = root.readFileSync(file, options as any) as string;
async (filePath: string) => JSON.parse(readFile(filePath))
)); return JSON.parse(value, options?.reviver);
di.override(writeJsonSyncInjectable, () => ( }) as typeof readJsonSyncImpl;
(filePath, data) => state.set(filePath, JSON.stringify(data)) const writeJsonSync = ((file, object, opts) => {
)); const options = typeof opts === "string"
di.override(readJsonSyncInjectable, () => ( ? {
(filePath) => JSON.parse(readFile(filePath)) encoding: opts,
)); }
di.override(pathExistsInjectable, () => ( : opts;
async (filePath: string) => state.has(filePath)
)); root.writeFileSync(file, JSON.stringify(object, options?.replacer, options?.spaces), options as any);
di.override(deleteFileInjectable, () => async (filePath: string) => { }) as typeof writeJsonSyncImpl;
state.delete(filePath);
}); return (di: DiContainer) => {
}; di.override(fsInjectable, () => ({
pathExists: async (path) => root.existsSync(path),
const readFileFor = (state: Map<string, string>) => (filePath: string) => { pathExistsSync: root.existsSync,
const fileContent = state.get(filePath); readFile: root.promises.readFile as any,
readFileSync: root.readFileSync as any,
if (!fileContent) { readJson: async (file, opts) => readJsonSync(file, opts),
const existingFilePaths = [...state.keys()].join('", "'); readJsonSync,
writeFile: root.promises.writeFile as any,
throw new Error( writeFileSync: root.writeFileSync as any,
`Tried to access file ${filePath} which does not exist. Existing file paths are: "${existingFilePaths}"`, writeJson: async (file, obj, opts) => writeJsonSync(file, obj, opts as any),
); writeJsonSync,
} readdir: root.promises.readdir as any,
lstat: root.promises.lstat as any,
return fileContent; rm: root.promises.rm,
}));
};
}; };