/ src / ui / configMenu.test.js
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  });