genlayer 0.12.1 → 0.12.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.
Files changed (44) hide show
  1. package/.github/workflows/publish-beta.yml +2 -2
  2. package/.github/workflows/publish.yml +2 -2
  3. package/.github/workflows/validate-code.yml +1 -1
  4. package/CHANGELOG.md +6 -0
  5. package/dist/index.js +24736 -108331
  6. package/docker-compose.yml +1 -1
  7. package/esbuild.config.dev.js +18 -0
  8. package/esbuild.config.js +10 -5
  9. package/esbuild.config.prod.js +16 -0
  10. package/eslint.config.js +59 -0
  11. package/package.json +12 -9
  12. package/scripts/postinstall.js +10 -6
  13. package/src/commands/config/getSetReset.ts +25 -18
  14. package/src/commands/contracts/deploy.ts +105 -25
  15. package/src/commands/contracts/index.ts +5 -1
  16. package/src/commands/general/index.ts +10 -4
  17. package/src/commands/general/init.ts +136 -189
  18. package/src/commands/general/start.ts +76 -77
  19. package/src/commands/general/stop.ts +6 -5
  20. package/src/commands/keygen/create.ts +9 -11
  21. package/src/commands/update/index.ts +3 -8
  22. package/src/commands/update/ollama.ts +56 -56
  23. package/src/commands/validators/validators.ts +48 -55
  24. package/src/lib/actions/BaseAction.ts +75 -4
  25. package/src/lib/config/simulator.ts +1 -1
  26. package/src/lib/services/simulator.ts +3 -2
  27. package/tests/actions/create.test.ts +18 -30
  28. package/tests/actions/deploy.test.ts +200 -30
  29. package/tests/actions/getSetReset.test.ts +29 -42
  30. package/tests/actions/init.test.ts +240 -475
  31. package/tests/actions/ollama.test.ts +40 -55
  32. package/tests/actions/start.test.ts +107 -108
  33. package/tests/actions/stop.test.ts +23 -4
  34. package/tests/actions/validators.test.ts +273 -142
  35. package/tests/commands/call.test.ts +4 -1
  36. package/tests/commands/deploy.test.ts +11 -0
  37. package/tests/commands/init.test.ts +11 -12
  38. package/tests/commands/up.test.ts +31 -23
  39. package/tests/commands/update.test.ts +2 -5
  40. package/tests/libs/baseAction.test.ts +175 -0
  41. package/tests/services/simulator.test.ts +15 -0
  42. package/.eslintrc.js +0 -58
  43. package/esbuild.config.dev +0 -16
  44. package/esbuild.config.prod +0 -16
@@ -1,72 +1,71 @@
1
1
  import Docker from "dockerode";
2
2
  import { rpcClient } from "../../lib/clients/jsonRpcClient";
3
+ import { BaseAction } from "../../lib/actions/BaseAction";
3
4
 
