This commit is contained in:
ThatBen 2025-04-15 14:01:44 +02:00
parent 0b24fc238e
commit 03dd11d57e
No known key found for this signature in database
GPG Key ID: E020A7DDCD52E1AB
8 changed files with 159 additions and 35 deletions

View File

@ -6,3 +6,9 @@ export const mockInstaller = {
installCodex: vi.fn(), installCodex: vi.fn(),
uninstallCodex: vi.fn(), uninstallCodex: vi.fn(),
}; };
export const mockProcessControl = {
getNumberOfCodexProcesses: vi.fn(),
stopCodexProcess: vi.fn(),
startCodexProcess: vi.fn(),
};

View File

@ -27,12 +27,21 @@ export class ProcessControl {
const pid = processes[0].pid; const pid = processes[0].pid;
process.kill(pid, "SIGINT"); process.kill(pid, "SIGINT");
await new Promise((resolve) => setTimeout(resolve, 2000)); await this.sleep();
}; };
startCodexProcess = async () => { startCodexProcess = async () => {
await this.saveCodexConfigFile(); await this.saveCodexConfigFile();
await this.startCodex(); await this.startCodex();
await this.sleep();
};
sleep = async () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 5000);
});
}; };
saveCodexConfigFile = async () => { saveCodexConfigFile = async () => {

View File

@ -2,7 +2,8 @@ import axios from "axios";
export class CodexGlobals { export class CodexGlobals {
getPublicIp = async () => { getPublicIp = async () => {
return (await axios.get(`https://ip.codex.storage`)).data; const result = (await axios.get(`https://ip.codex.storage`)).data;
return result.replaceAll("\n", "");
}; };
getTestnetSPRs = async () => { getTestnetSPRs = async () => {

View File

@ -86,6 +86,28 @@ describe("ConfigService", () => {
}); });
}); });
describe("validateConfiguration", () => {
var configService;
var config;
beforeEach(() => {
config = expectedDefaultConfig;
configService = new ConfigService(mockFsService);
configService.config = config;
});
it("throws when codexExe is not set", () => {
config.codexExe = "";
expect(configService.validateConfiguration).toThrow(
"Missing config value: codexExe",
);
});
});
describe("writecodexConfigFile", () => { describe("writecodexConfigFile", () => {
const logsPath = "C:\\path\\codex.log"; const logsPath = "C:\\path\\codex.log";
var configService; var configService;
@ -95,6 +117,7 @@ describe("ConfigService", () => {
mockFsService.isFile.mockReturnValue(false); mockFsService.isFile.mockReturnValue(false);
configService = new ConfigService(mockFsService); configService = new ConfigService(mockFsService);
configService.validateConfiguration = vi.fn();
configService.getLogFilePath = vi.fn(); configService.getLogFilePath = vi.fn();
configService.getLogFilePath.mockReturnValue(logsPath); configService.getLogFilePath.mockReturnValue(logsPath);
}); });
@ -126,7 +149,7 @@ describe("ConfigService", () => {
.map((v) => { .map((v) => {
return '"' + v + '"'; return '"' + v + '"';
}) })
.join(",")}]`, .join(",")}]${newLine}`,
); );
}); });
}); });

View File

@ -64,7 +64,6 @@ export class FsService {
}; };
writeFile = (filePath, content) => { writeFile = (filePath, content) => {
console.log("filepath: " + filePath);
fs.writeFileSync(filePath, content); fs.writeFileSync(filePath, content);
}; };
} }

View File

@ -23,6 +23,6 @@ export class OsService {
}; };
listProcesses = async () => { listProcesses = async () => {
await psList(); return await psList();
}; };
} }

View File

