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 +4 -0
- package/dist/index.js +19 -19
- package/package.json +1 -1
- package/src/lib/clients/system.ts +10 -15
- package/src/lib/services/simulator.ts +2 -2
- package/tests/libs/system.test.ts +223 -0
- package/tests/services/simulator.test.ts +283 -0
package/CHANGELOG.md
CHANGED
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.
|
|
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
|
|
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
|
-
|
|
40316
|
-
|
|
40317
|
-
|
|
40318
|
-
|
|
40319
|
-
|
|
40320
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
@@ -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
|
|
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
|
-
):
|
|
27
|
+
): Promise<ExecuteCommandResult> {
|
|
31
28
|
const runningPlatform = getPlatform();
|
|
32
29
|
const command = cmdsByPlatform[runningPlatform];
|
|
33
30
|
try {
|
|
34
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
+
});
|