4
- export class OllamaAction {
5
+ export class OllamaAction extends BaseAction {
5
6
  private docker: Docker;
6
7
 
7
8
  constructor() {
9
+ super();
8
10
  this.docker = new Docker();
9
11
  }
10
12
 
11
13
  async updateModel(modelName: string) {
12
- const providersAndModels = await rpcClient.request({
13
- method: "sim_getProvidersAndModels",
14
- params: [],
15
- });
16
-
17
- const existingOllamaProvider = providersAndModels.result.find(
18
- (entry: any) => entry.plugin === "ollama"
19
- );
14
+ try {
15
+ this.startSpinner(`Updating model "${modelName}"...`);
20
16
 
21
- if (!existingOllamaProvider) {
22
- throw new Error("No existing 'ollama' provider found. Unable to add/update a model.");
23
- }
17
+ if(!modelName){
18
+ modelName = this.getConfig().defaultOllamaModel;
19
+ }
24
20
 
25
- await this.executeModelCommand(
26
- "pull",
27
- modelName,
28
- `Model "${modelName}" updated successfully`
29
- );
30
-
31
- const existingModel = providersAndModels.result.some(
32
- (entry: any) =>
33
- entry.plugin === "ollama" && entry.model === modelName
34
- );
35
-
36
- if (!existingModel) {
37
- console.log(`Model "${modelName}" not found in Provider Presets. Adding...`);
38
-
39
- const newModelConfig = {
40
- config: existingOllamaProvider.config,
41
- model: modelName,
42
- plugin: "ollama",
43
- plugin_config: existingOllamaProvider.plugin_config,
44
- provider: "ollama",
45
- };
46
-
47
- await rpcClient.request({
48
- method: "sim_addProvider",
49
- params: [newModelConfig],
21
+ const providersAndModels = await rpcClient.request({
22
+ method: "sim_getProvidersAndModels",
23
+ params: [],
50
24
  });
51
25
 
52
- console.log(`Model "${modelName}" added successfully.`);
26
+ const existingOllamaProvider = providersAndModels.result.find(
27
+ (entry: any) => entry.plugin === "ollama"
28
+ );
29
+
30
+ if (!existingOllamaProvider) {
31
+ return this.failSpinner("No existing 'ollama' provider found. Unable to add/update a model.");
32
+ }
33
+
34
+ await this.executeModelCommand("pull", modelName, `Model "${modelName}" updated successfully`);
35
+
36
+ const existingModel = providersAndModels.result.some(
37
+ (entry: any) => entry.plugin === "ollama" && entry.model === modelName
38
+ );
39
+ if (!existingModel) {
40
+ this.startSpinner(`Adding model "${modelName}" to Provider Presets...`);
41
+
42
+ const newModelConfig = {
43
+ config: existingOllamaProvider.config,
44
+ model: modelName,
45
+ plugin: "ollama",
46
+ plugin_config: existingOllamaProvider.plugin_config,
47
+ provider: "ollama",
48
+ };
49
+
50
+ await rpcClient.request({
51
+ method: "sim_addProvider",
52
+ params: [newModelConfig],
53
+ });
54
+ this.succeedSpinner(`Model "${modelName}" added to Provider Presets successfully.`);
55
+ }
56
+ } catch (error) {
57
+ this.failSpinner(`Error updating model "${modelName}"`, error);
53
58
  }
54
59
  }
55
60
 
56
61
  async removeModel(modelName: string) {
57
- await this.executeModelCommand(
58
- "rm",
59
- modelName,
60
- `Model "${modelName}" removed successfully`
61
- );
62
+ await this.executeModelCommand("rm", modelName, `Model "${modelName}" removed successfully`);
62
63
  }
63
64
 
64
- private async executeModelCommand(
65
- command: string,
66
- modelName: string,
67
- successMessage: string
68
- ) {
65
+ private async executeModelCommand(command: string, modelName: string, successMessage: string) {
69
66
  try {
67
+ this.startSpinner(`Executing '${command}' command on model "${modelName}"...`);
68
+
70
69
  let success = false;
71
70
  const ollamaContainer = this.docker.getContainer("ollama");
72
71
  const exec = await ollamaContainer.exec({
@@ -74,12 +73,13 @@ export class OllamaAction {
74
73
  AttachStdout: true,
75
74
  AttachStderr: true,
76
75
  });
77
- const stream = await exec.start({Detach: false, Tty: false});
76
+ const stream = await exec.start({ Detach: false, Tty: false });
78
77
 
79
78
  stream.on("data", (chunk: any) => {
80
- const chunkStr = chunk.toString();
81
- console.log(chunkStr);
82
- if (chunkStr.includes("success") || chunkStr.includes("deleted")) {
79
+ const output = chunk.toString();
80
+ this.setSpinnerText(output.trim());
81
+
82
+ if (output.includes("success") || output.includes("deleted")) {
83
83
  success = true;
84
84
  }
85
85
  });
@@ -87,17 +87,17 @@ export class OllamaAction {
87
87
  await new Promise<void>((resolve, reject) => {
88
88
  stream.on("end", () => {
89
89
  if (success) {
90
+ this.succeedSpinner(successMessage);
90
91
  resolve();
91
92
  } else {
93
+ this.failSpinner(`Failed to execute '${command}' on model "${modelName}".`);
92
94
  reject('internal error');
93
95
  }
94
96
  });
95
97
  stream.on("error", reject);
96
98
  });
97
-
98
- console.log(successMessage);
99
- }catch (error) {
100
- console.error(`Error executing command "${command}" on model "${modelName}":`, error);
99
+ } catch (error) {
100
+ this.failSpinner(`Error executing command "${command}" on model "${modelName}"`, error);
101
101
  }
102
102
  }
103
103
  }
@@ -31,26 +31,25 @@ export class ValidatorsAction extends BaseAction {
31
31
  public async getValidator(options: ValidatorOptions): Promise<void> {
32
32
  try {
33
33
  if (options.address) {
34
- console.log(`Fetching validator with address: ${options.address}`);
34
+ this.startSpinner(`Fetching validator with address: ${options.address}`);
35
35
 
36
36
  const result = await rpcClient.request({
37
37
  method: "sim_getValidator",
38
38
  params: [options.address],
39
39
  });
40
40
 
41
- console.log("Validator Details:", result.result);
41
+ this.succeedSpinner(`Successfully fetched validator with address: ${options.address}`, result.result);
42
42
  } else {
43
- console.log("Fetching all validators...");
43
+ this.startSpinner(`Fetching all validators...`);
44
44
 
45
45
  const result = await rpcClient.request({
46
46
  method: "sim_getAllValidators",
47
47
  params: [],
48
48
  });
49
-
50
- console.log("All Validators:", result.result);
49
+ this.succeedSpinner('Successfully fetched all validators.', result.result)
51
50
  }
52
51
  } catch (error) {
53
- console.error("Error fetching validators:", error);
52
+ this.failSpinner("Error fetching validators", error);
54
53
  }
55
54
  }
56
55
 
@@ -58,65 +57,60 @@ export class ValidatorsAction extends BaseAction {
58
57
  try {
59
58
  if (options.address) {
60
59
  await this.confirmPrompt(`This command will delete the validator with the address: ${options.address}. Do you want to continue?`);
61
- console.log(`Deleting validator with address: ${options.address}`);
60
+ this.startSpinner(`Deleting validator with address: ${options.address}`);
62
61
 
63
62
  const result = await rpcClient.request({
64
63
  method: "sim_deleteValidator",
65
64
  params: [options.address],
66
65
  });
67
66
 
68
- console.log("Deleted Address:", result.result);
67
+ this.succeedSpinner(`Deleted Address: ${result.result}`);
69
68
  } else {
70
69
  await this.confirmPrompt(`This command will delete all validators. Do you want to continue?`);
71
- console.log("Deleting all validators...");
70
+ this.startSpinner("Deleting all validators...");
72
71
 
73
72
  await rpcClient.request({
74
73
  method: "sim_deleteAllValidators",
75
74
  params: [],
76
75
  });
77
76
 
78
- console.log("Successfully deleted all validators");
77
+ this.succeedSpinner("Successfully deleted all validators");
79
78
  }
80
79
  } catch (error) {
81
- console.error("Error deleting validators:", error);
80
+ this.failSpinner("Error deleting validators", error);
82
81
  }
83
82
  }
84
83
 
85
84
  public async countValidators(): Promise<void> {
86
85
  try {
87
- console.log("Counting all validators...");
86
+ this.startSpinner("Counting all validators...");
88
87
 
89
88
  const result = await rpcClient.request({
90
89
  method: "sim_countValidators",
91
90
  params: [],
92
91
  });
93
-
94
- console.log("Total Validators:", result.result);
92
+ this.succeedSpinner(`Total Validators: ${result.result}`);
95
93
  } catch (error) {
96
- console.error("Error counting validators:", error);
94
+ this.failSpinner("Error counting validators", error);
97
95
  }
98
96
  }
99
97
 
100
98
  public async updateValidator(options: UpdateValidatorOptions): Promise<void> {
101
99
  try {
102
- console.log(`Fetching validator with address: ${options.address}...`);
100
+ this.startSpinner(`Fetching validator with address: ${options.address}...`);
103
101
  const currentValidator = await rpcClient.request({
104
102
  method: "sim_getValidator",
105
103
  params: [options.address],
106
104
  });
107
105
 
108
- if (!currentValidator.result) {
109
- throw new Error(`Validator with address ${options.address} not found.`);
110
- }
111
-
112
- console.log("Current Validator Details:", currentValidator.result);
106
+ this.log("Current Validator Details:", currentValidator.result);
113
107
 
114
108
  const parsedStake = options.stake
115
109
  ? parseInt(options.stake, 10)
116
110
  : currentValidator.result.stake;
117
111
 
118
112
  if (isNaN(parsedStake) || parsedStake < 0) {
119
- return console.error("Invalid stake value. Stake must be a positive integer.");
113
+ return this.failSpinner("Invalid stake value. Stake must be a positive integer.");
120
114
  }
121
115
 
122
116
  const updatedValidator = {
@@ -127,7 +121,9 @@ export class ValidatorsAction extends BaseAction {
127
121
  config: options.config ? JSON.parse(options.config) : currentValidator.result.config,
128
122
  };
129
123
 
130
- console.log("Updated Validator Details:", updatedValidator);
124
+ this.log("Updated Validator Details:", updatedValidator);
125
+
126
+ this.setSpinnerText('Updating Validator...');
131
127
 
132
128
  const result = await rpcClient.request({
133
129
  method: "sim_updateValidator",
@@ -140,9 +136,9 @@ export class ValidatorsAction extends BaseAction {
140
136
  ],
141
137
  });
142
138
 
143
- console.log("Validator successfully updated:", result.result);
139
+ this.succeedSpinner("Validator successfully updated", result.result);
144
140
  } catch (error) {
145
- console.error("Error updating validator:", error);
141
+ this.failSpinner("Error updating validator", error);
146
142
  }
147
143
  }
148
144
 
@@ -150,21 +146,21 @@ export class ValidatorsAction extends BaseAction {
150
146
  try {
151
147
  const count = parseInt(options.count, 10);
152
148
  if (isNaN(count) || count < 1) {
153
- return console.error("Invalid count. Please provide a positive integer.");
149
+ return this.logError("Invalid count. Please provide a positive integer.");
154
150
  }
155
151
 
156
- console.log(`Creating ${count} random validator(s)...`);
157
- console.log(`Providers: ${options.providers.length > 0 ? options.providers.join(", ") : "None"}`);
158
- console.log(`Models: ${options.models.length > 0 ? options.models.join(", ") : "None"}`);
152
+ this.startSpinner(`Creating ${count} random validator(s)...`);
153
+ this.log(`Providers: ${options.providers.length > 0 ? options.providers.join(", ") : "All"}`);
154
+ this.log(`Models: ${options.models.length > 0 ? options.models.join(", ") : "All"}`);
159
155
 
160
156
  const result = await rpcClient.request({
161
157
  method: "sim_createRandomValidators",
162
158
  params: [count, 1, 10, options.providers, options.models],
163
159
  });
164
160
 
165
- console.log("Random validators successfully created:", result.result);
161
+ this.succeedSpinner("Random validators successfully created", result.result);
166
162
  } catch (error) {
167
- console.error("Error creating random validators:", error);
163
+ this.failSpinner("Error creating random validators", error);
168
164
  }
169
165
  }
170
166
 
@@ -172,22 +168,23 @@ export class ValidatorsAction extends BaseAction {
172
168
  try {
173
169
  const stake = parseInt(options.stake, 10);
174
170
  if (isNaN(stake) || stake < 1) {
175
- return console.error("Invalid stake. Please provide a positive integer.");
171
+ return this.logError("Invalid stake. Please provide a positive integer.");
176
172
  }
177
173
 
178
174
  if (options.model && !options.provider) {
179
- return console.error("You must specify a provider if using a model.");
175
+ return this.logError("You must specify a provider if using a model.");
180
176
  }
181
177
 
182
- console.log("Fetching available providers and models...");
178
+ this.startSpinner("Fetching available providers and models...");
183
179
 
184
180
  const providersAndModels = await rpcClient.request({
185
181
  method: "sim_getProvidersAndModels",
186
182
  params: [],
187
183
  });
184
+ this.stopSpinner();
188
185
 
189
186
  if (!providersAndModels.result || providersAndModels.result.length === 0) {
190
- return console.error("No providers or models available.");
187
+ return this.logError("No providers or models available.");
191
188
  }
192
189
 
193
190
  const availableProviders = [
@@ -218,7 +215,7 @@ export class ValidatorsAction extends BaseAction {
218
215
  );
219
216
 
220
217
  if (availableModels.length === 0) {
221
- return console.error("No models available for the selected provider.");
218
+ return this.logError("No models available for the selected provider.");
222
219
  }
223
220
 
224
221
  let model = options.model;
@@ -241,34 +238,30 @@ export class ValidatorsAction extends BaseAction {
241
238
  );
242
239
 
243
240
  if (!modelDetails) {
244
- return console.error("Selected model details not found.");
241
+ return this.logError("Selected model details not found.");
245
242
  }
246
243
 
247
244
  const config = options.config ? JSON.parse(options.config) : modelDetails.config;
248
-
249
- console.log("Creating validator with the following details:");
250
- console.log(`Stake: ${stake}`);
251
- console.log(`Provider: ${modelDetails.provider}`);
252
- console.log(`Model: ${modelDetails.model}`);
253
- console.log(`Config:`, config);
254
- console.log(`Plugin:`, modelDetails.plugin);
255
- console.log(`Plugin Config:`, modelDetails.plugin_config);
245
+ const params = [
246
+ stake,
247
+ modelDetails.provider,
248
+ modelDetails.model,
249
+ config,
250
+ modelDetails.plugin,
251
+ modelDetails.plugin_config,
252
+ ]
253
+
254
+ this.log("Validator details:", params);
255
+ this.startSpinner('Creating validator...');
256
256
 
257
257
  const result = await rpcClient.request({
258
258
  method: "sim_createValidator",
259
- params: [
260
- stake,
261
- modelDetails.provider,
262
- modelDetails.model,
263
- config,
264
- modelDetails.plugin,
265
- modelDetails.plugin_config,
266
- ],
259
+ params,
267
260
  });
268
261
 
269
- console.log("Validator successfully created:", result.result);
262
+ this.succeedSpinner("Validator successfully created:", result.result);
270
263
  } catch (error) {
271
- console.error("Error creating validator:", error);
264
+ this.failSpinner("Error creating validator", error);
272
265
  }
273
266
  }
274
267
  }
@@ -1,19 +1,90 @@
1
+ import { ConfigFileManager } from "../../lib/config/ConfigFileManager";
2
+ import ora, { Ora } from "ora";
3
+ import chalk from "chalk";
1
4
  import inquirer from "inquirer";
2
5
 
3
- export class BaseAction {
6
+
7
+ export class BaseAction extends ConfigFileManager {
8
+ private spinner: Ora;
9
+
10
+ constructor() {
11
+ super()
12
+ this.spinner = ora({ text: "", spinner: "dots" });
13
+ }
14
+
4
15
  protected async confirmPrompt(message: string): Promise<void> {
5
16
  const answer = await inquirer.prompt([
6
17
  {
7
18
  type: "confirm",
8
19
  name: "confirmAction",
9
- message: message,
20
+ message: chalk.yellow(message),
10
21
  default: true,
11
22
  },
12
23
  ]);
13
24
 
14
25
  if (!answer.confirmAction) {
15
- console.log("Operation aborted!");
26
+ this.logError("Operation aborted!");
16
27
  process.exit(0);
17
28
  }
18
29
  }
19
- }
30
+
31
+ private formatOutput(data: any): string {
32
+ if (data instanceof Error) {
33
+ const errorDetails = {
34
+ name: data.name,
35
+ message: data.message,
36
+ ...(Object.keys(data).length ? data : {}),
37
+ };
38
+ return JSON.stringify(errorDetails, null, 2);
39
+ }
40
+ return typeof data === "object" ? JSON.stringify(data, null, 2) : String(data);
41
+ }
42
+
43
+ protected log(message: string, data?: any): void {
44
+ console.log(chalk.white(`\n${message}`));
45
+ if (data) console.log(this.formatOutput(data));
46
+ }
47
+
48
+ protected logSuccess(message: string, data?: any): void {
49
+ console.log(chalk.green(`\n✔ ${message}`));
50
+ if (data) console.log(chalk.green(this.formatOutput(data)));
51
+ }
52
+
53
+ protected logInfo(message: string, data?: any): void {
54
+ console.log(chalk.blue(`\nℹ ${message}`));
55
+ if (data) console.log(chalk.blue(this.formatOutput(data)));
56
+ }
57
+
58
+ protected logWarning(message: string, data?: any): void {
59
+ console.log(chalk.yellow(`\n⚠ ${message}`));
60
+ if (data) console.log(chalk.yellow(this.formatOutput(data)));
61
+ }
62
+
63
+ protected logError(message: string, error?: any): void {
64
+ console.error(chalk.red(`\n✖ ${message}`));
65
+ if (error) console.error(chalk.red(this.formatOutput(error)));
66
+ }
67
+
68
+ protected startSpinner(message: string) {
69
+ this.spinner.text = chalk.blue(`${message}`);
70
+ this.spinner.start();
71
+ }
72
+
73
+ protected succeedSpinner(message: string, data?: any): void {
74
+ if (data) this.log('Result:', data);
75
+ this.spinner.succeed(chalk.green(message));
76
+ }
77
+
78
+ protected failSpinner(message: string, error?:any): void {
79
+ if (error) this.log('Error:', error);
80
+ this.spinner.fail(chalk.red(message));
81
+ }
82
+
83
+ protected stopSpinner(): void {
84
+ this.spinner.stop();
85
+ }
86
+
87
+ protected setSpinnerText(message: string): void {
88
+ this.spinner.text = chalk.blue(message);
89
+ }
90
+ }
@@ -32,7 +32,7 @@ export type AiProvidersConfigType = {
32
32
  export const AI_PROVIDERS_CONFIG: AiProvidersConfigType = {
33
33
  ollama: {
34
34
  name: "Ollama",
35
- hint: "(This will download and run a local instance of Llama 3)",
35
+ hint: "(By default, this will download and run a local instance of Llama 3)",
36
36
  cliOptionValue: "ollama",
37
37
  },
38
38
  openai: {
@@ -4,6 +4,7 @@ import * as dotenv from "dotenv";
4
4
  import * as path from "path";
5
5
  import * as semver from "semver";
6
6
  import updateCheck from "update-check";
7
+ import { fileURLToPath } from "url";
7
8
  import pkg from '../../../package.json'
8
9
 
9
10
  import {rpcClient} from "../clients/jsonRpcClient";
@@ -43,7 +44,8 @@ export class SimulatorService implements ISimulatorService {
43
44
  public location: string;
44
45
 
45
46
  constructor() {
46
- this.location = path.resolve(__dirname, '..');
47
+ const __filename = fileURLToPath(import.meta.url);
48
+ this.location = path.resolve(path.dirname(__filename), '..');
47
49
  this.composeOptions = "";
48
50
  this.docker = new Docker();
49
51
  }
@@ -184,7 +186,6 @@ export class SimulatorService implements ISimulatorService {
184
186
  public async waitForSimulatorToBeReady(
185
187
  retries: number = STARTING_TIMEOUT_ATTEMPTS,
186
188
  ): Promise<WaitForSimulatorToBeReadyResultType> {
187
- console.log("Waiting for the simulator to start up...");
188
189
  try {
189
190
  const response = await rpcClient.request({method: "ping", params: []});
190
191
 
@@ -13,13 +13,6 @@ vi.mock("ethers", () => ({
13
13
  },
14
14
  }));
15
15
 
16
- vi.mock("../../src/lib/config/ConfigFileManager", () => ({
17
- ConfigFileManager: vi.fn().mockImplementation(() => ({
18
- getFilePath: vi.fn((fileName) => `/mocked/path/${fileName}`),
19
- writeConfig: vi.fn(),
20
- })),
21
- }));
22
-
23
16
  describe("KeypairCreator", () => {
24
17
  let keypairCreator: KeypairCreator;
25
18
 
@@ -31,6 +24,11 @@ describe("KeypairCreator", () => {
31
24
 
32
25
  beforeEach(() => {
33
26
  vi.clearAllMocks();
27
+ vi.spyOn(keypairCreator as any, "startSpinner").mockImplementation(() => {});
28
+ vi.spyOn(keypairCreator as any, "succeedSpinner").mockImplementation(() => {});
29
+ vi.spyOn(keypairCreator as any, "failSpinner").mockImplementation(() => {});
30
+ vi.spyOn(keypairCreator as any, "writeConfig").mockImplementation(() => {});
31
+ vi.spyOn(keypairCreator as any, "getFilePath").mockImplementation((fileName) => `/mocked/path/${fileName}`);
34
32
  vi.mocked(ethers.Wallet.createRandom).mockReturnValue(mockWallet);
35
33
  });
36
34
 
@@ -39,15 +37,15 @@ describe("KeypairCreator", () => {
39
37
  });
40
38
 
41
39
  test("successfully creates and saves a keypair", () => {
42
- const consoleLogSpy = vi.spyOn(console, "log");
43
40
  vi.mocked(existsSync).mockReturnValue(false);
44
41
  const options = { output: "keypair.json", overwrite: false };
45
42
 
46
43
  keypairCreator.createKeypairAction(options);
47
44
 
45
+ expect(keypairCreator["startSpinner"]).toHaveBeenCalledWith("Creating keypair...");
48
46
  expect(ethers.Wallet.createRandom).toHaveBeenCalledTimes(1);
47
+ expect(keypairCreator["getFilePath"]).toHaveBeenCalledWith("keypair.json");
49
48
 
50
- expect(keypairCreator["filePathManager"].getFilePath).toHaveBeenCalledWith("keypair.json");
51
49
 
52
50
  expect(writeFileSync).toHaveBeenCalledWith(
53
51
  "/mocked/path/keypair.json",
@@ -61,20 +59,17 @@ describe("KeypairCreator", () => {
61
59
  )
62
60
  );
63
61
 
64
- expect(keypairCreator["filePathManager"].writeConfig).toHaveBeenCalledWith(
62
+ expect(keypairCreator["writeConfig"]).toHaveBeenCalledWith(
65
63
  "keyPairPath",
66
64
  "/mocked/path/keypair.json"
67
65
  );
68
66
 
69
- expect(consoleLogSpy).toHaveBeenCalledWith(
67
+ expect(keypairCreator["succeedSpinner"]).toHaveBeenCalledWith(
70
68
  "Keypair successfully created and saved to: /mocked/path/keypair.json"
71
69
  );
72
70
  });
73
71
 
74
72
  test("skips creation if file exists and overwrite is false", () => {
75
-
76
-
77
- const consoleWarnSpy = vi.spyOn(console, "warn");
78
73
  vi.mocked(existsSync).mockReturnValue(true);
79
74
  const options = { output: "keypair.json", overwrite: false };
80
75
 
@@ -82,21 +77,20 @@ describe("KeypairCreator", () => {
82
77
 
83
78
  expect(ethers.Wallet.createRandom).not.toHaveBeenCalled();
84
79
  expect(writeFileSync).not.toHaveBeenCalled();
85
- expect(consoleWarnSpy).toHaveBeenCalledWith(
80
+ expect(keypairCreator["failSpinner"]).toHaveBeenCalledWith(
86
81
  "The file at /mocked/path/keypair.json already exists. Use the '--overwrite' option to replace it."
87
82
  );
88
83
  });
89
84
 
90
85
  test("overwrites the file if overwrite is true", () => {
91
- const consoleLogSpy = vi.spyOn(console, "log");
92
86
  vi.mocked(existsSync).mockReturnValue(true);
93
87
  const options = { output: "keypair.json", overwrite: true };
94
88
 
95
89
  keypairCreator.createKeypairAction(options);
96
90
 
91
+ expect(keypairCreator["startSpinner"]).toHaveBeenCalledWith("Creating keypair...");
97
92
  expect(ethers.Wallet.createRandom).toHaveBeenCalledTimes(1);
98
-
99
- expect(keypairCreator["filePathManager"].getFilePath).toHaveBeenCalledWith("keypair.json");
93
+ expect(keypairCreator["getFilePath"]).toHaveBeenCalledWith("keypair.json");
100
94
 
101
95
  expect(writeFileSync).toHaveBeenCalledWith(
102
96
  "/mocked/path/keypair.json",
@@ -110,31 +104,25 @@ describe("KeypairCreator", () => {
110
104
  )
111
105
  );
112
106
 
113
- expect(keypairCreator["filePathManager"].writeConfig).toHaveBeenCalledWith(
107
+ expect(keypairCreator["writeConfig"]).toHaveBeenCalledWith(
114
108
  "keyPairPath",
115
109
  "/mocked/path/keypair.json"
116
110
  );
117
111
 
118
- expect(consoleLogSpy).toHaveBeenCalledWith(
112
+ expect(keypairCreator["succeedSpinner"]).toHaveBeenCalledWith(
119
113
  "Keypair successfully created and saved to: /mocked/path/keypair.json"
120
114
  );
121
115
  });
122
116
 
123
117
  test("handles errors during keypair creation", () => {
124
- const consoleErrorSpy = vi.spyOn(console, "error");
125
- const processExitSpy = vi.spyOn(process, "exit").mockImplementation(() => {
126
- throw new Error("process.exit");
127
- });
118
+ const mockError = new Error("Mocked write error");
128
119
 
129
120
  vi.mocked(writeFileSync).mockImplementation(() => {
130
- throw new Error("Mocked write error");
121
+ throw mockError;
131
122
  });
132
123
 
133
- expect(() => {
134
- keypairCreator.createKeypairAction({ output: "keypair.json", overwrite: true });
135
- }).toThrowError("process.exit");
124
+ keypairCreator.createKeypairAction({ output: "keypair.json", overwrite: true });
136
125
 
137
- expect(consoleErrorSpy).toHaveBeenCalledWith("Failed to generate keypair:", expect.any(Error));
138
- expect(processExitSpy).toHaveBeenCalledWith(1);
126
+ expect(keypairCreator["failSpinner"]).toHaveBeenCalledWith("Failed to generate keypair:", mockError);
139
127
  });
140
128
  });