mirror of
https://github.com/logos-storage/logos-storage-installer.git
synced 2026-01-31 11:43:10 +00:00
finishes install + uninstall
This commit is contained in:
parent
7730176ecc
commit
0ed0b0a218
8
src/__mocks__/handler.mocks.js
Normal file
8
src/__mocks__/handler.mocks.js
Normal file
@ -0,0 +1,8 @@
|
||||
import { vi } from "vitest";
|
||||
|
||||
export const mockInstaller = {
|
||||
isCodexInstalled: vi.fn(),
|
||||
getCodexVersion: vi.fn(),
|
||||
installCodex: vi.fn(),
|
||||
uninstallCodex: vi.fn(),
|
||||
};
|
||||
@ -6,6 +6,9 @@ export const mockUiService = {
|
||||
showErrorMessage: vi.fn(),
|
||||
askMultipleChoice: vi.fn(),
|
||||
askPrompt: vi.fn(),
|
||||
createAndStartSpinner: vi.fn(),
|
||||
stopSpinnerSuccess: vi.fn(),
|
||||
stopSpinnerError: vi.fn(),
|
||||
};
|
||||
|
||||
export const mockConfigService = {
|
||||
@ -21,6 +24,7 @@ export const mockFsService = {
|
||||
isFile: vi.fn(),
|
||||
readDir: vi.fn(),
|
||||
makeDir: vi.fn(),
|
||||
deleteDir: vi.fn(),
|
||||
};
|
||||
|
||||
export const mockShellService = {
|
||||
|
||||
@ -39,6 +39,11 @@ export class Installer {
|
||||
processCallbacks.installSuccessful();
|
||||
};
|
||||
|
||||
uninstallCodex = () => {
|
||||
this.fs.deleteDir(this.config.codexInstallPath);
|
||||
this.fs.deleteDir(this.config.dataDir);
|
||||
};
|
||||
|
||||
arePrerequisitesCorrect = async (processCallbacks) => {
|
||||
if (await this.isCodexInstalled()) {
|
||||
processCallbacks.warn("Codex is already installed.");
|
||||
@ -74,7 +79,7 @@ export class Installer {
|
||||
};
|
||||
|
||||
installCodexUnix = async (processCallbacks) => {
|
||||
if (!await this.ensureUnixDependencies(processCallbacks)) return;
|
||||
if (!(await this.ensureUnixDependencies(processCallbacks))) return;
|
||||
await this.shell.run(
|
||||
"curl -# --connect-timeout 10 --max-time 60 -L https://get.codex.storage/install.sh -o install.sh && chmod +x install.sh",
|
||||
);
|
||||
|
||||
@ -241,7 +241,9 @@ describe("Installer", () => {
|
||||
|
||||
it("ensures unix dependencies", async () => {
|
||||
await installer.installCodexUnix(processCallbacks);
|
||||
expect(installer.ensureUnixDependencies).toHaveBeenCalled(processCallbacks);
|
||||
expect(installer.ensureUnixDependencies).toHaveBeenCalled(
|
||||
processCallbacks,
|
||||
);
|
||||
});
|
||||
|
||||
it("returns early if unix dependencies are not met", async () => {
|
||||
@ -255,9 +257,9 @@ describe("Installer", () => {
|
||||
});
|
||||
|
||||
describe("when dependencies are met", () => {
|
||||
beforeEach(() =>{
|
||||
beforeEach(() => {
|
||||
installer.ensureUnixDependencies.mockResolvedValue(true);
|
||||
})
|
||||
});
|
||||
|
||||
it("runs the curl command to download the installer", async () => {
|
||||
await installer.installCodexUnix(processCallbacks);
|
||||
@ -385,4 +387,20 @@ describe("Installer", () => {
|
||||
expect(mockConfigService.saveConfig).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("uninstallCodex", () => {
|
||||
it("deletes the codex install path", () => {
|
||||
installer.uninstallCodex();
|
||||
|
||||
expect(mockFsService.deleteDir).toHaveBeenCalledWith(
|
||||
config.codexInstallPath,
|
||||
);
|
||||
});
|
||||
|
||||
it("deletes the codex data path", () => {
|
||||
installer.uninstallCodex();
|
||||
|
||||
expect(mockFsService.deleteDir).toHaveBeenCalledWith(config.dataDir);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
14
src/main.js
14
src/main.js
@ -105,8 +105,18 @@ export async function main() {
|
||||
const numberSelector = new NumberSelector(uiService);
|
||||
const shellService = new ShellService();
|
||||
const osService = new OsService();
|
||||
const installer = new Installer(configService, shellService, osService, fsService);
|
||||
const installMenu = new InstallMenu(uiService, configService, pathSelector, installer);
|
||||
const installer = new Installer(
|
||||
configService,
|
||||
shellService,
|
||||
osService,
|
||||
fsService,
|
||||
);
|
||||
const installMenu = new InstallMenu(
|
||||
uiService,
|
||||
configService,
|
||||
pathSelector,
|
||||
installer,
|
||||
);
|
||||
const configMenu = new ConfigMenu(
|
||||
uiService,
|
||||
new MenuLoop(),
|
||||
|
||||
@ -50,4 +50,8 @@ export class FsService {
|
||||
moveDir = (oldPath, newPath) => {
|
||||
fs.moveSync(oldPath, newPath);
|
||||
};
|
||||
|
||||
deleteDir = (dir) => {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
};
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ export class UiService {
|
||||
|
||||
createAndStartSpinner = (message) => {
|
||||
return createSpinner(message).start();
|
||||
}
|
||||
};
|
||||
|
||||
stopSpinnerSuccess = (spinner) => {
|
||||
if (spinner == undefined) return;
|
||||
|
||||
@ -13,20 +13,7 @@ export class InstallMenu {
|
||||
} else {
|
||||
await this.showInstallMenu();
|
||||
}
|
||||
}
|
||||
|
||||
showUninstallMenu = async () => {
|
||||
await this.ui.askMultipleChoice("Codex is installed", [
|
||||
{
|
||||
label: "Uninstall",
|
||||
action: this.performUninstall,
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
action: this.doNothing,
|
||||
},
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
showInstallMenu = async () => {
|
||||
await this.ui.askMultipleChoice("Configure your Codex installation", [
|
||||
@ -49,6 +36,41 @@ export class InstallMenu {
|
||||
]);
|
||||
};
|
||||
|
||||
showUninstallMenu = async () => {
|
||||
await this.ui.askMultipleChoice("Codex is installed", [
|
||||
{
|
||||
label: "Uninstall",
|
||||
action: this.showConfirmUninstall,
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
action: this.doNothing,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
showConfirmUninstall = async () => {
|
||||
this.ui.showInfoMessage(
|
||||
"You are about to:\n" +
|
||||
" - Uninstall the Codex application\n" +
|
||||
" - Delete the data stored in your Codex node",
|
||||
);
|
||||
|
||||
await this.ui.askMultipleChoice(
|
||||
"Are you sure you want to uninstall Codex?",
|
||||
[
|
||||
{
|
||||
label: "No",
|
||||
action: this.doNothing,
|
||||
},
|
||||
{
|
||||
label: "Yes",
|
||||
action: this.performUninstall,
|
||||
},
|
||||
],
|
||||
);
|
||||
};
|
||||
|
||||
selectInstallPath = async () => {
|
||||
this.config.codexInstallPath = await this.pathSelector.show(
|
||||
this.config.codexInstallPath,
|
||||
@ -66,7 +88,9 @@ export class InstallMenu {
|
||||
await this.installer.installCodex(this);
|
||||
};
|
||||
|
||||
performUninstall = async () => {};
|
||||
performUninstall = async () => {
|
||||
this.installer.uninstallCodex();
|
||||
};
|
||||
|
||||
doNothing = async () => {};
|
||||
|
||||
@ -77,12 +101,12 @@ export class InstallMenu {
|
||||
|
||||
downloadSuccessful = () => {
|
||||
this.ui.showInfoMessage("Download successful...");
|
||||
}
|
||||
};
|
||||
|
||||
installSuccessful = () => {
|
||||
this.ui.showInfoMessage("Installation successful!");
|
||||
this.ui.stopSpinnerSuccess(this.installSpinner);
|
||||
}
|
||||
};
|
||||
|
||||
warn = (message) => {
|
||||
this.ui.showErrorMessage(message);
|
||||
|
||||
@ -3,6 +3,7 @@ import { InstallMenu } from "./installMenu.js";
|
||||
import { mockUiService } from "../__mocks__/service.mocks.js";
|
||||
import { mockConfigService } from "../__mocks__/service.mocks.js";
|
||||
import { mockPathSelector } from "../__mocks__/utils.mocks.js";
|
||||
import { mockInstaller } from "../__mocks__/handler.mocks.js";
|
||||
|
||||
describe("InstallMenu", () => {
|
||||
const config = {
|
||||
@ -18,11 +19,35 @@ describe("InstallMenu", () => {
|
||||
mockUiService,
|
||||
mockConfigService,
|
||||
mockPathSelector,
|
||||
mockInstaller,
|
||||
);
|
||||
});
|
||||
|
||||
describe("show", () => {
|
||||
beforeEach(() => {
|
||||
installMenu.showInstallMenu = vi.fn();
|
||||
installMenu.showUninstallMenu = vi.fn();
|
||||
});
|
||||
|
||||
it("shows uninstall menu when codex is installed", async () => {
|
||||
mockInstaller.isCodexInstalled.mockResolvedValue(true);
|
||||
|
||||
await installMenu.show();
|
||||
|
||||
expect(installMenu.showUninstallMenu).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows install menu when codex is not installed", async () => {
|
||||
mockInstaller.uninstallCodex.mockResolvedValue(false);
|
||||
|
||||
await installMenu.show();
|
||||
|
||||
expect(installMenu.showInstallMenu).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("displays the install menu", async () => {
|
||||
await installMenu.show();
|
||||
await installMenu.showInstallMenu();
|
||||
expect(mockUiService.askMultipleChoice).toHaveBeenCalledWith(
|
||||
"Configure your Codex installation",
|
||||
[
|
||||
@ -46,6 +71,47 @@ describe("InstallMenu", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("displays the uninstall menu", async () => {
|
||||
await installMenu.showUninstallMenu();
|
||||
expect(mockUiService.askMultipleChoice).toHaveBeenCalledWith(
|
||||
"Codex is installed",
|
||||
[
|
||||
{
|
||||
label: "Uninstall",
|
||||
action: installMenu.showConfirmUninstall,
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
action: installMenu.doNothing,
|
||||
},
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
it("confirms uninstall", async () => {
|
||||
await installMenu.showConfirmUninstall();
|
||||
|
||||
expect(mockUiService.showInfoMessage).toHaveBeenCalledWith(
|
||||
"You are about to:\n" +
|
||||
" - Uninstall the Codex application\n" +
|
||||
" - Delete the data stored in your Codex node",
|
||||
);
|
||||
|
||||
expect(mockUiService.askMultipleChoice).toHaveBeenCalledWith(
|
||||
"Are you sure you want to uninstall Codex?",
|
||||
[
|
||||
{
|
||||
label: "No",
|
||||
action: installMenu.doNothing,
|
||||
},
|
||||
{
|
||||
label: "Yes",
|
||||
action: installMenu.performUninstall,
|
||||
},
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
it("allows selecting the install path", async () => {
|
||||
const originalPath = config.codexInstallPath;
|
||||
const newPath = "/new/path";
|
||||
@ -68,4 +134,66 @@ describe("InstallMenu", () => {
|
||||
"This option is not currently available.",
|
||||
);
|
||||
});
|
||||
|
||||
it("calls installed for installation", async () => {
|
||||
await installMenu.performInstall();
|
||||
|
||||
expect(mockInstaller.installCodex).toHaveBeenCalledWith(installMenu);
|
||||
});
|
||||
|
||||
it("calls installer for deinstallation", async () => {
|
||||
await installMenu.performUninstall();
|
||||
|
||||
expect(mockInstaller.uninstallCodex).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("process callback handling", () => {
|
||||
const mockSpinner = {
|
||||
isRealSpinner: "no srry",
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockUiService.createAndStartSpinner.mockReturnValue(mockSpinner);
|
||||
});
|
||||
|
||||
it("creates spinner on installStarts", () => {
|
||||
installMenu.installStarts();
|
||||
|
||||
expect(installMenu.installSpinner).toBe(mockSpinner);
|
||||
expect(mockUiService.createAndStartSpinner).toHaveBeenCalledWith(
|
||||
"Installing...",
|
||||
);
|
||||
});
|
||||
|
||||
it("shows download success message", () => {
|
||||
installMenu.downloadSuccessful();
|
||||
|
||||
expect(mockUiService.showInfoMessage).toHaveBeenCalledWith(
|
||||
"Download successful...",
|
||||
);
|
||||
});
|
||||
|
||||
it("shows install success message", () => {
|
||||
installMenu.installSpinner = mockSpinner;
|
||||
|
||||
installMenu.installSuccessful();
|
||||
|
||||
expect(mockUiService.showInfoMessage).toHaveBeenCalledWith(
|
||||
"Installation successful!",
|
||||
);
|
||||
expect(mockUiService.stopSpinnerSuccess).toHaveBeenCalledWith(
|
||||
mockSpinner,
|
||||
);
|
||||
});
|
||||
|
||||
it("shows warnings", () => {
|
||||
const message = "warning!";
|
||||
installMenu.installSpinner = mockSpinner;
|
||||
|
||||
installMenu.warn(message);
|
||||
|
||||
expect(mockUiService.showErrorMessage).toHaveBeenCalledWith(message);
|
||||
expect(mockUiService.stopSpinnerError).toHaveBeenCalledWith(mockSpinner);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -143,8 +143,7 @@ export class PathSelector {
|
||||
try {
|
||||
const entries = this.fs.readDir(fullPath);
|
||||
return entries.filter((entry) => this.isSubDir(entry));
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
@ -102,12 +102,14 @@ describe("PathSelector", () => {
|
||||
});
|
||||
|
||||
it("handles non-existing paths", async () => {
|
||||
mockFsService.readDir.mockImplementationOnce(() => { throw new Error("A!"); });
|
||||
|
||||
mockFsService.readDir.mockImplementationOnce(() => {
|
||||
throw new Error("A!");
|
||||
});
|
||||
|
||||
await pathSelector.downOne();
|
||||
|
||||
expect(mockUiService.showInfoMessage).toHaveBeenCalledWith(
|
||||
"There are no subdirectories here."
|
||||
"There are no subdirectories here.",
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user