genlayer 0.1.1 → 0.1.3

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
 
2
2
 
3
+ ## 0.1.3 (2024-11-08)
4
+
5
+ ## 0.1.2 (2024-11-08)
6
+
3
7
  ## 0.1.1 (2024-11-08)
4
8
 
5
9
  ## 0.1.0 (2024-11-08)
package/dist/index.js CHANGED
@@ -39699,7 +39699,7 @@ var {
39699
39699
  } = import_index.default;
39700
39700
 
39701
39701
  // package.json
39702
- var version = "0.1.1";
39702
+ var version = "0.1.3";
39703
39703
 
39704
39704
  // src/lib/config/text.ts
39705
39705
  var CLI_DESCRIPTION = "GenLayer CLI is a development environment for the GenLayer ecosystem. It allows developers to interact with the protocol by creating accounts, sending transactions, and working with Intelligent Contracts by testing, debugging, and deploying them.";
@@ -40302,23 +40302,24 @@ var MissingRequirementError = class extends Error {
40302
40302
  };
40303
40303
 
40304
40304
  // src/lib/clients/system.ts
40305
- var asyncExec = import_node_util5.default.promisify(import_child_process.exec);
40306
40305
  function checkCommand(command, toolName) {
40307
40306
  return __async(this, null, function* () {
40308
- const { stderr } = yield asyncExec(command);
40307
+ const { stderr } = yield import_node_util5.default.promisify(import_child_process.exec)(command);
40309
40308
  if (stderr) {
40310
40309
  throw new MissingRequirementError(toolName);
40311
40310
  }
40312
40311
  });
40313
40312
  }
40314
40313
  function executeCommand(cmdsByPlatform, toolName) {
40315
- const runningPlatform = getPlatform();
40316
- const command = cmdsByPlatform[runningPlatform];
40317
- try {
40318
- return asyncExec(command);
40319
- } catch (error) {
40320
- throw new Error(`Error executing ${toolName || command}: ${error.message}.`);
40321
- }
40314
+ return __async(this, null, function* () {
40315
+ const runningPlatform = getPlatform();
40316
+ const command = cmdsByPlatform[runningPlatform];
40317
+ try {
40318
+ return yield import_node_util5.default.promisify(import_child_process.exec)(command);
40319
+ } catch (error) {
40320
+ throw new Error(`Error executing ${toolName || command}: ${error.message}.`);
40321
+ }
40322
+ });
40322
40323
  }
