configMenu.test.js
1 import { describe, beforeEach, it, expect, vi } from "vitest"; 2 import { ConfigMenu } from "./configMenu.js"; 3 import { mockUiService } from "../__mocks__/service.mocks.js"; 4 import { mockConfigService } from "../__mocks__/service.mocks.js"; 5 import { mockNumberSelector, mockMenuLoop } from "../__mocks__/utils.mocks.js"; 6 7 describe("ConfigMenu", () => { 8 const config = { 9 dataDir: "/data", 10 logsDir: "/logs", 11 storageQuota: 1024 * 1024 * 1024, 12 ports: { 13 discPort: 8090, 14 listenPort: 8070, 15 apiPort: 8080, 16 }, 17 }; 18 let configMenu; 19 20 beforeEach(() => { 21 vi.resetAllMocks(); 22 mockConfigService.get.mockReturnValue(config); 23 24 configMenu = new ConfigMenu( 25 mockUiService, 26 mockMenuLoop, 27 mockConfigService, 28 mockNumberSelector, 29 ); 30 }); 31 32 it("initializes the loop with the config menu", () => { 33 expect(mockMenuLoop.initialize).toHaveBeenCalledWith( 34 configMenu.showConfigMenu, 35 ); 36 }); 37 38 it("shows the config menu header", async () => { 39 await configMenu.show(); 40 expect(mockUiService.showInfoMessage).toHaveBeenCalledWith( 41 "Codex Configuration", 42 ); 43 }); 44 45 it("starts the menu loop", async () => { 46 await configMenu.show(); 47 expect(mockMenuLoop.showLoop).toHaveBeenCalled(); 48 }); 49 50 it("sets the config field", async () => { 51 await configMenu.show(); 52 expect(configMenu.config).toEqual(config); 53 }); 54 55 describe("config menu options", () => { 56 beforeEach(() => { 57 configMenu.config = config; 58 }); 59 60 it("displays the configuration menu", async () => { 61 await configMenu.showConfigMenu(); 62 expect(mockUiService.askMultipleChoice).toHaveBeenCalledWith( 63 "Select to edit:", 64 [ 65 { 66 label: `Storage quota = 1073741824 Bytes (1024 MB)`, 67 action: configMenu.editStorageQuota, 68 }, 69 { 70 label: `Discovery port = ${mockConfigService.get().ports.discPort}`, 71 action: configMenu.editDiscPort, 72 }, 73 { 74 label: `P2P listen port = ${mockConfigService.get().ports.listenPort}`, 75 action: configMenu.editListenPort, 76 }, 77 { 78 label: `API port = ${mockConfigService.get().ports.apiPort}`, 79 action: configMenu.editApiPort, 80 }, 81 { 82 label: "Save changes and exit", 83 action: configMenu.saveChangesAndExit, 84 }, 85 { 86 label: "Discard changes and exit", 87 action: configMenu.discardChangesAndExit, 88 }, 89 ], 90 ); 91 }); 92 93 it("edits the storage quota", async () => { 94 const originalQuota = config.storageQuota; 95 const newQuota = 200 * 1024 * 1024; 96 mockNumberSelector.show.mockResolvedValue(newQuota); 97 98 await configMenu.editStorageQuota(); 99 100 expect(mockUiService.showInfoMessage).toHaveBeenCalledWith( 101 "You can use: 'GB' or 'gb', etc.", 102 ); 103 expect(mockNumberSelector.show).toHaveBeenCalledWith( 104 originalQuota, 105 "Storage quota", 106 true, 107 ); 108 expect(configMenu.config.storageQuota).toEqual(newQuota); 109 }); 110 111 it("shows an error if storage quota is too small", async () => { 112 const originalQuota = config.storageQuota; 113 mockNumberSelector.show.mockResolvedValue(50 * 1024 * 1024); 114 115 await configMenu.editStorageQuota(); 116 117 expect(mockUiService.showErrorMessage).toHaveBeenCalledWith( 118 "Storage quote should be >= 100mb.", 119 ); 120 expect(configMenu.config.storageQuota).toEqual(originalQuota); 121 }); 122 123 it("edits the discovery port", async () => { 124 const originalPort = config.ports.discPort; 125 const newPort = 9000; 126 mockNumberSelector.show.mockResolvedValue(newPort); 127 128 await configMenu.editDiscPort(); 129 130 expect(mockNumberSelector.show).toHaveBeenCalledWith( 131 originalPort, 132 "Discovery port", 133 false, 134 ); 135 expect(configMenu.config.ports.discPort).toEqual(newPort); 136 }); 137 138 it("shows an error if discovery port is out of range", async () => { 139 const originalPort = config.ports.discPort; 140 mockNumberSelector.show.mockResolvedValue(1000); 141 await configMenu.editDiscPort(); 142 143 expect(mockUiService.showErrorMessage).toHaveBeenCalledWith( 144 "Port should be between 1024 and 65535.", 145 ); 146 expect(configMenu.config.ports.discPort).toEqual(originalPort); 147 }); 148 149 it("edits the listen port", async () => { 150 const originalPort = config.ports.listenPort; 151 const newPort = 9000; 152 mockNumberSelector.show.mockResolvedValue(newPort); 153 154 await configMenu.editListenPort(); 155 156 expect(mockNumberSelector.show).toHaveBeenCalledWith( 157 originalPort, 158 "P2P listen port", 159 false, 160 ); 161 expect(configMenu.config.ports.listenPort).toEqual(newPort); 162 }); 163 164 it("shows an error if listen port is out of range", async () => { 165 const originalPort = config.ports.listenPort; 166 mockNumberSelector.show.mockResolvedValue(1000); 167 await configMenu.editListenPort(); 168 169 expect(mockUiService.showErrorMessage).toHaveBeenCalledWith( 170 "Port should be between 1024 and 65535.", 171 ); 172 expect(configMenu.config.ports.listenPort).toEqual(originalPort); 173 }); 174 175 it("edits the API port", async () => { 176 const originalPort = config.ports.apiPort; 177 const newPort = 9000; 178 mockNumberSelector.show.mockResolvedValue(newPort); 179 180 await configMenu.editApiPort(); 181 182 expect(mockNumberSelector.show).toHaveBeenCalledWith( 183 originalPort, 184 "API port", 185 false, 186 ); 187 expect(configMenu.config.ports.apiPort).toEqual(newPort); 188 }); 189 190 it("shows an error if API port is out of range", async () => { 191 const originalPort = config.ports.apiPort; 192 mockNumberSelector.show.mockResolvedValue(1000); 193 await configMenu.editApiPort(); 194 195 expect(mockUiService.showErrorMessage).toHaveBeenCalledWith( 196 "Port should be between 1024 and 65535.", 197 ); 198 expect(configMenu.config.ports.apiPort).toEqual(originalPort); 199 }); 200 }); 201 202 describe("save and discard changes", () => { 203 it("saves changes and exits", async () => { 204 await configMenu.show(); 205 await configMenu.saveChangesAndExit(); 206 207 expect(mockConfigService.saveConfig).toHaveBeenCalled(); 208 expect(mockUiService.showInfoMessage).toHaveBeenCalledWith( 209 "Configuration changes saved.", 210 ); 211 expect(mockMenuLoop.stopLoop).toHaveBeenCalled(); 212 }); 213 214 it("discards changes and exits", async () => { 215 await configMenu.discardChangesAndExit(); 216 217 expect(mockConfigService.loadConfig).toHaveBeenCalled(); 218 expect(mockUiService.showInfoMessage).toHaveBeenCalledWith( 219 "Changes discarded.", 220 ); 221 expect(mockMenuLoop.stopLoop).toHaveBeenCalled(); 222 }); 223 }); 224 });