@ -26,11 +26,11 @@ export class MainMenu {
}; };
promptMainMenu = async () => { promptMainMenu = async () => {
if ((await this.processControl.getNumberOfCodexProcesses) > 0) { if ((await this.processControl.getNumberOfCodexProcesses()) > 0) {
await this.showRunningMenu(); await this.showRunningMenu();
} else { } else {
if (await this.installer.isCodexInstalled()) { if (await this.installer.isCodexInstalled()) {
await this.showCodexNotRunningMenu(); await this.showNotRunningMenu();
} else { } else {
await this.showNotInstalledMenu(); await this.showNotInstalledMenu();
} }
@ -52,14 +52,14 @@ export class MainMenu {
showRunningMenu = async () => { showRunningMenu = async () => {
await this.ui.askMultipleChoice("Codex is running", [ await this.ui.askMultipleChoice("Codex is running", [
{
label: "Stop Codex",
action: this.processControl.stopCodexProcess,
},
{ {
label: "Open Codex app", label: "Open Codex app",
action: this.openCodexApp, action: this.openCodexApp,
}, },
{
label: "Stop Codex",
action: this.processControl.stopCodexProcess,
},
{ {
label: "Exit", label: "Exit",
action: this.loop.stopLoop, action: this.loop.stopLoop,
@ -71,7 +71,7 @@ export class MainMenu {
console.log("todo!"); console.log("todo!");
}; };
showCodexNotRunningMenu = async () => { showNotRunningMenu = async () => {
await this.ui.askMultipleChoice("Codex is not running", [ await this.ui.askMultipleChoice("Codex is not running", [
{ {
label: "Start Codex", label: "Start Codex",

View File

@ -2,6 +2,10 @@ import { describe, beforeEach, it, expect, vi } from "vitest";
import { MainMenu } from "./mainMenu.js"; import { MainMenu } from "./mainMenu.js";
import { mockUiService } from "../__mocks__/service.mocks.js"; import { mockUiService } from "../__mocks__/service.mocks.js";
import { mockInstallMenu, mockConfigMenu } from "../__mocks__/ui.mocks.js"; import { mockInstallMenu, mockConfigMenu } from "../__mocks__/ui.mocks.js";
import {
mockInstaller,
mockProcessControl,
} from "../__mocks__/handler.mocks.js";
import { mockMenuLoop } from "../__mocks__/utils.mocks.js"; import { mockMenuLoop } from "../__mocks__/utils.mocks.js";
describe("mainmenu", () => { describe("mainmenu", () => {
@ -15,42 +19,124 @@ describe("mainmenu", () => {
mockMenuLoop, mockMenuLoop,
mockInstallMenu, mockInstallMenu,
mockConfigMenu, mockConfigMenu,
mockInstaller,
mockProcessControl,
); );
}); });
it("initializes the menu loop with the promptMainMenu function", () => { describe("constructor", () => {
expect(mockMenuLoop.initialize).toHaveBeenCalledWith( it("initializes the menu loop with the promptMainMenu function", () => {
mainmenu.promptMainMenu, expect(mockMenuLoop.initialize).toHaveBeenCalledWith(
); mainmenu.promptMainMenu,
);
});
}); });
it("shows the logo", async () => { describe("show", () => {
await mainmenu.show(); it("shows the logo", async () => {
await mainmenu.show();
expect(mockUiService.showLogo).toHaveBeenCalled(); expect(mockUiService.showLogo).toHaveBeenCalled();
});
it("starts the menu loop", async () => {
await mainmenu.show();
expect(mockMenuLoop.showLoop).toHaveBeenCalled();
});
it("shows the exit message after the menu loop", async () => {
await mainmenu.show();
expect(mockUiService.showInfoMessage).toHaveBeenCalledWith("K-THX-BYE");
});
}); });
it("starts the menu loop", async () => { describe("promptMainMenu", () => {
await mainmenu.show(); beforeEach(() => {
mainmenu.showRunningMenu = vi.fn();
mainmenu.showNotRunningMenu = vi.fn();
mainmenu.showNotInstalledMenu = vi.fn();
});
expect(mockMenuLoop.showLoop).toHaveBeenCalled(); it("shows running menu when number of codex processes is greater than zero", async () => {
mockProcessControl.getNumberOfCodexProcesses.mockResolvedValue(1);
await mainmenu.promptMainMenu();
expect(mainmenu.showRunningMenu).toHaveBeenCalled();
expect(mainmenu.showNotRunningMenu).not.toHaveBeenCalled();
expect(mainmenu.showNotInstalledMenu).not.toHaveBeenCalled();
});
it("shows not running menu when number of codex processes is zero and codex is installed", async () => {
mockProcessControl.getNumberOfCodexProcesses.mockResolvedValue(0);
mockInstaller.isCodexInstalled.mockResolvedValue(true);
await mainmenu.promptMainMenu();
expect(mainmenu.showRunningMenu).not.toHaveBeenCalled();
expect(mainmenu.showNotRunningMenu).toHaveBeenCalled();
expect(mainmenu.showNotInstalledMenu).not.toHaveBeenCalled();
});
it("shows not installed menu when number of codex processes is zero and codex is not installed", async () => {
mockProcessControl.getNumberOfCodexProcesses.mockResolvedValue(0);
mockInstaller.isCodexInstalled.mockResolvedValue(false);
await mainmenu.promptMainMenu();
expect(mainmenu.showRunningMenu).not.toHaveBeenCalled();
expect(mainmenu.showNotRunningMenu).not.toHaveBeenCalled();
expect(mainmenu.showNotInstalledMenu).toHaveBeenCalled();
});
}); });
it("shows the exit message after the menu loop", async () => { describe("showNotInstalledMenu", () => {
await mainmenu.show(); it("shows a menu with options to install Codex or exit", async () => {
await mainmenu.showNotInstalledMenu();
expect(mockUiService.showInfoMessage).toHaveBeenCalledWith("K-THX-BYE"); expect(mockUiService.askMultipleChoice).toHaveBeenCalledWith(
"Codex is not installed",
[
{ label: "Install Codex", action: mockInstallMenu.show },
{ label: "Exit", action: mockMenuLoop.stopLoop },
],
);
});
}); });
it("prompts the main menu with multiple choices", async () => { describe("showRunningMenu", () => {
await mainmenu.promptMainMenu(); it("shows a menu with options to stop Codex, open Codex app, or exit", async () => {
expect(mockUiService.askMultipleChoice).toHaveBeenCalledWith( await mainmenu.showRunningMenu();
"Select an option",
[ expect(mockUiService.askMultipleChoice).toHaveBeenCalledWith(
{ label: "Install/uninstall Codex", action: mockInstallMenu.show }, "Codex is running",
{ label: "Configure Codex", action: mockConfigMenu.show }, [
{ label: "Exit", action: mockMenuLoop.stopLoop }, { label: "Open Codex app", action: mainmenu.openCodexApp },
], { label: "Stop Codex", action: mockProcessControl.stopCodexProcess },
); { label: "Exit", action: mockMenuLoop.stopLoop },
],
);
});
});
describe("showNotRunningMenu", () => {
it("shows a menu with options to start Codex, configure, uninstall, or exit", async () => {
await mainmenu.showNotRunningMenu();
expect(mockUiService.askMultipleChoice).toHaveBeenCalledWith(
"Codex is not running",
[
{
label: "Start Codex",
action: mockProcessControl.startCodexProcess,
},
{ label: "Edit Codex config", action: mockConfigMenu.show },
{ label: "Uninstall Codex", action: mockInstallMenu.show },
{ label: "Exit", action: mockMenuLoop.stopLoop },
],
);
});
}); });
}); });