genlayer 0.18.1 → 0.18.2-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.
- package/CHANGELOG.md +7 -0
- package/dist/index.js +49 -29
- package/package.json +1 -1
- package/src/commands/contracts/call.ts +7 -49
- package/src/commands/contracts/deploy.ts +3 -2
- package/src/commands/contracts/index.ts +36 -11
- package/src/commands/contracts/write.ts +49 -0
- package/src/commands/general/init.ts +15 -20
- package/src/commands/general/start.ts +14 -10
- package/src/commands/keygen/create.ts +2 -2
- package/tests/actions/call.test.ts +22 -86
- package/tests/actions/create.test.ts +8 -8
- package/tests/actions/init.test.ts +173 -102
- package/tests/actions/start.test.ts +48 -24
- package/tests/actions/write.test.ts +102 -0
- package/tests/commands/write.test.ts +76 -0
- package/tests/libs/baseAction.test.ts +37 -26
- package/tests/services/simulator.test.ts +98 -102
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {describe, test, vi, beforeEach, afterEach, expect} from "vitest";
|
|
2
2
|
import inquirer from "inquirer";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import {InitAction, InitActionOptions} from "../../src/commands/general/init";
|
|
4
|
+
import {SimulatorService} from "../../src/lib/services/simulator";
|
|
5
|
+
import {OllamaAction} from "../../src/commands/update/ollama";
|
|
6
6
|
|
|
7
7
|
describe("InitAction", () => {
|
|
8
8
|
let initAction: InitAction;
|
|
@@ -22,7 +22,7 @@ describe("InitAction", () => {
|
|
|
22
22
|
let getFrontendUrlSpy: ReturnType<typeof vi.spyOn>;
|
|
23
23
|
let normalizeLocalnetVersionSpy: ReturnType<typeof vi.spyOn>;
|
|
24
24
|
|
|
25
|
-
const defaultConfig = {
|
|
25
|
+
const defaultConfig = {defaultOllamaModel: "llama3"};
|
|
26
26
|
|
|
27
27
|
const defaultOptions: InitActionOptions = {
|
|
28
28
|
numValidators: 5,
|
|
@@ -44,19 +44,39 @@ describe("InitAction", () => {
|
|
|
44
44
|
vi.spyOn(initAction as any, "logError").mockImplementation(() => {});
|
|
45
45
|
vi.spyOn(initAction, "getConfig").mockReturnValue(defaultConfig);
|
|
46
46
|
checkCliVersionSpy = vi.spyOn(SimulatorService.prototype, "checkCliVersion").mockResolvedValue(undefined);
|
|
47
|
-
checkInstallRequirementsSpy = vi
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
checkInstallRequirementsSpy = vi
|
|
48
|
+
.spyOn(SimulatorService.prototype, "checkInstallRequirements")
|
|
49
|
+
.mockResolvedValue({git: true, docker: true});
|
|
50
|
+
checkVersionRequirementsSpy = vi
|
|
51
|
+
.spyOn(SimulatorService.prototype, "checkVersionRequirements")
|
|
52
|
+
.mockResolvedValue({node: "", docker: ""});
|
|
53
|
+
resetDockerContainersSpy = vi
|
|
54
|
+
.spyOn(SimulatorService.prototype, "resetDockerContainers")
|
|
55
|
+
.mockResolvedValue(undefined);
|
|
56
|
+
resetDockerImagesSpy = vi
|
|
57
|
+
.spyOn(SimulatorService.prototype, "resetDockerImages")
|
|
58
|
+
.mockResolvedValue(undefined);
|
|
51
59
|
addConfigToEnvFileSpy = vi.spyOn(SimulatorService.prototype, "addConfigToEnvFile").mockResolvedValue();
|
|
52
|
-
runSimulatorSpy = vi
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
runSimulatorSpy = vi
|
|
61
|
+
.spyOn(SimulatorService.prototype, "runSimulator")
|
|
62
|
+
.mockResolvedValue(undefined as any);
|
|
63
|
+
waitForSimulatorSpy = vi
|
|
64
|
+
.spyOn(SimulatorService.prototype, "waitForSimulatorToBeReady")
|
|
65
|
+
.mockResolvedValue({initialized: true}) as any;
|
|
66
|
+
deleteAllValidatorsSpy = vi
|
|
67
|
+
.spyOn(SimulatorService.prototype, "deleteAllValidators")
|
|
68
|
+
.mockResolvedValue(undefined);
|
|
69
|
+
createRandomValidatorsSpy = vi
|
|
70
|
+
.spyOn(SimulatorService.prototype, "createRandomValidators")
|
|
71
|
+
.mockResolvedValue(undefined) as any;
|
|
56
72
|
cleanDatabaseSpy = vi.spyOn(SimulatorService.prototype, "cleanDatabase").mockResolvedValue(true);
|
|
57
73
|
openFrontendSpy = vi.spyOn(SimulatorService.prototype, "openFrontend").mockResolvedValue(true);
|
|
58
|
-
getFrontendUrlSpy = vi
|
|
59
|
-
|
|
74
|
+
getFrontendUrlSpy = vi
|
|
75
|
+
.spyOn(SimulatorService.prototype, "getFrontendUrl")
|
|
76
|
+
.mockReturnValue("http://localhost:8080");
|
|
77
|
+
normalizeLocalnetVersionSpy = vi
|
|
78
|
+
.spyOn(SimulatorService.prototype, "normalizeLocalnetVersion")
|
|
79
|
+
.mockImplementation((v: string) => v) as any;
|
|
60
80
|
vi.spyOn(SimulatorService.prototype, "isLocalnetRunning").mockResolvedValue(false);
|
|
61
81
|
});
|
|
62
82
|
|
|
@@ -67,64 +87,69 @@ describe("InitAction", () => {
|
|
|
67
87
|
describe("Successful Execution", () => {
|
|
68
88
|
test("should show combined confirmation message when localnet is running", async () => {
|
|
69
89
|
vi.spyOn(SimulatorService.prototype, "isLocalnetRunning").mockResolvedValue(true);
|
|
70
|
-
|
|
90
|
+
|
|
71
91
|
const confirmPromptSpy = vi.spyOn(initAction as any, "confirmPrompt").mockResolvedValue(undefined);
|
|
72
|
-
|
|
92
|
+
|
|
73
93
|
inquirerPromptSpy
|
|
74
|
-
.mockResolvedValueOnce({
|
|
75
|
-
.mockResolvedValueOnce({
|
|
76
|
-
|
|
94
|
+
.mockResolvedValueOnce({selectedLlmProviders: ["openai"]})
|
|
95
|
+
.mockResolvedValueOnce({openai: "API_KEY_OPENAI"});
|
|
96
|
+
|
|
77
97
|
await initAction.execute(defaultOptions);
|
|
78
|
-
|
|
98
|
+
|
|
79
99
|
expect(confirmPromptSpy).toHaveBeenCalledWith(
|
|
80
|
-
"GenLayer Localnet is already running and this command is going to reset GenLayer docker images and containers, providers API Keys, and GenLayer database (accounts, transactions, validators and logs). Contract code (gpy files) will be kept. Do you want to continue?"
|
|
100
|
+
"GenLayer Localnet is already running and this command is going to reset GenLayer docker images and containers, providers API Keys, and GenLayer database (accounts, transactions, validators and logs). Contract code (gpy files) will be kept. Do you want to continue?",
|
|
81
101
|
);
|
|
82
102
|
});
|
|
83
|
-
|
|
103
|
+
|
|
84
104
|
test("should show standard confirmation message when localnet is not running", async () => {
|
|
85
105
|
vi.spyOn(SimulatorService.prototype, "isLocalnetRunning").mockResolvedValue(false);
|
|
86
|
-
|
|
106
|
+
|
|
87
107
|
const confirmPromptSpy = vi.spyOn(initAction as any, "confirmPrompt").mockResolvedValue(undefined);
|
|
88
|
-
|
|
108
|
+
|
|
89
109
|
inquirerPromptSpy
|
|
90
|
-
.mockResolvedValueOnce({
|
|
91
|
-
.mockResolvedValueOnce({
|
|
92
|
-
|
|
110
|
+
.mockResolvedValueOnce({selectedLlmProviders: ["openai"]})
|
|
111
|
+
.mockResolvedValueOnce({openai: "API_KEY_OPENAI"});
|
|
112
|
+
|
|
93
113
|
await initAction.execute(defaultOptions);
|
|
94
|
-
|
|
114
|
+
|
|
95
115
|
expect(confirmPromptSpy).toHaveBeenCalledWith(
|
|
96
|
-
"This command is going to reset GenLayer docker images and containers, providers API Keys, and GenLayer database (accounts, transactions, validators and logs). Contract code (gpy files) will be kept. Do you want to continue?"
|
|
116
|
+
"This command is going to reset GenLayer docker images and containers, providers API Keys, and GenLayer database (accounts, transactions, validators and logs). Contract code (gpy files) will be kept. Do you want to continue?",
|
|
97
117
|
);
|
|
98
118
|
});
|
|
99
|
-
|
|
119
|
+
|
|
100
120
|
test("executes the full flow in non-headless mode", async () => {
|
|
101
121
|
inquirerPromptSpy
|
|
102
|
-
.mockResolvedValueOnce({
|
|
103
|
-
.mockResolvedValueOnce({
|
|
104
|
-
.mockResolvedValueOnce({
|
|
105
|
-
.mockResolvedValueOnce({
|
|
122
|
+
.mockResolvedValueOnce({confirmAction: true})
|
|
123
|
+
.mockResolvedValueOnce({selectedLlmProviders: ["openai", "heuristai"]})
|
|
124
|
+
.mockResolvedValueOnce({openai: "API_KEY_OPENAI"})
|
|
125
|
+
.mockResolvedValueOnce({heuristai: "API_KEY_HEURIST"});
|
|
106
126
|
await initAction.execute(defaultOptions);
|
|
107
127
|
expect(checkCliVersionSpy).toHaveBeenCalled();
|
|
108
128
|
expect(checkInstallRequirementsSpy).toHaveBeenCalled();
|
|
109
129
|
expect(checkVersionRequirementsSpy).toHaveBeenCalled();
|
|
110
130
|
expect(resetDockerContainersSpy).toHaveBeenCalled();
|
|
111
131
|
expect(resetDockerImagesSpy).toHaveBeenCalled();
|
|
112
|
-
expect(addConfigToEnvFileSpy).toHaveBeenCalledWith({
|
|
113
|
-
|
|
132
|
+
expect(addConfigToEnvFileSpy).toHaveBeenCalledWith({
|
|
133
|
+
OPENAIKEY: "API_KEY_OPENAI",
|
|
134
|
+
HEURISTAIAPIKEY: "API_KEY_HEURIST",
|
|
135
|
+
});
|
|
136
|
+
expect(addConfigToEnvFileSpy).toHaveBeenCalledWith({LOCALNETVERSION: "v1.0.0"});
|
|
114
137
|
expect(runSimulatorSpy).toHaveBeenCalled();
|
|
115
138
|
expect(waitForSimulatorSpy).toHaveBeenCalled();
|
|
116
139
|
expect(deleteAllValidatorsSpy).toHaveBeenCalled();
|
|
117
140
|
expect(createRandomValidatorsSpy).toHaveBeenCalledWith(5, ["openai", "heuristai"]);
|
|
118
141
|
expect(getFrontendUrlSpy).toHaveBeenCalled();
|
|
119
142
|
expect(openFrontendSpy).toHaveBeenCalled();
|
|
120
|
-
expect((initAction as any).succeedSpinner).toHaveBeenCalledWith(
|
|
143
|
+
expect((initAction as any).succeedSpinner).toHaveBeenCalledWith(
|
|
144
|
+
"GenLayer Localnet initialized successfully! Go to http://localhost:8080 in your browser to access it.",
|
|
145
|
+
);
|
|
121
146
|
});
|
|
122
147
|
|
|
123
148
|
test("executes correctly in headless mode with DB reset and 'ollama' selected", async () => {
|
|
124
149
|
inquirerPromptSpy
|
|
125
|
-
.mockResolvedValueOnce({
|
|
126
|
-
.mockResolvedValueOnce({
|
|
127
|
-
.mockResolvedValueOnce({
|
|
150
|
+
.mockResolvedValueOnce({confirmAction: true})
|
|
151
|
+
.mockResolvedValueOnce({selectedLlmProviders: ["openai", "ollama"]})
|
|
152
|
+
.mockResolvedValueOnce({openai: "API_KEY_OPENAI"});
|
|
128
153
|
const ollamaUpdateSpy = vi.spyOn(OllamaAction.prototype, "updateModel").mockResolvedValue(undefined);
|
|
129
154
|
const headlessOptions: InitActionOptions = {
|
|
130
155
|
numValidators: 5,
|
|
@@ -136,7 +161,9 @@ describe("InitAction", () => {
|
|
|
136
161
|
await initAction.execute(headlessOptions);
|
|
137
162
|
expect(cleanDatabaseSpy).toHaveBeenCalled();
|
|
138
163
|
expect(openFrontendSpy).not.toHaveBeenCalled();
|
|
139
|
-
expect((initAction as any).succeedSpinner).toHaveBeenCalledWith(
|
|
164
|
+
expect((initAction as any).succeedSpinner).toHaveBeenCalledWith(
|
|
165
|
+
"GenLayer Localnet initialized successfully! ",
|
|
166
|
+
);
|
|
140
167
|
expect(ollamaUpdateSpy).toHaveBeenCalledWith("llama3");
|
|
141
168
|
});
|
|
142
169
|
|
|
@@ -144,12 +171,12 @@ describe("InitAction", () => {
|
|
|
144
171
|
const customVersion = "custom-v1";
|
|
145
172
|
normalizeLocalnetVersionSpy.mockReturnValue(customVersion);
|
|
146
173
|
inquirerPromptSpy
|
|
147
|
-
.mockResolvedValueOnce({
|
|
148
|
-
.mockResolvedValueOnce({
|
|
149
|
-
.mockResolvedValueOnce({
|
|
150
|
-
await initAction.execute({
|
|
174
|
+
.mockResolvedValueOnce({confirmAction: true})
|
|
175
|
+
.mockResolvedValueOnce({selectedLlmProviders: ["openai"]})
|
|
176
|
+
.mockResolvedValueOnce({openai: "API_KEY_OPENAI"});
|
|
177
|
+
await initAction.execute({...defaultOptions, localnetVersion: customVersion});
|
|
151
178
|
expect(normalizeLocalnetVersionSpy).toHaveBeenCalledWith(customVersion);
|
|
152
|
-
expect(addConfigToEnvFileSpy).toHaveBeenCalledWith({
|
|
179
|
+
expect(addConfigToEnvFileSpy).toHaveBeenCalledWith({LOCALNETVERSION: customVersion});
|
|
153
180
|
});
|
|
154
181
|
|
|
155
182
|
test("should set defaultOllamaModel to 'llama3' if not provided in config", async () => {
|
|
@@ -157,21 +184,21 @@ describe("InitAction", () => {
|
|
|
157
184
|
const writeConfigSpy = vi.spyOn(initAction, "writeConfig").mockImplementation(() => {});
|
|
158
185
|
const ollamaUpdateSpy = vi.spyOn(OllamaAction.prototype, "updateModel").mockResolvedValue(undefined);
|
|
159
186
|
inquirerPromptSpy
|
|
160
|
-
.mockResolvedValueOnce({
|
|
161
|
-
.mockResolvedValueOnce({
|
|
162
|
-
.mockResolvedValueOnce({
|
|
187
|
+
.mockResolvedValueOnce({confirmAction: true})
|
|
188
|
+
.mockResolvedValueOnce({selectedLlmProviders: ["ollama"]})
|
|
189
|
+
.mockResolvedValueOnce({ollama: "API_KEY_OLLAMA"});
|
|
163
190
|
await initAction.execute(defaultOptions);
|
|
164
191
|
expect(writeConfigSpy).toHaveBeenCalledWith("defaultOllamaModel", "llama3");
|
|
165
192
|
expect(ollamaUpdateSpy).toHaveBeenCalledWith("llama3");
|
|
166
193
|
});
|
|
167
194
|
|
|
168
195
|
test("validates API key input for configurable provider", async () => {
|
|
169
|
-
inquirerPromptSpy.mockResolvedValueOnce({
|
|
170
|
-
inquirerPromptSpy.mockResolvedValueOnce({
|
|
196
|
+
inquirerPromptSpy.mockResolvedValueOnce({confirmAction: true});
|
|
197
|
+
inquirerPromptSpy.mockResolvedValueOnce({selectedLlmProviders: ["openai"]});
|
|
171
198
|
let capturedQuestion: any;
|
|
172
199
|
inquirerPromptSpy.mockImplementationOnce((questions: any) => {
|
|
173
200
|
capturedQuestion = questions[0];
|
|
174
|
-
return Promise.resolve({
|
|
201
|
+
return Promise.resolve({openai: "dummy-key"});
|
|
175
202
|
});
|
|
176
203
|
await initAction.execute(defaultOptions);
|
|
177
204
|
expect(capturedQuestion).toBeDefined();
|
|
@@ -183,12 +210,12 @@ describe("InitAction", () => {
|
|
|
183
210
|
test("validates LLM provider selection prompt", async () => {
|
|
184
211
|
let capturedQuestion: any;
|
|
185
212
|
inquirerPromptSpy
|
|
186
|
-
.mockResolvedValueOnce({
|
|
213
|
+
.mockResolvedValueOnce({confirmAction: true})
|
|
187
214
|
.mockImplementationOnce((questions: any) => {
|
|
188
215
|
capturedQuestion = questions[0];
|
|
189
|
-
return Promise.resolve({
|
|
216
|
+
return Promise.resolve({selectedLlmProviders: ["openai"]});
|
|
190
217
|
})
|
|
191
|
-
.mockResolvedValueOnce({
|
|
218
|
+
.mockResolvedValueOnce({openai: "API_KEY_OPENAI"});
|
|
192
219
|
await initAction.execute(defaultOptions);
|
|
193
220
|
expect(capturedQuestion.validate([])).toBe("You must choose at least one option.");
|
|
194
221
|
expect(capturedQuestion.validate(["openai"])).toBe(true);
|
|
@@ -221,127 +248,171 @@ describe("InitAction", () => {
|
|
|
221
248
|
|
|
222
249
|
describe("Error Handling", () => {
|
|
223
250
|
test("fails if Docker is not installed", async () => {
|
|
224
|
-
checkInstallRequirementsSpy.mockResolvedValue({
|
|
251
|
+
checkInstallRequirementsSpy.mockResolvedValue({git: true, docker: false});
|
|
225
252
|
await initAction.execute(defaultOptions);
|
|
226
|
-
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
253
|
+
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
254
|
+
"Docker is not installed. Please install Docker and try again.\n",
|
|
255
|
+
);
|
|
227
256
|
});
|
|
228
257
|
|
|
229
258
|
test("fails if checkInstallRequirements throws an error", async () => {
|
|
230
259
|
const error = new Error("Install error");
|
|
231
260
|
checkInstallRequirementsSpy.mockRejectedValue(error);
|
|
232
261
|
await initAction.execute(defaultOptions);
|
|
233
|
-
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
262
|
+
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
263
|
+
"An error occurred during initialization.",
|
|
264
|
+
error,
|
|
265
|
+
);
|
|
234
266
|
});
|
|
235
267
|
|
|
236
268
|
test("fails if version requirements are not met (both docker and node)", async () => {
|
|
237
269
|
const version = "99.9.9";
|
|
238
|
-
checkVersionRequirementsSpy.mockResolvedValue({
|
|
270
|
+
checkVersionRequirementsSpy.mockResolvedValue({docker: version, node: version});
|
|
239
271
|
await initAction.execute(defaultOptions);
|
|
240
|
-
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
272
|
+
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
273
|
+
`Docker version ${version} or higher is required. Please update Docker and try again.\nNode version ${version} or higher is required. Please update Node and try again.\n`,
|
|
274
|
+
);
|
|
241
275
|
});
|
|
242
276
|
|
|
243
277
|
test("fails if version requirement for docker is not met", async () => {
|
|
244
278
|
const version = "99.9.9";
|
|
245
|
-
checkVersionRequirementsSpy.mockResolvedValue({
|
|
279
|
+
checkVersionRequirementsSpy.mockResolvedValue({docker: version});
|
|
246
280
|
await initAction.execute(defaultOptions);
|
|
247
|
-
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
281
|
+
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
282
|
+
`Docker version ${version} or higher is required. Please update Docker and try again.\n`,
|
|
283
|
+
);
|
|
248
284
|
});
|
|
249
285
|
|
|
250
286
|
test("fails if version requirement for node is not met", async () => {
|
|
251
287
|
const version = "99.9.9";
|
|
252
|
-
checkVersionRequirementsSpy.mockResolvedValue({
|
|
288
|
+
checkVersionRequirementsSpy.mockResolvedValue({node: version});
|
|
253
289
|
await initAction.execute(defaultOptions);
|
|
254
|
-
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
290
|
+
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
291
|
+
`Node version ${version} or higher is required. Please update Node and try again.\n`,
|
|
292
|
+
);
|
|
255
293
|
});
|
|
256
294
|
|
|
257
295
|
test("aborts if user does not confirm reset action", async () => {
|
|
258
|
-
inquirerPromptSpy.mockResolvedValueOnce({
|
|
259
|
-
await initAction.execute(defaultOptions)
|
|
296
|
+
inquirerPromptSpy.mockResolvedValueOnce({confirmAction: false});
|
|
297
|
+
await initAction.execute(defaultOptions);
|
|
260
298
|
expect((initAction as any).logError).toHaveBeenCalledWith(`Operation aborted!`);
|
|
261
299
|
});
|
|
262
300
|
|
|
263
301
|
test("fails if resetDockerContainers throws an error", async () => {
|
|
264
|
-
inquirerPromptSpy.mockResolvedValueOnce({
|
|
302
|
+
inquirerPromptSpy.mockResolvedValueOnce({confirmAction: true});
|
|
265
303
|
resetDockerContainersSpy.mockRejectedValue(new Error("Container reset error"));
|
|
266
304
|
await initAction.execute(defaultOptions);
|
|
267
|
-
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
305
|
+
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
306
|
+
"An error occurred during initialization.",
|
|
307
|
+
new Error("Container reset error"),
|
|
308
|
+
);
|
|
268
309
|
});
|
|
269
310
|
|
|
270
311
|
test("fails if runSimulator throws an error", async () => {
|
|
271
312
|
inquirerPromptSpy
|
|
272
|
-
.mockResolvedValueOnce({
|
|
273
|
-
.mockResolvedValueOnce({
|
|
274
|
-
.mockResolvedValueOnce({
|
|
313
|
+
.mockResolvedValueOnce({confirmAction: true})
|
|
314
|
+
.mockResolvedValueOnce({selectedLlmProviders: ["openai"]})
|
|
315
|
+
.mockResolvedValueOnce({openai: "API_KEY_OPENAI"});
|
|
275
316
|
runSimulatorSpy.mockRejectedValue(new Error("Run simulator error"));
|
|
276
317
|
await initAction.execute(defaultOptions);
|
|
277
|
-
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
318
|
+
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
319
|
+
"An error occurred during initialization.",
|
|
320
|
+
new Error("Run simulator error"),
|
|
321
|
+
);
|
|
278
322
|
});
|
|
279
323
|
|
|
280
324
|
test("fails if waitForSimulatorToBeReady returns ERROR code", async () => {
|
|
281
325
|
inquirerPromptSpy
|
|
282
|
-
.mockResolvedValueOnce({
|
|
283
|
-
.mockResolvedValueOnce({
|
|
284
|
-
.mockResolvedValueOnce({
|
|
285
|
-
waitForSimulatorSpy.mockResolvedValue({
|
|
326
|
+
.mockResolvedValueOnce({confirmAction: true})
|
|
327
|
+
.mockResolvedValueOnce({selectedLlmProviders: ["openai"]})
|
|
328
|
+
.mockResolvedValueOnce({openai: "API_KEY_OPENAI"});
|
|
329
|
+
waitForSimulatorSpy.mockResolvedValue({
|
|
330
|
+
initialized: false,
|
|
331
|
+
errorCode: "ERROR",
|
|
332
|
+
errorMessage: "Initialization failed",
|
|
333
|
+
});
|
|
286
334
|
await initAction.execute(defaultOptions);
|
|
287
|
-
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
335
|
+
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
336
|
+
"Unable to initialize the GenLayer Localnet: Initialization failed",
|
|
337
|
+
);
|
|
288
338
|
});
|
|
289
339
|
|
|
290
340
|
test("fails if waitForSimulatorToBeReady returns TIMEOUT code", async () => {
|
|
291
341
|
inquirerPromptSpy
|
|
292
|
-
.mockResolvedValueOnce({
|
|
293
|
-
.mockResolvedValueOnce({
|
|
294
|
-
.mockResolvedValueOnce({
|
|
295
|
-
waitForSimulatorSpy.mockResolvedValue({
|
|
342
|
+
.mockResolvedValueOnce({confirmAction: true})
|
|
343
|
+
.mockResolvedValueOnce({selectedLlmProviders: ["openai"]})
|
|
344
|
+
.mockResolvedValueOnce({openai: "API_KEY_OPENAI"});
|
|
345
|
+
waitForSimulatorSpy.mockResolvedValue({
|
|
346
|
+
initialized: false,
|
|
347
|
+
errorCode: "TIMEOUT",
|
|
348
|
+
errorMessage: "Timeout",
|
|
349
|
+
});
|
|
296
350
|
await initAction.execute(defaultOptions);
|
|
297
|
-
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
351
|
+
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
352
|
+
"The localnet is taking too long to initialize. Please try again after the localnet is ready.",
|
|
353
|
+
);
|
|
298
354
|
});
|
|
299
355
|
|
|
300
356
|
test("fails if deleteAllValidators throws an error", async () => {
|
|
301
357
|
inquirerPromptSpy
|
|
302
|
-
.mockResolvedValueOnce({
|
|
303
|
-
.mockResolvedValueOnce({
|
|
304
|
-
.mockResolvedValueOnce({
|
|
358
|
+
.mockResolvedValueOnce({confirmAction: true})
|
|
359
|
+
.mockResolvedValueOnce({selectedLlmProviders: ["openai"]})
|
|
360
|
+
.mockResolvedValueOnce({openai: "API_KEY_OPENAI"});
|
|
305
361
|
deleteAllValidatorsSpy.mockRejectedValue(new Error("Validator deletion error"));
|
|
306
362
|
await initAction.execute(defaultOptions);
|
|
307
|
-
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
363
|
+
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
364
|
+
"An error occurred during initialization.",
|
|
365
|
+
expect.any(Error),
|
|
366
|
+
);
|
|
308
367
|
});
|
|
309
368
|
|
|
310
369
|
test("fails if createRandomValidators throws an error", async () => {
|
|
311
370
|
inquirerPromptSpy
|
|
312
|
-
.mockResolvedValueOnce({
|
|
313
|
-
.mockResolvedValueOnce({
|
|
314
|
-
.mockResolvedValueOnce({
|
|
371
|
+
.mockResolvedValueOnce({confirmAction: true})
|
|
372
|
+
.mockResolvedValueOnce({selectedLlmProviders: ["openai"]})
|
|
373
|
+
.mockResolvedValueOnce({openai: "API_KEY_OPENAI"});
|
|
315
374
|
createRandomValidatorsSpy.mockRejectedValue(new Error("Validator creation error"));
|
|
316
375
|
await initAction.execute(defaultOptions);
|
|
317
|
-
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
376
|
+
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
377
|
+
"An error occurred during initialization.",
|
|
378
|
+
Error("Validator creation error"),
|
|
379
|
+
);
|
|
318
380
|
});
|
|
319
381
|
|
|
320
382
|
test("fails if cleanDatabase throws an error when resetDb is true", async () => {
|
|
321
383
|
inquirerPromptSpy
|
|
322
|
-
.mockResolvedValueOnce({
|
|
323
|
-
.mockResolvedValueOnce({
|
|
324
|
-
.mockResolvedValueOnce({
|
|
384
|
+
.mockResolvedValueOnce({confirmAction: true})
|
|
385
|
+
.mockResolvedValueOnce({selectedLlmProviders: ["openai"]})
|
|
386
|
+
.mockResolvedValueOnce({openai: "API_KEY_OPENAI"});
|
|
325
387
|
cleanDatabaseSpy.mockRejectedValue(new Error("Database error"));
|
|
326
|
-
const optionsWithResetDb: InitActionOptions = {
|
|
388
|
+
const optionsWithResetDb: InitActionOptions = {...defaultOptions, resetDb: true};
|
|
327
389
|
await initAction.execute(optionsWithResetDb);
|
|
328
|
-
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
390
|
+
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
391
|
+
"An error occurred during initialization.",
|
|
392
|
+
new Error("Database error"),
|
|
393
|
+
);
|
|
329
394
|
});
|
|
330
395
|
|
|
331
396
|
test("fails if openFrontend throws an error", async () => {
|
|
332
397
|
inquirerPromptSpy
|
|
333
|
-
.mockResolvedValueOnce({
|
|
334
|
-
.mockResolvedValueOnce({
|
|
335
|
-
.mockResolvedValueOnce({
|
|
398
|
+
.mockResolvedValueOnce({confirmAction: true})
|
|
399
|
+
.mockResolvedValueOnce({selectedLlmProviders: ["openai"]})
|
|
400
|
+
.mockResolvedValueOnce({openai: "API_KEY_OPENAI"});
|
|
336
401
|
openFrontendSpy.mockRejectedValue(new Error("Frontend error"));
|
|
337
402
|
await initAction.execute(defaultOptions);
|
|
338
|
-
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
403
|
+
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
404
|
+
"An error occurred during initialization.",
|
|
405
|
+
new Error("Frontend error"),
|
|
406
|
+
);
|
|
339
407
|
});
|
|
340
408
|
|
|
341
409
|
test("catches and logs unexpected errors", async () => {
|
|
342
410
|
inquirerPromptSpy.mockRejectedValueOnce(new Error("Unexpected prompt error"));
|
|
343
411
|
await initAction.execute(defaultOptions);
|
|
344
|
-
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
412
|
+
expect((initAction as any).failSpinner).toHaveBeenCalledWith(
|
|
413
|
+
"An error occurred during initialization.",
|
|
414
|
+
new Error("Unexpected prompt error"),
|
|
415
|
+
);
|
|
345
416
|
});
|
|
346
417
|
});
|
|
347
|
-
});
|
|
418
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {describe, beforeEach, afterEach, test, expect, vi, Mock} from "vitest";
|
|
2
2
|
import inquirer from "inquirer";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import {StartAction, StartActionOptions} from "../../src/commands/general/start";
|
|
4
|
+
import {SimulatorService} from "../../src/lib/services/simulator";
|
|
5
5
|
|
|
6
6
|
vi.mock("../../src/lib/services/simulator");
|
|
7
7
|
vi.mock("inquirer");
|
|
@@ -18,7 +18,7 @@ describe("StartAction", () => {
|
|
|
18
18
|
startAction = new StartAction();
|
|
19
19
|
startAction["simulatorService"] = mockSimulatorService;
|
|
20
20
|
|
|
21
|
-
mockSimulatorService.waitForSimulatorToBeReady = vi.fn().mockResolvedValue({
|
|
21
|
+
mockSimulatorService.waitForSimulatorToBeReady = vi.fn().mockResolvedValue({initialized: true});
|
|
22
22
|
mockSimulatorService.stopDockerContainers = vi.fn().mockResolvedValue(undefined);
|
|
23
23
|
mockSimulatorService.getAiProvidersOptions = vi.fn().mockResolvedValue(undefined);
|
|
24
24
|
|
|
@@ -43,9 +43,9 @@ describe("StartAction", () => {
|
|
|
43
43
|
|
|
44
44
|
test("should check if localnet is running and proceed without confirmation when not running", async () => {
|
|
45
45
|
mockSimulatorService.isLocalnetRunning = vi.fn().mockResolvedValue(false);
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
await startAction.execute(defaultOptions);
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
expect(mockSimulatorService.isLocalnetRunning).toHaveBeenCalled();
|
|
50
50
|
expect(mockConfirmPrompt).not.toHaveBeenCalled();
|
|
51
51
|
expect(mockSimulatorService.runSimulator).toHaveBeenCalled();
|
|
@@ -53,11 +53,13 @@ describe("StartAction", () => {
|
|
|
53
53
|
|
|
54
54
|
test("should prompt for confirmation when localnet is already running", async () => {
|
|
55
55
|
mockSimulatorService.isLocalnetRunning = vi.fn().mockResolvedValue(true);
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
await startAction.execute(defaultOptions);
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
expect(mockSimulatorService.isLocalnetRunning).toHaveBeenCalled();
|
|
60
|
-
expect(mockConfirmPrompt).toHaveBeenCalledWith(
|
|
60
|
+
expect(mockConfirmPrompt).toHaveBeenCalledWith(
|
|
61
|
+
"GenLayer Localnet is already running. Do you want to proceed?",
|
|
62
|
+
);
|
|
61
63
|
expect(mockSimulatorService.runSimulator).toHaveBeenCalled();
|
|
62
64
|
});
|
|
63
65
|
|
|
@@ -71,13 +73,17 @@ describe("StartAction", () => {
|
|
|
71
73
|
expect(startAction["startSpinner"]).toHaveBeenCalledWith("Checking CLI version...");
|
|
72
74
|
expect(mockSimulatorService.checkCliVersion).toHaveBeenCalled();
|
|
73
75
|
|
|
74
|
-
expect(startAction["setSpinnerText"]).toHaveBeenCalledWith(
|
|
76
|
+
expect(startAction["setSpinnerText"]).toHaveBeenCalledWith(
|
|
77
|
+
"Starting GenLayer Localnet (keeping the existing validators)...",
|
|
78
|
+
);
|
|
75
79
|
expect(mockSimulatorService.runSimulator).toHaveBeenCalled();
|
|
76
80
|
|
|
77
81
|
expect(startAction["setSpinnerText"]).toHaveBeenCalledWith("Waiting for the simulator to be ready...");
|
|
78
82
|
expect(mockSimulatorService.waitForSimulatorToBeReady).toHaveBeenCalled();
|
|
79
83
|
|
|
80
|
-
expect(startAction["succeedSpinner"]).toHaveBeenCalledWith(
|
|
84
|
+
expect(startAction["succeedSpinner"]).toHaveBeenCalledWith(
|
|
85
|
+
"GenLayer simulator initialized successfully! Go to http://localhost:8080 in your browser to access it.",
|
|
86
|
+
);
|
|
81
87
|
});
|
|
82
88
|
|
|
83
89
|
test("should fail when simulator fails to start", async () => {
|
|
@@ -90,15 +96,20 @@ describe("StartAction", () => {
|
|
|
90
96
|
});
|
|
91
97
|
|
|
92
98
|
test("should fail when waiting for simulator initialization times out", async () => {
|
|
93
|
-
(mockSimulatorService.waitForSimulatorToBeReady as Mock).mockResolvedValue({
|
|
99
|
+
(mockSimulatorService.waitForSimulatorToBeReady as Mock).mockResolvedValue({
|
|
100
|
+
initialized: false,
|
|
101
|
+
errorCode: "TIMEOUT",
|
|
102
|
+
});
|
|
94
103
|
|
|
95
104
|
await startAction.execute(defaultOptions);
|
|
96
105
|
|
|
97
|
-
expect(startAction["failSpinner"]).toHaveBeenCalledWith(
|
|
106
|
+
expect(startAction["failSpinner"]).toHaveBeenCalledWith(
|
|
107
|
+
"The simulator is taking too long to initialize. Please try again later.",
|
|
108
|
+
);
|
|
98
109
|
});
|
|
99
110
|
|
|
100
111
|
test("should reset the database if resetDb is true", async () => {
|
|
101
|
-
const options: StartActionOptions = {
|
|
112
|
+
const options: StartActionOptions = {...defaultOptions, resetDb: true};
|
|
102
113
|
|
|
103
114
|
mockSimulatorService.cleanDatabase = vi.fn().mockResolvedValue(undefined);
|
|
104
115
|
|
|
@@ -109,13 +120,13 @@ describe("StartAction", () => {
|
|
|
109
120
|
});
|
|
110
121
|
|
|
111
122
|
test("should initialize validators when resetValidators is true", async () => {
|
|
112
|
-
const options: StartActionOptions = {
|
|
123
|
+
const options: StartActionOptions = {...defaultOptions, resetValidators: true};
|
|
113
124
|
|
|
114
125
|
mockSimulatorService.deleteAllValidators = vi.fn().mockResolvedValue(undefined);
|
|
115
126
|
mockSimulatorService.createRandomValidators = vi.fn().mockResolvedValue(undefined);
|
|
116
127
|
mockSimulatorService.getAiProvidersOptions = vi.fn().mockReturnValue(["Provider1", "Provider2"]);
|
|
117
128
|
|
|
118
|
-
vi.mocked(inquirer.prompt).mockResolvedValue({
|
|
129
|
+
vi.mocked(inquirer.prompt).mockResolvedValue({selectedLlmProviders: ["Provider1"]});
|
|
119
130
|
|
|
120
131
|
await startAction.execute(options);
|
|
121
132
|
|
|
@@ -125,13 +136,18 @@ describe("StartAction", () => {
|
|
|
125
136
|
});
|
|
126
137
|
|
|
127
138
|
test("should fail when initializing validators fails", async () => {
|
|
128
|
-
const options: StartActionOptions = {
|
|
139
|
+
const options: StartActionOptions = {...defaultOptions, resetValidators: true};
|
|
129
140
|
|
|
130
|
-
mockSimulatorService.deleteAllValidators = vi
|
|
141
|
+
mockSimulatorService.deleteAllValidators = vi
|
|
142
|
+
.fn()
|
|
143
|
+
.mockRejectedValue(new Error("Failed to delete validators"));
|
|
131
144
|
|
|
132
145
|
await startAction.execute(options);
|
|
133
146
|
|
|
134
|
-
expect(startAction["failSpinner"]).toHaveBeenCalledWith(
|
|
147
|
+
expect(startAction["failSpinner"]).toHaveBeenCalledWith(
|
|
148
|
+
"Unable to initialize the validators",
|
|
149
|
+
expect.any(Error),
|
|
150
|
+
);
|
|
135
151
|
});
|
|
136
152
|
|
|
137
153
|
test("should open frontend when not in headless mode", async () => {
|
|
@@ -165,7 +181,9 @@ describe("StartAction", () => {
|
|
|
165
181
|
|
|
166
182
|
await startAction.execute(defaultOptions);
|
|
167
183
|
|
|
168
|
-
expect(startAction["failSpinner"]).toHaveBeenCalledWith(
|
|
184
|
+
expect(startAction["failSpinner"]).toHaveBeenCalledWith(
|
|
185
|
+
"The simulator is taking too long to initialize. Please try again later.",
|
|
186
|
+
);
|
|
169
187
|
});
|
|
170
188
|
|
|
171
189
|
test("should log error message if simulator fails to initialize with ERROR code", async () => {
|
|
@@ -177,7 +195,10 @@ describe("StartAction", () => {
|
|
|
177
195
|
|
|
178
196
|
await startAction.execute(defaultOptions);
|
|
179
197
|
|
|
180
|
-
expect(startAction["failSpinner"]).toHaveBeenCalledWith(
|
|
198
|
+
expect(startAction["failSpinner"]).toHaveBeenCalledWith(
|
|
199
|
+
"Unable to initialize the GenLayer simulator.",
|
|
200
|
+
"Initialization failed",
|
|
201
|
+
);
|
|
181
202
|
});
|
|
182
203
|
|
|
183
204
|
test("catches and logs error if waitForSimulatorToBeReady throws an exception", async () => {
|
|
@@ -186,14 +207,17 @@ describe("StartAction", () => {
|
|
|
186
207
|
|
|
187
208
|
await startAction.execute(defaultOptions);
|
|
188
209
|
|
|
189
|
-
expect(startAction["failSpinner"]).toHaveBeenCalledWith(
|
|
210
|
+
expect(startAction["failSpinner"]).toHaveBeenCalledWith(
|
|
211
|
+
"Error waiting for the simulator to be ready",
|
|
212
|
+
errorMsg,
|
|
213
|
+
);
|
|
190
214
|
});
|
|
191
215
|
|
|
192
216
|
test("should not append frontend URL when in headless mode", async () => {
|
|
193
|
-
await startAction.execute({
|
|
217
|
+
await startAction.execute({...defaultOptions, headless: true});
|
|
194
218
|
|
|
195
219
|
expect(startAction["succeedSpinner"]).toHaveBeenCalledWith(
|
|
196
|
-
"GenLayer simulator initialized successfully! "
|
|
220
|
+
"GenLayer simulator initialized successfully! ",
|
|
197
221
|
);
|
|
198
222
|
});
|
|
199
223
|
|