genlayer 0.4.0 → 0.5.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,8 +12,6 @@ import {
12
12
  import {
13
13
  DOCKER_IMAGES_AND_CONTAINERS_NAME_PREFIX,
14
14
  VERSION_REQUIREMENTS,
15
- } from "../../src/lib/config/simulator";
16
- import {
17
15
  STARTING_TIMEOUT_ATTEMPTS,
18
16
  DEFAULT_RUN_SIMULATOR_COMMAND,
19
17
  } from "../../src/lib/config/simulator";
@@ -21,7 +19,15 @@ import { rpcClient } from "../../src/lib/clients/jsonRpcClient";
21
19
  import * as semver from "semver";
22
20
  import Docker from "dockerode";
23
21
  import {VersionRequiredError} from "../../src/lib/errors/versionRequired";
22
+ import updateCheck from "update-check";
24
23
 
24
+ vi.mock("../../package.json", () => ({
25
+ default: { version: "1.0.0", name: "genlayer" },
26
+ }));
27
+
28
+ vi.mock("update-check", () => ({
29
+ default: vi.fn(),
30
+ }));
25
31
  vi.mock("dockerode");
26
32
  vi.mock("fs");
27
33
  vi.mock("path");
@@ -51,23 +57,14 @@ describe("SimulatorService - Basic Tests", () => {
51
57
  vi.mocked(path.join).mockImplementation((...args) => args.join("/"));
52
58
  });
53
59
 
54
- test("should return the correct simulator location path", () => {
55
- const expectedPath = "/mock/home/genlayer-simulator";
56
- simulatorService.setSimulatorLocation("/mock/home/genlayer-simulator");
57
- const simulatorLocation = simulatorService.getSimulatorLocation();
58
- expect(simulatorLocation).toBe(expectedPath);
59
- });
60
60
 
61
61
  test("should read the correct frontend URL from .env config", () => {
62
- const mockEnvFilePath = "/mock/home/genlayer-simulator/.env";
63
62
  const mockEnvContent = "FRONTEND_PORT=8080";
64
63
  const mockEnvConfig = { FRONTEND_PORT: "8080" };
65
64
  vi.mocked(fs.readFileSync).mockReturnValue(mockEnvContent);
66
65
  vi.mocked(dotenv.parse).mockReturnValue(mockEnvConfig);
67
- simulatorService.setSimulatorLocation("/mock/home/genlayer-simulator");
68
66
  const frontendUrl = simulatorService.getFrontendUrl();
69
67
  expect(frontendUrl).toBe("http://localhost:8080");
70
- expect(fs.readFileSync).toHaveBeenCalledWith(mockEnvFilePath, "utf8");
71
68
  });
72
69
 
73
70
  test("should check version requirements and return missing versions", async () => {
@@ -87,20 +84,6 @@ describe("SimulatorService - Basic Tests", () => {
87
84
  await expect(simulatorService.checkVersion("14.0.0", "node")).rejects.toThrow();
88
85
  });
89
86
 
90
- test("should download simulator if not already installed", async () => {
91
- const result = await simulatorService.downloadSimulator();
92
- expect(result.wasInstalled).toBe(false);
93
- expect(executeCommand).toHaveBeenCalled();
94
- });
95
-
96
- test("should skip download if simulator is already installed", async () => {
97
- vi.mocked(executeCommand).mockRejectedValueOnce(new Error("Mocked command error"));
98
- vi.spyOn(fs, "existsSync").mockReturnValue(true);
99
- const result = await simulatorService.downloadSimulator();
100
- expect(result.wasInstalled).toBe(true);
101
- expect(executeCommand).toHaveBeenCalled();
102
- expect(fs.existsSync).toHaveBeenCalled();
103
- });
104
87
 
105
88
  test("should return initialized true when simulator responds with OK (result.status = OK)", async () => {
106
89
  vi.mocked(rpcClient.request).mockResolvedValueOnce({ result: {status: 'OK'} });
@@ -142,7 +125,7 @@ describe("SimulatorService - Basic Tests", () => {
142
125
  stderr: "",
143
126
  });
144
127
  const result = await simulatorService.runSimulator();
145
- const expectedCommand = DEFAULT_RUN_SIMULATOR_COMMAND("/mock/home/genlayer-simulator", '');
128
+ const expectedCommand = DEFAULT_RUN_SIMULATOR_COMMAND(simulatorService.location, '');
146
129
  expect(executeCommand).toHaveBeenCalledWith(expectedCommand);
147
130
  expect(result).toEqual({ stdout: "Simulator started", stderr: "" });
148
131
  });
@@ -155,11 +138,57 @@ describe("SimulatorService - Basic Tests", () => {
155
138
  simulatorService.setComposeOptions(true)
156
139
  const commandOption = simulatorService.getComposeOptions();
157
140
  const result = await simulatorService.runSimulator();
158
- const expectedCommand = DEFAULT_RUN_SIMULATOR_COMMAND("/mock/home/genlayer-simulator", commandOption);
141
+ const expectedCommand = DEFAULT_RUN_SIMULATOR_COMMAND(simulatorService.location, commandOption);
159
142
  expect(executeCommand).toHaveBeenCalledWith(expectedCommand);
160
143
  expect(result).toEqual({ stdout: "Simulator started", stderr: "" });
161
144
  });
162
145
 
146
+ test("should create a backup of the .env file and add new config", () => {
147
+ const envFilePath = `/.env`;
148
+ const originalEnvContent = "KEY1=value1\nKEY2=value2";
149
+ const parsedEnvConfig = { KEY1: "value1", KEY2: "value2" };
150
+ const newConfig = { KEY3: "value3", KEY2: "newValue2" };
151
+
152
+ vi.mocked(fs.readFileSync).mockImplementation((filePath: any) => {
153
+ if (filePath === envFilePath) return originalEnvContent;
154
+ return "";
155
+ });
156
+
157
+ vi.mocked(dotenv.parse).mockReturnValue(parsedEnvConfig);
158
+ const writeFileSyncMock = vi.mocked(fs.writeFileSync);
159
+
160
+ simulatorService.addConfigToEnvFile(newConfig);
161
+
162
+ const expectedUpdatedContent = `KEY1=value1\nKEY2=newValue2\nKEY3=value3`;
163
+ expect(writeFileSyncMock).toHaveBeenCalledWith(envFilePath, expectedUpdatedContent);
164
+ });
165
+
166
+ test("should handle empty .env file and add new config", () => {
167
+ const envFilePath = `/.env`;
168
+ const newConfig = { NEW_KEY: "newValue" };
169
+
170
+ vi.mocked(fs.readFileSync).mockReturnValue("");
171
+ vi.mocked(dotenv.parse).mockReturnValue({});
172
+ const writeFileSyncMock = vi.mocked(fs.writeFileSync);
173
+
174
+ simulatorService.addConfigToEnvFile(newConfig);
175
+
176
+ expect(writeFileSyncMock).toHaveBeenCalledWith(`${envFilePath}.bak`, "");
177
+ const expectedUpdatedContent = `NEW_KEY=newValue`;
178
+ expect(writeFileSyncMock).toHaveBeenCalledWith(envFilePath, expectedUpdatedContent);
179
+ });
180
+
181
+ test("should throw error when .env file does not exist", () => {
182
+ vi.mocked(fs.readFileSync).mockImplementation(() => {
183
+ throw new Error("File not found");
184
+ });
185
+
186
+ expect(() => simulatorService.addConfigToEnvFile({ KEY: "value" })).toThrow(
187
+ "File not found"
188
+ );
189
+ });
190
+
191
+
163
192
  test("should open the frontend URL and return true", async () => {
164
193
  vi.spyOn(simulatorService, "getFrontendUrl").mockReturnValue("http://localhost:8080");
165
194
  const result = await simulatorService.openFrontend();
@@ -234,47 +263,14 @@ describe("SimulatorService - Basic Tests", () => {
234
263
  expect(result).toEqual({ initialized: false, errorCode: "ERROR", errorMessage: fetchError.message });
235
264
  });
236
265
 
237
- test("should throw an error if executeCommand fails and simulator location does not exist", async () => {
238
- const mockError = new Error("git clone failed");
239
- vi.mocked(executeCommand).mockRejectedValueOnce(mockError);
240
- vi.mocked(fs.existsSync).mockReturnValue(false);
241
- await expect(simulatorService.downloadSimulator()).rejects.toThrow("git clone failed");
242
- expect(executeCommand).toHaveBeenCalled();
243
- expect(fs.existsSync).toHaveBeenCalledWith("/mock/home/genlayer-simulator");
244
- });
245
-
246
266
  test("should call executeCommand if docker ps command fails", async () => {
247
267
  vi.mocked(checkCommand)
248
268
  .mockResolvedValueOnce(undefined)
249
269
 
250
-
251
270
  const result = await simulatorService.checkInstallRequirements();
252
271
  expect(result.docker).toBe(true);
253
- expect(result.git).toBe(true);
254
272
  });
255
273
 
256
- test("should update envConfig with newConfig values", () => {
257
- const envFilePath = path.join("/mock/home/genlayer-simulator", ".env");
258
- const originalEnvContent = "KEY1=value1\nKEY2=value2";
259
- const envConfig = { KEY1: "value1", KEY2: "value2" };
260
- const newConfig = { KEY2: "new_value2", KEY3: "value3" };
261
- vi.mocked(fs.readFileSync)
262
- .mockReturnValueOnce(originalEnvContent)
263
- .mockReturnValueOnce(originalEnvContent);
264
- vi.mocked(dotenv.parse).mockReturnValue(envConfig);
265
- const writeFileSyncSpy = vi.spyOn(fs, "writeFileSync");
266
- simulatorService["addConfigToEnvFile"](newConfig);
267
- expect(envConfig).toEqual({
268
- KEY1: "value1",
269
- KEY2: "new_value2",
270
- KEY3: "value3",
271
- });
272
- expect(writeFileSyncSpy).toHaveBeenCalledWith(`${envFilePath}.bak`, originalEnvContent);
273
- expect(writeFileSyncSpy).toHaveBeenCalledWith(
274
- envFilePath,
275
- "KEY1=value1\nKEY2=new_value2\nKEY3=value3"
276
- );
277
- });
278
274
 
279
275
  test("should return providers without errors", () => {
280
276
  expect(simulatorService.getAiProvidersOptions(true)).toEqual(expect.any(Array));
@@ -306,8 +302,60 @@ describe("SimulatorService - Docker Tests", () => {
306
302
  mockPing = vi.mocked(Docker.prototype.ping);
307
303
  });
308
304
 
309
- test("should pull the Ollama model", async () => {
310
- mockGetContainer.mockReturnValueOnce({exec: mockExec} as unknown as Docker.Container);
305
+ test("should handle errors during the execution of pullOllamaModel gracefully", async () => {
306
+ const mockExec = vi.fn();
307
+ const mockStart = vi.fn();
308
+
309
+ mockExec.mockResolvedValue({
310
+ start: mockStart,
311
+ });
312
+
313
+ const mockStream = {
314
+ on: vi.fn((event, callback) => {
315
+ if (event === "data") callback("Mock data chunk");
316
+ if (event === "error") callback(new Error("Mock error during stream"));
317
+ }),
318
+ };
319
+
320
+ mockStart.mockResolvedValue(mockStream);
321
+
322
+ mockGetContainer.mockReturnValueOnce({
323
+ exec: mockExec,
324
+ } as unknown as Docker.Container);
325
+
326
+ const result = await simulatorService.pullOllamaModel();
327
+
328
+ expect(result).toBe(false);
329
+ expect(mockGetContainer).toHaveBeenCalledWith("ollama");
330
+ expect(mockExec).toHaveBeenCalledWith({
331
+ Cmd: ["ollama", "pull", "llama3"],
332
+ AttachStdout: true,
333
+ AttachStderr: true,
334
+ });
335
+ expect(mockStart).toHaveBeenCalledWith({ Detach: false, Tty: false });
336
+ expect(mockStream.on).toHaveBeenCalledWith("error", expect.any(Function));
337
+ });
338
+
339
+ test("should successfully execute pullOllamaModel and return true", async () => {
340
+ const mockExec = vi.fn();
341
+ const mockStart = vi.fn();
342
+
343
+ mockExec.mockResolvedValue({
344
+ start: mockStart,
345
+ });
346
+
347
+ const mockStream = {
348
+ on: vi.fn((event, callback) => {
349
+ if (event === "data") callback("Mock data chunk");
350
+ if (event === "end") callback();
351
+ }),
352
+ };
353
+
354
+ mockStart.mockResolvedValue(mockStream);
355
+
356
+ mockGetContainer.mockReturnValueOnce({
357
+ exec: mockExec,
358
+ } as unknown as Docker.Container);
311
359
 
312
360
  const result = await simulatorService.pullOllamaModel();
313
361
 
@@ -315,9 +363,15 @@ describe("SimulatorService - Docker Tests", () => {
315
363
  expect(mockGetContainer).toHaveBeenCalledWith("ollama");
316
364
  expect(mockExec).toHaveBeenCalledWith({
317
365
  Cmd: ["ollama", "pull", "llama3"],
366
+ AttachStdout: true,
367
+ AttachStderr: true,
318
368
  });
369
+ expect(mockStart).toHaveBeenCalledWith({ Detach: false, Tty: false });
370
+ expect(mockStream.on).toHaveBeenCalledWith("data", expect.any(Function));
371
+ expect(mockStream.on).toHaveBeenCalledWith("end", expect.any(Function));
319
372
  });
320
373
 
374
+
321
375
  test("should stop and remove Docker containers with the specified prefix", async () => {
322
376
  const mockContainers = [
323
377
  {
@@ -403,11 +457,51 @@ describe("SimulatorService - Docker Tests", () => {
403
457
  expect(executeCommand).toHaveBeenCalledTimes(1);
404
458
  });
405
459
 
406
- test("should throw an unexpected error when checking docker installation requirement", async () => {
460
+ test("should call execute command again to start docker service", async () => {
407
461
  vi.mocked(checkCommand)
408
462
  .mockResolvedValueOnce(undefined)
409
463
  .mockRejectedValue(undefined);
410
- mockPing.mockRejectedValueOnce("Unexpected docker error");
411
- await expect(simulatorService.checkInstallRequirements()).rejects.toThrow("Unexpected docker error");
464
+ mockPing.mockRejectedValueOnce("");
465
+ await expect(simulatorService.checkInstallRequirements()).resolves.toStrictEqual({ docker: true });
466
+ });
467
+
468
+ test("should warn the user when an update is available", async () => {
469
+ const update = { latest: "1.1.0" };
470
+ (updateCheck as any).mockResolvedValue(update);
471
+
472
+ const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
473
+
474
+ await simulatorService.checkCliVersion();
475
+
476
+ expect(consoleWarnSpy).toHaveBeenCalledWith(
477
+ `\nA new version (${update.latest}) is available! You're using version 1.0.0.\nRun npm install -g genlayer to update\n`
478
+ );
479
+
480
+ consoleWarnSpy.mockRestore();
481
+ });
482
+
483
+ test("should not warn the user when the CLI is up-to-date", async () => {
484
+ const update = { latest: "1.0.0" };
485
+ (updateCheck as any).mockResolvedValue(update);
486
+
487
+ const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
488
+
489
+ await simulatorService.checkCliVersion();
490
+
491
+ expect(consoleWarnSpy).not.toHaveBeenCalled();
492
+
493
+ consoleWarnSpy.mockRestore();
494
+ });
495
+
496
+ test("should handle update-check returning undefined", async () => {
497
+ (updateCheck as any).mockResolvedValue(undefined);
498
+
499
+ const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
500
+
501
+ await simulatorService.checkCliVersion();
502
+
503
+ expect(consoleWarnSpy).not.toHaveBeenCalled();
504
+
505
+ consoleWarnSpy.mockRestore();
412
506
  });
413
507
  });
package/vitest.config.ts CHANGED
@@ -5,7 +5,7 @@ export default defineConfig({
5
5
  globals: true,
6
6
  environment: 'jsdom',
7
7
  coverage: {
8
- exclude: [...configDefaults.exclude, '*.js', 'tests/**/*.ts', 'src/types'],
8
+ exclude: [...configDefaults.exclude, '*.js', 'tests/**/*.ts', 'src/types', 'scripts'],
9
9
  }
10
10
  }
11
11
  });