40323
40324
  function getPlatform() {
40324
40325
  const currentPlatform = process.platform;
@@ -40333,7 +40334,7 @@ function openUrl(url) {
40333
40334
  function getVersion(toolName) {
40334
40335
  return __async(this, null, function* () {
40335
40336
  try {
40336
- const toolResponse = yield asyncExec(`${toolName} --version`);
40337
+ const toolResponse = yield import_node_util5.default.promisify(import_child_process.exec)(`${toolName} --version`);
40337
40338
  if (toolResponse.stderr) {
40338
40339
  throw new Error(toolResponse.stderr);
40339
40340
  }
@@ -40354,31 +40355,29 @@ function getVersion(toolName) {
40354
40355
  function listDockerContainers() {
40355
40356
  return __async(this, null, function* () {
40356
40357
  try {
40357
- const dockerResponse = yield asyncExec("docker ps -a --format '{{.Names}}'");
40358
+ const dockerResponse = yield import_node_util5.default.promisify(import_child_process.exec)("docker ps -a --format '{{.Names}}'");
40358
40359
  const dockerContainers = dockerResponse.stdout.split("\n");
40359
40360
  return dockerContainers;
40360
40361
  } catch (error) {
40361
40362
  throw new Error("Error listing Docker containers.");
40362
40363
  }
40363
- return [];
40364
40364
  });
40365
40365
  }
40366
40366
  function listDockerImages() {
40367
40367
  return __async(this, null, function* () {
40368
40368
  try {
40369
- const dockerResponse = yield asyncExec("docker images --format '{{.Repository}}'");
40369
+ const dockerResponse = yield import_node_util5.default.promisify(import_child_process.exec)("docker images --format '{{.Repository}}'");
40370
40370
  const dockerImages = dockerResponse.stdout.split("\n");
40371
40371
  return dockerImages;
40372
40372
  } catch (error) {
40373
40373
  throw new Error("Error listing Docker images.");
40374
40374
  }
40375
- return [];
40376
40375
  });
40377
40376
  }
40378
40377
  function stopDockerContainer(containerName) {
40379
40378
  return __async(this, null, function* () {
40380
40379
  try {
40381
- yield asyncExec(`docker stop ${containerName}`);
40380
+ yield import_node_util5.default.promisify(import_child_process.exec)(`docker stop ${containerName}`);
40382
40381
  } catch (error) {
40383
40382
  throw new Error(`Error stopping Docker container ${containerName}.`);
40384
40383
  }
@@ -40387,7 +40386,7 @@ function stopDockerContainer(containerName) {
40387
40386
  function removeDockerContainer(containerName) {
40388
40387
  return __async(this, null, function* () {
40389
40388
  try {
40390
- yield asyncExec(`docker rm ${containerName}`);
40389
+ yield import_node_util5.default.promisify(import_child_process.exec)(`docker rm ${containerName}`);
40391
40390
  } catch (error) {
40392
40391
  throw new Error(`Error removing container ${containerName}.`);
40393
40392
  }
@@ -40396,7 +40395,7 @@ function removeDockerContainer(containerName) {
40396
40395
  function removeDockerImage(imageName) {
40397
40396
  return __async(this, null, function* () {
40398
40397
  try {
40399
- yield asyncExec(`docker rmi ${imageName}`);
40398
+ yield import_node_util5.default.promisify(import_child_process.exec)(`docker rmi ${imageName}`);
40400
40399
  } catch (error) {
40401
40400
  throw new Error(`Error removing image ${imageName}.`);
40402
40401
  }
@@ -40569,10 +40568,11 @@ var SimulatorService = class {
40569
40568
  }
40570
40569
  waitForSimulatorToBeReady() {
40571
40570
  return __async(this, arguments, function* (retries = STARTING_TIMEOUT_ATTEMPTS) {
40571
+ var _a, _b, _c;
40572
40572
  console.log("Waiting for the simulator to start up...");
40573
40573
  try {
40574
40574
  const response = yield rpcClient.request({ method: "ping", params: [] });
40575
- if (response && (response.result.status === "OK" || response.result.data.status === "OK")) {
40575
+ if (((_a = response == null ? void 0 : response.result) == null ? void 0 : _a.status) === "OK" || ((_c = (_b = response == null ? void 0 : response.result) == null ? void 0 : _b.data) == null ? void 0 : _c.status) === "OK") {
40576
40576
  return { initialized: true };
40577
40577
  }
40578
40578
  if (retries > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genlayer",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "GenLayer Command Line Tool",
5
5
  "main": "src/index.ts",
6
6
  "bin": {
@@ -4,12 +4,9 @@ import open from "open";
4
4
 
5
5
  import {RunningPlatform, AVAILABLE_PLATFORMS} from "../config/simulator";
6
6
  import {MissingRequirementError} from "../errors/missingRequirement";
7
- import {VersionRequiredError} from "../errors/versionRequired";
8
-
9
- const asyncExec = util.promisify(exec);
10
7
 
11
8
  export async function checkCommand(command: string, toolName: string): Promise<void> {
12
- const {stderr} = await asyncExec(command);
9
+ const {stderr} = await util.promisify(exec)(command);
13
10
  if (stderr) {
14
11
  throw new MissingRequirementError(toolName);
15
12
  }
@@ -24,14 +21,14 @@ type ExecuteCommandByPlatformInput = {
24
21
  [key in RunningPlatform]: string;
25
22
  };
26
23
 
27
- export function executeCommand(
24
+ export async function executeCommand(
28
25
  cmdsByPlatform: ExecuteCommandByPlatformInput,
29
26
  toolName?: string,
30
- ): PromiseWithChild<ExecuteCommandResult> {
27
+ ): Promise<ExecuteCommandResult> {
31
28
  const runningPlatform = getPlatform();
32
29
  const command = cmdsByPlatform[runningPlatform];
33
30
  try {
34
- return asyncExec(command);
31
+ return await util.promisify(exec)(command);
35
32
  } catch (error: any) {
36
33
  throw new Error(`Error executing ${toolName || command}: ${error.message}.`);
37
34
  }
@@ -51,7 +48,7 @@ export function openUrl(url: string): Promise<ChildProcess> {
51
48
 
52
49
  export async function getVersion(toolName: string): Promise<string> {
53
50
  try {
54
- const toolResponse = await asyncExec(`${toolName} --version`);
51
+ const toolResponse = await util.promisify(exec)(`${toolName} --version`);
55
52
 
56
53
  if (toolResponse.stderr) {
57
54
  throw new Error(toolResponse.stderr);
@@ -74,29 +71,27 @@ export async function getVersion(toolName: string): Promise<string> {
74
71
 
75
72
  export async function listDockerContainers(): Promise<string[]> {
76
73
  try {
77
- const dockerResponse = await asyncExec("docker ps -a --format '{{.Names}}'");
74
+ const dockerResponse = await util.promisify(exec)("docker ps -a --format '{{.Names}}'");
78
75
  const dockerContainers = dockerResponse.stdout.split("\n");
79
76
  return dockerContainers;
80
77
  } catch (error) {
81
78
  throw new Error("Error listing Docker containers.");
82
79
  }
83
- return [];
84
80
  }
85
81
 
86
82
  export async function listDockerImages(): Promise<string[]> {
87
83
  try {
88
- const dockerResponse = await asyncExec("docker images --format '{{.Repository}}'");
84
+ const dockerResponse = await util.promisify(exec)("docker images --format '{{.Repository}}'");
89
85
  const dockerImages = dockerResponse.stdout.split("\n");
90
86
  return dockerImages;
91
87
  } catch (error) {
92
88
  throw new Error("Error listing Docker images.");
93
89
  }
94
- return [];
95
90
  }
96
91
 
97
92
  export async function stopDockerContainer(containerName: string): Promise<void> {
98
93
  try {
99
- await asyncExec(`docker stop ${containerName}`);
94
+ await util.promisify(exec)(`docker stop ${containerName}`);
100
95
  } catch (error) {
101
96
  throw new Error(`Error stopping Docker container ${containerName}.`);
102
97
  }
@@ -104,7 +99,7 @@ export async function stopDockerContainer(containerName: string): Promise<void>
104
99
 
105
100
  export async function removeDockerContainer(containerName: string) {
106
101
  try {
107
- await asyncExec(`docker rm ${containerName}`);
102
+ await util.promisify(exec)(`docker rm ${containerName}`);
108
103
  } catch (error) {
109
104
  throw new Error(`Error removing container ${containerName}.`);
110
105
  }
@@ -112,7 +107,7 @@ export async function removeDockerContainer(containerName: string) {
112
107
 
113
108
  export async function removeDockerImage(imageName: string) {
114
109
  try {
115
- await asyncExec(`docker rmi ${imageName}`);
110
+ await util.promisify(exec)(`docker rmi ${imageName}`);
116
111
  } catch (error) {
117
112
  throw new Error(`Error removing image ${imageName}.`);
118
113
  }
@@ -221,8 +221,8 @@ export class SimulatorService implements ISimulatorService {
221
221
  const response = await rpcClient.request({method: "ping", params: []});
222
222
 
223
223
  //Compatibility with current simulator version
224
- if (response && (response.result.status === "OK" || response.result.data.status === "OK")) {
225
- return {initialized: true};
224
+ if (response?.result?.status === "OK" || response?.result?.data?.status === "OK") {
225
+ return { initialized: true };
226
226
  }
227
227
  if (retries > 0) {
228
228
  await sleep(STARTING_TIMEOUT_WAIT_CYLCE);
@@ -0,0 +1,223 @@
1
+ import { describe, test, expect, vi, beforeEach } from "vitest";
2
+ import util from "node:util";
3
+ import {
4
+ checkCommand,
5
+ executeCommand,
6
+ openUrl,
7
+ getVersion,
8
+ listDockerContainers,
9
+ listDockerImages,
10
+ stopDockerContainer,
11
+ removeDockerContainer,
12
+ removeDockerImage
13
+ } from "../../src/lib/clients/system";
14
+ import { MissingRequirementError } from "../../src/lib/errors/missingRequirement";
15
+ import open from "open";
16
+
17
+ vi.mock("open");
18
+ vi.mock("util");
19
+
20
+ describe("System Functions - Success Paths", () => {
21
+ beforeEach(() => {
22
+ vi.clearAllMocks();
23
+ });
24
+
25
+ test("openUrl opens URL successfully", async () => {
26
+ const openSpy = vi.mocked(open).mockResolvedValue({} as any);
27
+ const url = "https://example.com";
28
+ await openUrl(url);
29
+ expect(openSpy).toHaveBeenCalledWith(url);
30
+ });
31
+
32
+ test("getVersion retrieves tool version", async () => {
33
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.resolve({
34
+ stdout: "git v1.2.3",
35
+ stderr: ""
36
+ }));
37
+ const version = await getVersion("git");
38
+ expect(version).toBe("1.2.3");
39
+ });
40
+
41
+ test("checkCommand verifies a command exists", async () => {
42
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.resolve({ stdout: "", stderr: "" }));
43
+ const result = await checkCommand("node --version", "node");
44
+ expect(result).toBe(undefined);
45
+ });
46
+
47
+ test("executeCommand executes a command successfully", async () => {
48
+ const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("linux");
49
+ vi.mocked(util.promisify).mockReturnValueOnce((param: string) => Promise.resolve({
50
+ stdout: param,
51
+ stderr: ""
52
+ }));
53
+ const result = await executeCommand({
54
+ linux: "echo linux",
55
+ win32: "echo win32",
56
+ darwin: "echo darwin",
57
+ },
58
+ "echo");
59
+ expect(result.stdout).toBe("echo linux");
60
+ platformSpy.mockRestore();
61
+ });
62
+
63
+ test("listDockerContainers retrieves a list of containers", async () => {
64
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.resolve({
65
+ stdout: "container1\ncontainer2",
66
+ stderr: ""
67
+ }));
68
+ const containers = await listDockerContainers();
69
+ expect(containers).toEqual(["container1", "container2"]);
70
+ });
71
+
72
+ test("listDockerImages retrieves a list of images", async () => {
73
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.resolve({
74
+ stdout: "image1\nimage2",
75
+ stderr: ""
76
+ }));
77
+ const images = await listDockerImages();
78
+ expect(images).toEqual(["image1", "image2"]);
79
+ });
80
+
81
+ test("stopDockerContainer stops a container", async () => {
82
+ const containerId = "container123";
83
+ const execMock = vi.fn().mockResolvedValue({ stdout: "", stderr: "" });
84
+ vi.mocked(util.promisify).mockReturnValue(execMock);
85
+ await stopDockerContainer(containerId);
86
+ expect(execMock).toHaveBeenCalledWith(`docker stop ${containerId}`);
87
+ });
88
+
89
+ test("removeDockerContainer removes a container", async () => {
90
+ const containerId = "container123";
91
+ const execMock = vi.fn().mockResolvedValue({ stdout: "", stderr: "" });
92
+ vi.mocked(util.promisify).mockReturnValue(execMock);
93
+ await removeDockerContainer(containerId);
94
+ expect(execMock).toHaveBeenCalledWith(`docker rm ${containerId}`);
95
+ });
96
+
97
+ test("removeDockerImage removes an image", async () => {
98
+ const imageId = "image123";
99
+ const execMock = vi.fn().mockResolvedValue({ stdout: "", stderr: "" });
100
+ vi.mocked(util.promisify).mockReturnValue(execMock);
101
+ await removeDockerImage(imageId);
102
+ expect(execMock).toHaveBeenCalledWith(`docker rmi ${imageId}`);
103
+ });
104
+ });
105
+
106
+ describe("System Functions - Error Paths", () => {
107
+ beforeEach(() => {
108
+ vi.clearAllMocks();
109
+ });
110
+
111
+ test("getVersion throws an error if the command fails", async () => {
112
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.resolve({
113
+ stdout: "",
114
+ stderr: "command not found"
115
+ }));
116
+ const toolName = "nonexistent";
117
+ await expect(getVersion(toolName)).rejects.toThrow(`Error getting ${toolName} version.`);
118
+ });
119
+
120
+ test("getVersion returns '' if stdout is empty", async () => {
121
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.resolve({
122
+ stdout: "",
123
+ stderr: ""
124
+ }));
125
+ const result = await getVersion('git');
126
+ expect(result).toBe("");
127
+ });
128
+
129
+ test("getVersion throw error if stdout undefined", async () => {
130
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.resolve({
131
+ stderr: ""
132
+ }));
133
+ const toolName = "nonexistent";
134
+ await expect(getVersion(toolName)).rejects.toThrow(`Error getting ${toolName} version.`);
135
+ });
136
+
137
+ test("checkCommand returns false if the command does not exist", async () => {
138
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.resolve({
139
+ stdout: "",
140
+ stderr: "command not found"
141
+ }));
142
+ const toolName = 'nonexistent';
143
+ await expect(checkCommand(`${toolName} --version`, toolName)).rejects.toThrow(new MissingRequirementError(toolName));
144
+ });
145
+
146
+ test("executeCommand throws an error if the command fails", async () => {
147
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.reject(new Error("Execution failed")));
148
+ await expect(executeCommand({
149
+ linux: "echo hello",
150
+ win32: "echo hello",
151
+ darwin: "echo hello",
152
+ },
153
+ "echo")).rejects.toThrow("Execution failed");
154
+ });
155
+
156
+ test("stopDockerContainer throws an error if stopping fails", async () => {
157
+ const containerId = "container123";
158
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.reject(new Error("")));
159
+ await expect(stopDockerContainer(containerId)).rejects.toThrow("Error stopping Docker container container123");
160
+ });
161
+
162
+ test("removeDockerContainer throws an error if removal fails", async () => {
163
+ const containerId = "container123";
164
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.reject(new Error("")));
165
+ await expect(removeDockerContainer(containerId)).rejects.toThrow("Error removing container container123.");
166
+ });
167
+
168
+ test("removeDockerImage throws an error if image removal fails", async () => {
169
+ const imageId = "image123";
170
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.reject(new Error("")));
171
+ await expect(removeDockerImage(imageId)).rejects.toThrow("Error removing image image123.");
172
+ });
173
+
174
+ test("throws error when command fails", async () => {
175
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.reject(new Error("")));
176
+ await expect(listDockerContainers()).rejects.toThrow("Error listing Docker containers.");
177
+ });
178
+
179
+ test("throws error when command fails", async () => {
180
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.reject(new Error("")));
181
+ await expect(listDockerImages()).rejects.toThrow("Error listing Docker images.");
182
+ });
183
+
184
+ test("throws error when command execution fails", async () => {
185
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.reject(new Error("Execution error.")));
186
+ await expect(executeCommand({
187
+ linux: "echo no toolname",
188
+ win32: "echo no toolname",
189
+ darwin: "echo no toolname",
190
+ })).rejects.toThrow(
191
+ "Error executing echo no toolname: Execution error."
192
+ );
193
+ });
194
+
195
+ test("throws error when command execution fails (toolname)", async () => {
196
+ vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.reject(new Error("Execution error.")));
197
+ await expect(executeCommand({
198
+ linux: "echo linux",
199
+ win32: "echo win32",
200
+ darwin: "echo darwin",
201
+ },
202
+ "echo")).rejects.toThrow(
203
+ "Error executing echo: Execution error."
204
+ );
205
+ });
206
+
207
+ test("throws an error for unsupported platform in executeCommand", () => {
208
+ const unsupportedPlatform = "unsupportedOS";
209
+ const originalPlatform = process.platform;
210
+ Object.defineProperty(process, "platform", {
211
+ value: unsupportedPlatform,
212
+ });
213
+ const cmdsByPlatform = {
214
+ linux: "echo Linux",
215
+ darwin: "echo macOS",
216
+ win32: "echo Windows",
217
+ };
218
+ expect(executeCommand(cmdsByPlatform)).rejects.toThrow(
219
+ `Unsupported platform: ${unsupportedPlatform}.`
220
+ );
221
+ Object.defineProperty(process, "platform", { value: originalPlatform });
222
+ });
223
+ });
@@ -0,0 +1,283 @@
1
+ import { describe, beforeEach, test, expect, vi, Mock } from "vitest";
2
+ import * as path from "path";
3
+ import * as fs from "fs";
4
+ import * as dotenv from "dotenv";
5
+ import simulatorService from "../../src/lib/services/simulator";
6
+ import {
7
+ getVersion,
8
+ executeCommand,
9
+ openUrl,
10
+ checkCommand,
11
+ stopDockerContainer,
12
+ removeDockerContainer, listDockerContainers,
13
+ } from "../../src/lib/clients/system";
14
+ import {
15
+ DOCKER_IMAGES_AND_CONTAINERS_NAME_PREFIX,
16
+ VERSION_REQUIREMENTS,
17
+ } from "../../src/lib/config/simulator";
18
+ import {
19
+ STARTING_TIMEOUT_ATTEMPTS,
20
+ DEFAULT_RUN_SIMULATOR_COMMAND,
21
+ DEFAULT_RUN_DOCKER_COMMAND,
22
+ DEFAULT_PULL_OLLAMA_COMMAND
23
+ } from "../../src/lib/config/simulator";
24
+ import { rpcClient } from "../../src/lib/clients/jsonRpcClient";
25
+ import * as semver from "semver";
26
+
27
+
28
+ vi.mock("fs");
29
+ vi.mock("path");
30
+ vi.mock("dotenv");
31
+ vi.mock("semver", () => ({
32
+ satisfies: vi.fn(),
33
+ }));
34
+ vi.mock("../../src/lib/clients/system", () => ({
35
+ checkCommand: vi.fn(),
36
+ getVersion: vi.fn(),
37
+ executeCommand: vi.fn(),
38
+ openUrl: vi.fn(),
39
+ listDockerContainers: vi.fn(),
40
+ stopDockerContainer: vi.fn(),
41
+ removeDockerContainer: vi.fn(),
42
+ }));
43
+
44
+ vi.mock("../../src/lib/clients/jsonRpcClient", () => ({
45
+ rpcClient: {
46
+ request: vi.fn(),
47
+ },
48
+ }));
49
+
50
+ describe("SimulatorService - Basic Tests", () => {
51
+ beforeEach(() => {
52
+ vi.clearAllMocks();
53
+ vi.mocked(path.join).mockImplementation((...args) => args.join("/"));
54
+ });
55
+
56
+ test("should return the correct simulator location path", () => {
57
+ const expectedPath = "/mock/home/genlayer-simulator";
58
+ simulatorService.setSimulatorLocation("/mock/home/genlayer-simulator");
59
+ const simulatorLocation = simulatorService.getSimulatorLocation();
60
+ expect(simulatorLocation).toBe(expectedPath);
61
+ });
62
+
63
+ test("should read the correct frontend URL from .env config", () => {
64
+ const mockEnvFilePath = "/mock/home/genlayer-simulator/.env";
65
+ const mockEnvContent = "FRONTEND_PORT=8080";
66
+ const mockEnvConfig = { FRONTEND_PORT: "8080" };
67
+ vi.mocked(fs.readFileSync).mockReturnValue(mockEnvContent);
68
+ vi.mocked(dotenv.parse).mockReturnValue(mockEnvConfig);
69
+ simulatorService.setSimulatorLocation("/mock/home/genlayer-simulator");
70
+ const frontendUrl = simulatorService.getFrontendUrl();
71
+ expect(frontendUrl).toBe("http://localhost:8080");
72
+ expect(fs.readFileSync).toHaveBeenCalledWith(mockEnvFilePath, "utf8");
73
+ });
74
+
75
+ test("should check version requirements and return missing versions", async () => {
76
+ vi.mocked(getVersion).mockResolvedValueOnce("12.0.0").mockResolvedValueOnce("18.0.0");
77
+ vi.mocked(semver.satisfies).mockImplementation((version, range) => {
78
+ if (range === VERSION_REQUIREMENTS.node) return version === "18.0.0";
79
+ return false;
80
+ });
81
+ const missingVersions = await simulatorService.checkVersionRequirements();
82
+ expect(missingVersions.node).toBe(VERSION_REQUIREMENTS.node);
83
+ expect(missingVersions.docker).toBe(VERSION_REQUIREMENTS.docker);
84
+ });
85
+
86
+ test("should handle error when checkVersion throws VersionRequiredError", async () => {
87
+ vi.mocked(getVersion).mockResolvedValueOnce("10.0.0");
88
+ vi.mocked(semver.satisfies).mockReturnValue(false);
89
+ await expect(simulatorService.checkVersion("14.0.0", "node")).rejects.toThrow();
90
+ });
91
+
92
+ test("should download simulator if not already installed", async () => {
93
+ const result = await simulatorService.downloadSimulator();
94
+ expect(result.wasInstalled).toBe(false);
95
+ expect(executeCommand).toHaveBeenCalled();
96
+ });
97
+
98
+ test("should skip download if simulator is already installed", async () => {
99
+ vi.mocked(executeCommand).mockRejectedValueOnce(new Error("Mocked command error"));
100
+ vi.spyOn(fs, "existsSync").mockReturnValue(true);
101
+ const result = await simulatorService.downloadSimulator();
102
+ expect(result.wasInstalled).toBe(true);
103
+ expect(executeCommand).toHaveBeenCalled();
104
+ expect(fs.existsSync).toHaveBeenCalled();
105
+ });
106
+
107
+ test("should return initialized true when simulator responds with OK", async () => {
108
+ vi.mocked(rpcClient.request).mockResolvedValueOnce({ result: {status: 'OK'} });
109
+ const result = await simulatorService.waitForSimulatorToBeReady(STARTING_TIMEOUT_ATTEMPTS);
110
+ expect(result).toEqual({ initialized: true });
111
+ expect(rpcClient.request).toHaveBeenCalledWith({ method: "ping", params: [] });
112
+ });
113
+
114
+ test("should return initialized true when simulator responds with OK (different json structure)", async () => {
115
+ vi.mocked(rpcClient.request).mockResolvedValueOnce({ result: {data: {status: 'OK'}} });
116
+ const result = await simulatorService.waitForSimulatorToBeReady(STARTING_TIMEOUT_ATTEMPTS);
117
+ expect(result).toEqual({ initialized: true });
118
+ expect(rpcClient.request).toHaveBeenCalledWith({ method: "ping", params: [] });
119
+ });
120
+
121
+ test("should return initialized false with errorCode TIMEOUT after retries", async () => {
122
+ vi.mocked(rpcClient.request).mockResolvedValue(undefined);
123
+ const result = await simulatorService.waitForSimulatorToBeReady(1);
124
+ expect(result).toEqual({ initialized: false, errorCode: "TIMEOUT" });
125
+ });
126
+
127
+ test("should return initialized false with errorCode ERROR on non-retryable error", async () => {
128
+ const nonRetryableError = new Error("Unexpected error");
129
+ vi.mocked(rpcClient.request).mockRejectedValue(nonRetryableError);
130
+ const result = await simulatorService.waitForSimulatorToBeReady(STARTING_TIMEOUT_ATTEMPTS);
131
+ expect(result).toEqual({ initialized: false, errorCode: "ERROR", errorMessage: nonRetryableError.message });
132
+ });
133
+
134
+ test("should execute the correct pull command based on simulator location", async () => {
135
+ const expectedCommand = DEFAULT_PULL_OLLAMA_COMMAND("/mock/home/genlayer-simulator");
136
+ vi.mocked(executeCommand).mockResolvedValueOnce({
137
+ stdout: "success",
138
+ stderr: "",
139
+ });
140
+ const result = await simulatorService.pullOllamaModel();
141
+ expect(result).toBe(true);
142
+ expect(executeCommand).toHaveBeenCalledWith(expectedCommand);
143
+ });
144
+
145
+ test("should execute the correct run simulator command based on simulator location", async () => {
146
+ (executeCommand as Mock).mockResolvedValue({
147
+ stdout: "Simulator started",
148
+ stderr: "",
149
+ });
150
+ const result = await simulatorService.runSimulator();
151
+ const expectedCommand = DEFAULT_RUN_SIMULATOR_COMMAND("/mock/home/genlayer-simulator");
152
+ expect(executeCommand).toHaveBeenCalledWith(expectedCommand);
153
+ expect(result).toEqual({ stdout: "Simulator started", stderr: "" });
154
+ });
155
+
156
+ test("should open the frontend URL and return true", async () => {
157
+ vi.spyOn(simulatorService, "getFrontendUrl").mockReturnValue("http://localhost:8080");
158
+ const result = await simulatorService.openFrontend();
159
+ expect(simulatorService.getFrontendUrl).toHaveBeenCalled();
160
+ expect(openUrl).toHaveBeenCalledWith("http://localhost:8080");
161
+ expect(result).toBe(true);
162
+ });
163
+
164
+ test("should call rpcClient.request with correct parameters and return the response", async () => {
165
+ const mockResponse = { success: true };
166
+ vi.mocked(rpcClient.request).mockResolvedValue(mockResponse);
167
+ const result = await simulatorService.deleteAllValidators();
168
+ expect(rpcClient.request).toHaveBeenCalledWith({ method: "delete_all_validators", params: [] });
169
+ expect(result).toBe(mockResponse);
170
+ });
171
+
172
+ test("should throw an unexpected error when checking node version requirements", async () => {
173
+ const unexpectedError = new Error("Unexpected error (node)");
174
+ vi.spyOn(simulatorService, "checkVersion").mockRejectedValueOnce(unexpectedError);
175
+ await expect(simulatorService.checkVersionRequirements()).rejects.toThrow("Unexpected error (node)");
176
+ });
177
+
178
+ test("should throw an unexpected error when checking docker version requirements", async () => {
179
+ vi.spyOn(simulatorService, "checkVersion")
180
+ .mockResolvedValueOnce(undefined)
181
+ .mockRejectedValueOnce(new Error("Unexpected error (docker)"));
182
+ await expect(simulatorService.checkVersionRequirements()).rejects.toThrow("Unexpected error (docker)");
183
+ });
184
+
185
+ test("should throw an unexpected error when checking git installation requirement", async () => {
186
+ vi.mocked(checkCommand).mockRejectedValueOnce(new Error("Unexpected git error"));
187
+ await expect(simulatorService.checkInstallRequirements()).rejects.toThrow("Unexpected git error");
188
+ const requirementsInstalled = { git: false, docker: false };
189
+ expect(requirementsInstalled.git).toBe(false);
190
+ });
191
+
192
+ test("should throw an unexpected error when checking docker installation requirement", async () => {
193
+ vi.mocked(checkCommand)
194
+ .mockResolvedValueOnce(undefined)
195
+ .mockRejectedValueOnce(new Error("Unexpected docker error"));
196
+ await expect(simulatorService.checkInstallRequirements()).rejects.toThrow("Unexpected docker error");
197
+ const requirementsInstalled = { git: false, docker: false };
198
+ expect(requirementsInstalled.docker).toBe(false);
199
+ });
200
+
201
+ test("should stop and remove Docker containers with the specified prefix", async () => {
202
+ const mockContainers = [
203
+ DOCKER_IMAGES_AND_CONTAINERS_NAME_PREFIX + "1",
204
+ DOCKER_IMAGES_AND_CONTAINERS_NAME_PREFIX + "2"
205
+ ];
206
+ vi.mocked(listDockerContainers).mockResolvedValue(mockContainers);
207
+ vi.mocked(stopDockerContainer).mockResolvedValue(undefined);
208
+ vi.mocked(removeDockerContainer).mockResolvedValue(undefined);
209
+ const result = await simulatorService.resetDockerContainers();
210
+ expect(result).toBe(true);
211
+ expect(stopDockerContainer).toHaveBeenCalledWith(DOCKER_IMAGES_AND_CONTAINERS_NAME_PREFIX + "1");
212
+ expect(stopDockerContainer).toHaveBeenCalledWith(DOCKER_IMAGES_AND_CONTAINERS_NAME_PREFIX + "2");
213
+ expect(removeDockerContainer).toHaveBeenCalledWith(DOCKER_IMAGES_AND_CONTAINERS_NAME_PREFIX + "1");
214
+ expect(removeDockerContainer).toHaveBeenCalledWith(DOCKER_IMAGES_AND_CONTAINERS_NAME_PREFIX + "2");
215
+ });
216
+
217
+ test("should retry when response is not 'OK' and reach sleep path", async () => {
218
+ vi.mocked(rpcClient.request).mockResolvedValue({ result: { status: "NOT_OK" } });
219
+ const result = await simulatorService.waitForSimulatorToBeReady(1);
220
+ expect(result).toEqual({ initialized: false, errorCode: "TIMEOUT" });
221
+ });
222
+
223
+ test("should retry on fetch error and reach sleep path", async () => {
224
+ const fetchError = new Error("Fetch Error");
225
+ fetchError.name = "FetchError";
226
+ vi.mocked(rpcClient.request).mockRejectedValue(fetchError);
227
+ const result = await simulatorService.waitForSimulatorToBeReady(1);
228
+ expect(result).toEqual({ initialized: false, errorCode: "ERROR", errorMessage: fetchError.message });
229
+ });
230
+
231
+ test("should throw an error if executeCommand fails and simulator location does not exist", async () => {
232
+ const mockError = new Error("git clone failed");
233
+ vi.mocked(executeCommand).mockRejectedValueOnce(mockError);
234
+ vi.mocked(fs.existsSync).mockReturnValue(false);
235
+ await expect(simulatorService.downloadSimulator()).rejects.toThrow("git clone failed");
236
+ expect(executeCommand).toHaveBeenCalled();
237
+ expect(fs.existsSync).toHaveBeenCalledWith("/mock/home/genlayer-simulator");
238
+ });
239
+
240
+ test("should call executeCommand if docker ps command fails", async () => {
241
+ vi.mocked(checkCommand)
242
+ .mockResolvedValueOnce(undefined)
243
+ .mockResolvedValueOnce(undefined)
244
+ .mockRejectedValueOnce(new Error("docker ps failed"));
245
+ vi.mocked(executeCommand).mockResolvedValueOnce({
246
+ stdout: '',
247
+ stderr: ''
248
+ });
249
+ const result = await simulatorService.checkInstallRequirements();
250
+ expect(executeCommand).toHaveBeenCalledWith(DEFAULT_RUN_DOCKER_COMMAND);
251
+ expect(result.docker).toBe(true);
252
+ expect(result.git).toBe(true);
253
+ });
254
+
255
+ test("should update envConfig with newConfig values", () => {
256
+ const envFilePath = path.join("/mock/home/genlayer-simulator", ".env");
257
+ const originalEnvContent = "KEY1=value1\nKEY2=value2";
258
+ const envConfig = { KEY1: "value1", KEY2: "value2" };
259
+ const newConfig = { KEY2: "new_value2", KEY3: "value3" };
260
+ vi.mocked(fs.readFileSync)
261
+ .mockReturnValueOnce(originalEnvContent)
262
+ .mockReturnValueOnce(originalEnvContent);
263
+ vi.mocked(dotenv.parse).mockReturnValue(envConfig);
264
+ const writeFileSyncSpy = vi.spyOn(fs, "writeFileSync");
265
+ simulatorService["addConfigToEnvFile"](newConfig);
266
+ expect(envConfig).toEqual({
267
+ KEY1: "value1",
268
+ KEY2: "new_value2",
269
+ KEY3: "value3",
270
+ });
271
+ expect(writeFileSyncSpy).toHaveBeenCalledWith(`${envFilePath}.bak`, originalEnvContent);
272
+ expect(writeFileSyncSpy).toHaveBeenCalledWith(
273
+ envFilePath,
274
+ "KEY1=value1\nKEY2=new_value2\nKEY3=value3"
275
+ );
276
+ });
277
+
278
+ test("should return providers without errors", () => {
279
+ expect(simulatorService.getAiProvidersOptions(true)).toEqual(expect.any(Array));
280
+ expect(simulatorService.getAiProvidersOptions(false)).toEqual(expect.any(Array));
281
+ });
282
+
283
+ });