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.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { ReadOptions } 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({
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,
});

View File

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

View File

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

View File

@ -4,8 +4,8 @@
*/
import { getGlobalOverride } from "../test-utils/get-global-override";
import removePathInjectable from "./remove-path.injectable";
import removePathInjectable from "./remove.injectable";
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 fsInjectable from "./fs.injectable";
export type RemovePath = (path: string) => Promise<void>;
export type RemovePath = (filePath: string) => Promise<void>;
const removePathInjectable = getInjectable({
id: "remove-path",
instantiate: (di): RemovePath => di.inject(fsInjectable).remove,
instantiate: (di): RemovePath => di.inject(fsInjectable).rm,
});
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 getRelativePathInjectable from "../../common/path/get-relative-path.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";
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 watchInjectable from "../../common/fs/watch/watch.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 joinPathsInjectable from "../../common/path/join-paths.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 { GetDirnameOfPath } from "../../common/path/get-dirname.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";
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 clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.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 { noop } from "../../../../common/utils";
import { getRequestChannelListenerInjectable } from "../../../../main/utils/channel/channel-listeners/listener-tokens";
@ -20,7 +20,7 @@ const deleteClusterChannelListenerInjectable = getRequestChannelListenerInjectab
const clusterFrames = di.inject(clusterFramesInjectable);
const joinPaths = di.inject(joinPathsInjectable);
const directoryForLensLocalStorage = di.inject(directoryForLensLocalStorageInjectable);
const deleteFile = di.inject(deleteFileInjectable);
const deleteFile = di.inject(removePathInjectable);
return async (clusterId) => {
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 type { PathExists } from "../../common/fs/path-exists.injectable";
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
import type { DeleteFile } from "../../common/fs/delete-file.injectable";
import deleteFileInjectable from "../../common/fs/delete-file.injectable";
import type { RemovePath } from "../../common/fs/remove.injectable";
import removePathInjectable from "../../common/fs/remove.injectable";
const clusterServerUrl = "https://192.168.64.3:8443";
@ -36,7 +36,7 @@ describe("kubeconfig manager tests", () => {
let di: DiContainer;
let loggerMock: jest.Mocked<Logger>;
let readFileMock: AsyncFnMock<ReadFile>;
let deleteFileMock: AsyncFnMock<DeleteFile>;
let deleteFileMock: AsyncFnMock<RemovePath>;
let writeFileMock: AsyncFnMock<WriteFile>;
let pathExistsMock: AsyncFnMock<PathExists>;
let kubeConfManager: KubeconfigManager;
@ -58,7 +58,7 @@ describe("kubeconfig manager tests", () => {
pathExistsMock = asyncFn();
di.override(pathExistsInjectable, () => pathExistsMock);
deleteFileMock = asyncFn();
di.override(deleteFileInjectable, () => deleteFileMock);
di.override(removePathInjectable, () => deleteFileMock);
loggerMock = {
warn: jest.fn(),

View File

@ -6,7 +6,6 @@
// Move embedded kubeconfig into separate file and add reference to it to cluster settings
// 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 getCustomKubeConfigDirectoryInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
import type { ClusterModel } from "../../../common/cluster-types";
@ -20,24 +19,20 @@ interface Pre360ClusterModel extends ClusterModel {
import { getInjectable } from "@ogre-tools/injectable";
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 loggerInjectable from "../../../common/logger.injectable";
import writeFileSyncInjectable from "../../../common/fs/write-file-sync.injectable";
const v360Beta1ClusterStoreMigrationInjectable = getInjectable({
id: "v3.6.0-beta.1-cluster-store-migration",
instantiate: (di) => {
const userDataPath = di.inject(directoryForUserDataInjectable);
const kubeConfigsPath = di.inject(directoryForKubeConfigsInjectable);
const getCustomKubeConfigDirectory = di.inject(getCustomKubeConfigDirectoryInjectable);
const readFileSync = di.inject(readFileSyncInjectable);
const readFileBufferSync = di.inject(readFileBufferSyncInjectable);
const joinPaths = di.inject(joinPathsInjectable);
const logger = di.inject(loggerInjectable);
const {
ensureDirSync,
writeFileSync,
} = di.inject(fsInjectable);
const writeFileSync = di.inject(writeFileSyncInjectable);
return {
version: "3.6.0-beta.1",
@ -45,8 +40,6 @@ const v360Beta1ClusterStoreMigrationInjectable = getInjectable({
const storedClusters = (store.get("clusters") ?? []) as Pre360ClusterModel[];
const migratedClusters: ClusterModel[] = [];
ensureDirSync(kubeConfigsPath);
logger.info("Number of clusters to migrate: ", storedClusters.length);
for (const clusterModel of storedClusters) {
@ -61,7 +54,7 @@ const v360Beta1ClusterStoreMigrationInjectable = getInjectable({
}
// 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.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 loggerInjectable from "../../../common/logger.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 pathExistsSyncInjectable from "../../../common/fs/path-exists-sync.injectable";
const clusterStoreSnapMigrationInjectable = getInjectable({
id: "cluster-store-snap-migration",
@ -19,7 +19,7 @@ const clusterStoreSnapMigrationInjectable = getInjectable({
const { version } = di.inject(applicationInformationInjectable);
const logger = di.inject(loggerInjectable);
const isSnapPackage = di.inject(isSnapPackageInjectable);
const { existsSync } = di.inject(fsInjectable);
const pathExistsSync = di.inject(pathExistsSyncInjectable);
return {
version, // Run always after upgrade
@ -39,7 +39,7 @@ const clusterStoreSnapMigrationInjectable = getInjectable({
/**
* 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/");
cluster.kubeConfigPath = kubeconfigPath;

View File

@ -8,7 +8,7 @@ import loggerInjectable from "../../../common/logger.injectable";
import tempy from "tempy";
import getHelmReleaseInjectable from "./get-helm-release.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";
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 joinPathsInjectable from "../../common/path/join-paths.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 writeFileInjectable from "../../common/fs/write-file.injectable";
import removePathInjectable from "../../common/fs/remove.injectable";
export interface KubeConfigManagerInstantiationParameter {
cluster: Cluster;
@ -31,7 +31,7 @@ const createKubeconfigManagerInjectable = getInjectable({
lensProxyPort: di.inject(lensProxyPortInjectable),
joinPaths: di.inject(joinPathsInjectable),
getDirnameOfPath: di.inject(getDirnameOfPathInjectable),
deleteFile: di.inject(deleteFileInjectable),
removePath: di.inject(removePathInjectable),
pathExists: di.inject(pathExistsInjectable),
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 { GetDirnameOfPath } from "../../common/path/get-dirname.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";
export interface KubeconfigManagerDependencies {
@ -23,7 +23,7 @@ export interface KubeconfigManagerDependencies {
joinPaths: JoinPaths;
getDirnameOfPath: GetDirnameOfPath;
pathExists: PathExists;
deleteFile: DeleteFile;
removePath: RemovePath;
writeFile: WriteFile;
}
@ -65,7 +65,7 @@ export class KubeconfigManager {
this.dependencies.logger.info(`[KUBECONFIG-MANAGER]: Deleting temporary kubeconfig: ${this.tempFilePath}`);
try {
await this.dependencies.deleteFile(this.tempFilePath);
await this.dependencies.removePath(this.tempFilePath);
} catch (error) {
if (isErrnoException(error) && error.code !== "ENOENT") {
throw error;

View File

@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import emitAppEventInjectable from "../../common/app-event-bus/emit-event.injectable";
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 writeFileInjectable from "../../common/fs/write-file.injectable";
import loggerInjectable from "../../common/logger.injectable";
@ -19,7 +19,7 @@ const createResourceApplierInjectable = getInjectable({
id: "create-resource-applier",
instantiate: (di): CreateResourceApplier => {
const deps: ResourceApplierDependencies = {
deleteFile: di.inject(deleteFileInjectable),
deleteFile: di.inject(removePathInjectable),
emitAppEvent: di.inject(emitAppEventInjectable),
execFile: di.inject(execFileInjectable),
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 { Logger } from "../../common/logger";
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 { JoinPaths } from "../../common/path/join-paths.injectable";
import type { AsyncResult } from "../../common/utils/async-result";
@ -19,7 +19,7 @@ import type { AsyncResult } from "../../common/utils/async-result";
export interface ResourceApplierDependencies {
emitAppEvent: EmitAppEvent;
writeFile: WriteFile;
deleteFile: DeleteFile;
deleteFile: RemovePath;
execFile: ExecFile;
joinPaths: JoinPaths;
readonly logger: Logger;

View File

@ -15,8 +15,8 @@ import { getInjectable } from "@ogre-tools/injectable";
import { userStoreMigrationInjectionToken } from "../../../common/user-store/migrations-token";
import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable";
import homeDirectoryPathInjectable from "../../../common/os/home-directory-path.injectable";
import fsInjectable from "../../../common/fs/fs.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import pathExistsSyncInjectable from "../../../common/fs/path-exists-sync.injectable";
const v503Beta1UserStoreMigrationInjectable = getInjectable({
id: "v5.0.3-beta.1-user-store-migration",
@ -29,7 +29,7 @@ const v503Beta1UserStoreMigrationInjectable = getInjectable({
const getDirnameOfPath = di.inject(getDirnameOfPathInjectable);
const readJsonSync = di.inject(readJsonSyncInjectable);
const homeDirectoryPath = di.inject(homeDirectoryPathInjectable);
const { existsSync } = di.inject(fsInjectable);
const pathExistsSync = di.inject(pathExistsSyncInjectable);
return {
version: "5.0.3-beta.1",
@ -63,7 +63,7 @@ const v503Beta1UserStoreMigrationInjectable = getInjectable({
continue;
}
if (!existsSync(cluster.kubeConfigPath)) {
if (!pathExistsSync(cluster.kubeConfigPath)) {
logger.info(`Skipping ${cluster.id} because kubeConfigPath no longer exists`);
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 extensionInstallationStateStoreInjectable from "../../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable";
import { observable, when } from "mobx";
import type { DeleteFile } from "../../../../common/fs/delete-file.injectable";
import deleteFileInjectable from "../../../../common/fs/delete-file.injectable";
import type { RemovePath } from "../../../../common/fs/remove.injectable";
import removePathInjectable from "../../../../common/fs/remove.injectable";
import type { DownloadJson } from "../../../../common/fetch/download-json.injectable";
import type { DownloadBinary } from "../../../../common/fetch/download-binary.injectable";
import downloadJsonInjectable from "../../../../common/fetch/download-json.injectable";
@ -36,7 +36,7 @@ describe("Extensions", () => {
let installExtensionFromInput: jest.MockedFunction<InstallExtensionFromInput>;
let extensionInstallationStateStore: ExtensionInstallationStateStore;
let render: DiRender;
let deleteFileMock: jest.MockedFunction<DeleteFile>;
let deleteFileMock: jest.MockedFunction<RemovePath>;
let downloadJson: jest.MockedFunction<DownloadJson>;
let downloadBinary: jest.MockedFunction<DownloadBinary>;
@ -52,7 +52,7 @@ describe("Extensions", () => {
di.override(installExtensionFromInputInjectable, () => installExtensionFromInput);
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}`); });
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 namespaceApiInjectable from "../../../common/k8s-api/endpoints/namespace.api.injectable";
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 { getCompositePaths } from "../../../common/utils/composite/get-composite-paths/get-composite-paths";
import { discoverFor } from "./discovery-of-html-elements";
@ -173,9 +173,9 @@ export const getApplicationBuilder = () => {
const beforeWindowStartCallbacks: Callback[] = [];
const afterWindowStartCallbacks: Callback[] = [];
const fsState = new Map();
const overrideFsWithFakes = getOverrideFsWithFakes();
overrideFsWithFakes(mainDi, fsState);
overrideFsWithFakes(mainDi);
let environment = environments.application;
@ -210,7 +210,7 @@ export const getApplicationBuilder = () => {
const windowDi = getRendererDi({ doGeneralOverrides: true });
overrideForWindow(windowDi, windowId);
overrideFsWithFakes(windowDi, fsState);
overrideFsWithFakes(windowDi);
runInAction(() => {
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 requestFromChannelInjectable from "./utils/channel/request-from-channel.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 focusWindowInjectable from "./navigation/focus-window.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));
overrideFsWithFakes(di);
getOverrideFsWithFakes()(di);
di.override(focusWindowInjectable, () => () => {});

View File

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