genlayer 0.5.1-beta.0 → 0.7.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/.env.example +1 -0
- package/CHANGELOG.md +14 -0
- package/dist/index.js +32697 -5537
- package/docker-compose.yml +4 -4
- package/package.json +2 -1
- package/src/commands/general/index.ts +4 -4
- package/src/commands/general/init.ts +7 -0
- package/src/commands/keygen/create.ts +44 -0
- package/src/commands/keygen/index.ts +21 -0
- package/src/index.ts +3 -1
- package/src/lib/config/ConfigFileManager.ts +51 -0
- package/src/lib/interfaces/ISimulatorService.ts +1 -0
- package/src/lib/services/simulator.ts +17 -0
- package/tests/actions/create.test.ts +140 -0
- package/tests/actions/init.test.ts +3 -2
- package/tests/commands/init.test.ts +9 -1
- package/tests/commands/keygen.test.ts +77 -0
- package/tests/index.test.ts +4 -0
- package/tests/libs/configFileManager.test.ts +113 -0
- package/tests/services/simulator.test.ts +34 -0
package/docker-compose.yml
CHANGED
|
@@ -2,7 +2,7 @@ version: "3.8"
|
|
|
2
2
|
|
|
3
3
|
services:
|
|
4
4
|
frontend:
|
|
5
|
-
image: yeagerai/simulator-frontend
|
|
5
|
+
image: yeagerai/simulator-frontend:${LOCALNETVERSION:-latest}
|
|
6
6
|
ports:
|
|
7
7
|
- "${FRONTEND_PORT:-8080}:8080"
|
|
8
8
|
environment:
|
|
@@ -25,7 +25,7 @@ services:
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
jsonrpc:
|
|
28
|
-
image: yeagerai/simulator-jsonrpc
|
|
28
|
+
image: yeagerai/simulator-jsonrpc:${LOCALNETVERSION:-latest}
|
|
29
29
|
environment:
|
|
30
30
|
- FLASK_SERVER_PORT=${RPCPORT:-5000}
|
|
31
31
|
- PYTHONUNBUFFERED=1
|
|
@@ -63,7 +63,7 @@ services:
|
|
|
63
63
|
replicas: ${JSONRPC_REPLICAS:-1}
|
|
64
64
|
|
|
65
65
|
webrequest:
|
|
66
|
-
image: yeagerai/simulator-webrequest
|
|
66
|
+
image: yeagerai/simulator-webrequest:${LOCALNETVERSION:-latest}
|
|
67
67
|
shm_size: 2gb
|
|
68
68
|
environment:
|
|
69
69
|
- FLASK_SERVER_PORT=${WEBREQUESTPORT:-5002}
|
|
@@ -131,7 +131,7 @@ services:
|
|
|
131
131
|
# - "./data/postgres:/var/lib/postgresql/data"
|
|
132
132
|
|
|
133
133
|
database-migration:
|
|
134
|
-
image: yeagerai/simulator-database-migration
|
|
134
|
+
image: yeagerai/simulator-database-migration:${LOCALNETVERSION:-latest}
|
|
135
135
|
environment:
|
|
136
136
|
- DB_URL=postgresql://${DBUSER:-postgres}:${DBPASSWORD:-postgres}@postgres/${DBNAME:-simulator_db}
|
|
137
137
|
- WEBREQUESTPORT=${WEBREQUESTPORT:-5002}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genlayer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "GenLayer Command Line Tool",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"bin": {
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"commander": "^12.0.0",
|
|
60
60
|
"dockerode": "^4.0.2",
|
|
61
61
|
"dotenv": "^16.4.5",
|
|
62
|
+
"ethers": "^6.13.4",
|
|
62
63
|
"inquirer": "^9.2.19",
|
|
63
64
|
"node-fetch": "^2.7.0",
|
|
64
65
|
"open": "^10.1.0",
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {Command} from "commander";
|
|
1
|
+
import { Command } from "commander";
|
|
2
2
|
|
|
3
3
|
import simulatorService from "../../lib/services/simulator";
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import {startAction, StartActionOptions} from "./start";
|
|
4
|
+
import { initAction, InitActionOptions } from "./init";
|
|
5
|
+
import { startAction, StartActionOptions } from "./start";
|
|
7
6
|
|
|
8
7
|
export function initializeGeneralCommands(program: Command) {
|
|
9
8
|
program
|
|
@@ -12,6 +11,7 @@ export function initializeGeneralCommands(program: Command) {
|
|
|
12
11
|
.option("--numValidators <numValidators>", "Number of validators", "5")
|
|
13
12
|
.option("--headless", "Headless mode", false)
|
|
14
13
|
.option("--reset-db", "Reset Database", false)
|
|
14
|
+
.option("--localnet-version <localnetVersion>", "Select a specific localnet version", 'latest')
|
|
15
15
|
.action((options: InitActionOptions) => initAction(options, simulatorService));
|
|
16
16
|
|
|
17
17
|
program
|
|
@@ -6,6 +6,7 @@ export interface InitActionOptions {
|
|
|
6
6
|
numValidators: number;
|
|
7
7
|
headless: boolean;
|
|
8
8
|
resetDb: boolean;
|
|
9
|
+
localnetVersion: string;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
function getRequirementsErrorMessage({docker}: Record<string, boolean>): string {
|
|
@@ -34,6 +35,11 @@ function getVersionErrorMessage({docker, node}: Record<string, string>): string
|
|
|
34
35
|
export async function initAction(options: InitActionOptions, simulatorService: ISimulatorService) {
|
|
35
36
|
simulatorService.setComposeOptions(options.headless);
|
|
36
37
|
|
|
38
|
+
let localnetVersion = options.localnetVersion;
|
|
39
|
+
|
|
40
|
+
if(localnetVersion !== 'latest'){
|
|
41
|
+
localnetVersion = simulatorService.normalizeLocalnetVersion(localnetVersion);
|
|
42
|
+
}
|
|
37
43
|
await simulatorService.checkCliVersion();
|
|
38
44
|
|
|
39
45
|
// Check if requirements are installed
|
|
@@ -139,6 +145,7 @@ export async function initAction(options: InitActionOptions, simulatorService: I
|
|
|
139
145
|
|
|
140
146
|
console.log("Configuring GenLayer Simulator environment...");
|
|
141
147
|
simulatorService.addConfigToEnvFile(aiProvidersEnvVars);
|
|
148
|
+
simulatorService.addConfigToEnvFile({LOCALNETVERSION: localnetVersion});
|
|
142
149
|
|
|
143
150
|
|
|
144
151
|
// Run the GenLayer Simulator
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { writeFileSync, existsSync } from "fs";
|
|
2
|
+
import { ethers } from "ethers";
|
|
3
|
+
import { ConfigFileManager } from "../../lib/config/ConfigFileManager";
|
|
4
|
+
|
|
5
|
+
export interface CreateKeypairOptions {
|
|
6
|
+
output: string;
|
|
7
|
+
overwrite: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class KeypairCreator {
|
|
11
|
+
private filePathManager: ConfigFileManager;
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
this.filePathManager = new ConfigFileManager();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
createKeypairAction(options: CreateKeypairOptions) {
|
|
18
|
+
try {
|
|
19
|
+
|
|
20
|
+
const outputPath = this.filePathManager.getFilePath(options.output);
|
|
21
|
+
|
|
22
|
+
if(existsSync(outputPath) && !options.overwrite) {
|
|
23
|
+
console.warn(
|
|
24
|
+
`The file at ${outputPath} already exists. Use the '--overwrite' option to replace it.`
|
|
25
|
+
);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const wallet = ethers.Wallet.createRandom();
|
|
30
|
+
const keypairData = {
|
|
31
|
+
address: wallet.address,
|
|
32
|
+
privateKey: wallet.privateKey,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
writeFileSync(outputPath, JSON.stringify(keypairData, null, 2));
|
|
36
|
+
|
|
37
|
+
this.filePathManager.writeConfig('keyPairPath', outputPath);
|
|
38
|
+
console.log(`Keypair successfully created and saved to: ${outputPath}`);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error("Failed to generate keypair:", error);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { CreateKeypairOptions, KeypairCreator } from "./create";
|
|
3
|
+
|
|
4
|
+
export function initializeKeygenCommands(program: Command) {
|
|
5
|
+
|
|
6
|
+
const keygenCommand = program
|
|
7
|
+
.command("keygen")
|
|
8
|
+
.description("Manage keypair generation");
|
|
9
|
+
|
|
10
|
+
keygenCommand
|
|
11
|
+
.command("create")
|
|
12
|
+
.description("Generates a new keypair and saves it to a file")
|
|
13
|
+
.option("--output <path>", "Path to save the keypair", "./keypair.json")
|
|
14
|
+
.option("--overwrite", "Overwrite the existing file if it already exists", false)
|
|
15
|
+
.action((options: CreateKeypairOptions) => {
|
|
16
|
+
const keypairCreator = new KeypairCreator();
|
|
17
|
+
keypairCreator.createKeypairAction(options);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return program;
|
|
21
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
import {program} from "commander";
|
|
3
3
|
import {version} from "../package.json";
|
|
4
4
|
import {CLI_DESCRIPTION} from "../src/lib/config/text";
|
|
5
|
-
import {initializeGeneralCommands} from "../src/commands/general";
|
|
5
|
+
import { initializeGeneralCommands } from "../src/commands/general";
|
|
6
|
+
import { initializeKeygenCommands } from "../src/commands/keygen";
|
|
6
7
|
|
|
7
8
|
export function initializeCLI() {
|
|
8
9
|
program.version(version).description(CLI_DESCRIPTION);
|
|
9
10
|
initializeGeneralCommands(program);
|
|
11
|
+
initializeKeygenCommands(program);
|
|
10
12
|
program.parse(process.argv);
|
|
11
13
|
}
|
|
12
14
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import os from "os";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
|
|
5
|
+
export class ConfigFileManager {
|
|
6
|
+
private folderPath: string;
|
|
7
|
+
private configFilePath: string;
|
|
8
|
+
|
|
9
|
+
constructor(baseFolder: string = ".genlayer/", configFileName: string = "genlayer-config.json") {
|
|
10
|
+
this.folderPath = path.resolve(os.homedir(), baseFolder);
|
|
11
|
+
this.configFilePath = path.resolve(this.folderPath, configFileName);
|
|
12
|
+
this.ensureFolderExists();
|
|
13
|
+
this.ensureConfigFileExists();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
private ensureFolderExists(): void {
|
|
17
|
+
if (!fs.existsSync(this.folderPath)) {
|
|
18
|
+
fs.mkdirSync(this.folderPath, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private ensureConfigFileExists(): void {
|
|
23
|
+
if (!fs.existsSync(this.configFilePath)) {
|
|
24
|
+
fs.writeFileSync(this.configFilePath, JSON.stringify({}, null, 2));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getFolderPath(): string {
|
|
29
|
+
return this.folderPath;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getFilePath(fileName: string): string {
|
|
33
|
+
return path.resolve(this.folderPath, fileName);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getConfig(): Record<string, any> {
|
|
37
|
+
const configContent = fs.readFileSync(this.configFilePath, "utf-8");
|
|
38
|
+
return JSON.parse(configContent);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getConfigByKey(key: string): any {
|
|
42
|
+
const config = this.getConfig();
|
|
43
|
+
return config[key] !== undefined ? config[key] : null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
writeConfig(key: string, value: any): void {
|
|
47
|
+
const config = this.getConfig();
|
|
48
|
+
config[key] = value;
|
|
49
|
+
fs.writeFileSync(this.configFilePath, JSON.stringify(config, null, 2));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -295,6 +295,23 @@ export class SimulatorService implements ISimulatorService {
|
|
|
295
295
|
return true;
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
+
public normalizeLocalnetVersion(version: string) {
|
|
299
|
+
|
|
300
|
+
if (!version.startsWith('v')) {
|
|
301
|
+
version = 'v' + version;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const versionRegex = /^v(\d+)\.(\d+)\.(\d+)(-.+)?$/;
|
|
305
|
+
const match = version.match(versionRegex);
|
|
306
|
+
|
|
307
|
+
if (!match) {
|
|
308
|
+
console.error('Invalid version format. Expected format: v0.0.0 or v0.0.0-suffix');
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return version
|
|
313
|
+
}
|
|
314
|
+
|
|
298
315
|
}
|
|
299
316
|
|
|
300
317
|
export default new SimulatorService();
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { describe, test, vi, beforeEach, afterEach, expect } from "vitest";
|
|
2
|
+
import { KeypairCreator } from "../../src/commands/keygen/create";
|
|
3
|
+
import { writeFileSync, existsSync } from "fs";
|
|
4
|
+
import { ethers } from "ethers";
|
|
5
|
+
|
|
6
|
+
vi.mock("fs");
|
|
7
|
+
|
|
8
|
+
vi.mock("ethers", () => ({
|
|
9
|
+
ethers: {
|
|
10
|
+
Wallet: {
|
|
11
|
+
createRandom: vi.fn(),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
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
|
+
describe("KeypairCreator", () => {
|
|
24
|
+
let keypairCreator: KeypairCreator;
|
|
25
|
+
|
|
26
|
+
const mockWallet: any = {
|
|
27
|
+
address: "0xMockedAddress",
|
|
28
|
+
privateKey: "0xMockedPrivateKey",
|
|
29
|
+
};
|
|
30
|
+
keypairCreator = new KeypairCreator();
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
vi.clearAllMocks();
|
|
34
|
+
vi.mocked(ethers.Wallet.createRandom).mockReturnValue(mockWallet);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
vi.restoreAllMocks();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("successfully creates and saves a keypair", () => {
|
|
42
|
+
const consoleLogSpy = vi.spyOn(console, "log");
|
|
43
|
+
vi.mocked(existsSync).mockReturnValue(false);
|
|
44
|
+
const options = { output: "keypair.json", overwrite: false };
|
|
45
|
+
|
|
46
|
+
keypairCreator.createKeypairAction(options);
|
|
47
|
+
|
|
48
|
+
expect(ethers.Wallet.createRandom).toHaveBeenCalledTimes(1);
|
|
49
|
+
|
|
50
|
+
expect(keypairCreator["filePathManager"].getFilePath).toHaveBeenCalledWith("keypair.json");
|
|
51
|
+
|
|
52
|
+
expect(writeFileSync).toHaveBeenCalledWith(
|
|
53
|
+
"/mocked/path/keypair.json",
|
|
54
|
+
JSON.stringify(
|
|
55
|
+
{
|
|
56
|
+
address: mockWallet.address,
|
|
57
|
+
privateKey: mockWallet.privateKey,
|
|
58
|
+
},
|
|
59
|
+
null,
|
|
60
|
+
2
|
|
61
|
+
)
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
expect(keypairCreator["filePathManager"].writeConfig).toHaveBeenCalledWith(
|
|
65
|
+
"keyPairPath",
|
|
66
|
+
"/mocked/path/keypair.json"
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
70
|
+
"Keypair successfully created and saved to: /mocked/path/keypair.json"
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("skips creation if file exists and overwrite is false", () => {
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
const consoleWarnSpy = vi.spyOn(console, "warn");
|
|
78
|
+
vi.mocked(existsSync).mockReturnValue(true);
|
|
79
|
+
const options = { output: "keypair.json", overwrite: false };
|
|
80
|
+
|
|
81
|
+
keypairCreator.createKeypairAction(options);
|
|
82
|
+
|
|
83
|
+
expect(ethers.Wallet.createRandom).not.toHaveBeenCalled();
|
|
84
|
+
expect(writeFileSync).not.toHaveBeenCalled();
|
|
85
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
|
86
|
+
"The file at /mocked/path/keypair.json already exists. Use the '--overwrite' option to replace it."
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("overwrites the file if overwrite is true", () => {
|
|
91
|
+
const consoleLogSpy = vi.spyOn(console, "log");
|
|
92
|
+
vi.mocked(existsSync).mockReturnValue(true); // Simulate file exists
|
|
93
|
+
const options = { output: "keypair.json", overwrite: true };
|
|
94
|
+
|
|
95
|
+
keypairCreator.createKeypairAction(options);
|
|
96
|
+
|
|
97
|
+
expect(ethers.Wallet.createRandom).toHaveBeenCalledTimes(1);
|
|
98
|
+
|
|
99
|
+
expect(keypairCreator["filePathManager"].getFilePath).toHaveBeenCalledWith("keypair.json");
|
|
100
|
+
|
|
101
|
+
expect(writeFileSync).toHaveBeenCalledWith(
|
|
102
|
+
"/mocked/path/keypair.json",
|
|
103
|
+
JSON.stringify(
|
|
104
|
+
{
|
|
105
|
+
address: mockWallet.address,
|
|
106
|
+
privateKey: mockWallet.privateKey,
|
|
107
|
+
},
|
|
108
|
+
null,
|
|
109
|
+
2
|
|
110
|
+
)
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
expect(keypairCreator["filePathManager"].writeConfig).toHaveBeenCalledWith(
|
|
114
|
+
"keyPairPath",
|
|
115
|
+
"/mocked/path/keypair.json"
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
119
|
+
"Keypair successfully created and saved to: /mocked/path/keypair.json"
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
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
|
+
});
|
|
128
|
+
|
|
129
|
+
vi.mocked(writeFileSync).mockImplementation(() => {
|
|
130
|
+
throw new Error("Mocked write error");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
expect(() => {
|
|
134
|
+
keypairCreator.createKeypairAction({ output: "keypair.json", overwrite: true });
|
|
135
|
+
}).toThrowError("process.exit");
|
|
136
|
+
|
|
137
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith("Failed to generate keypair:", expect.any(Error));
|
|
138
|
+
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
@@ -14,7 +14,7 @@ vi.mock("dotenv");
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
const tempDir = mkdtempSync(join(tmpdir(), "test-initAction-"));
|
|
17
|
-
const defaultActionOptions = { numValidators: 5, branch: "main", location: tempDir, headless: false, resetDb: false };
|
|
17
|
+
const defaultActionOptions = { numValidators: 5, branch: "main", location: tempDir, headless: false, resetDb: false, localnetVersion: 'latest' };
|
|
18
18
|
|
|
19
19
|
describe("init action", () => {
|
|
20
20
|
let error: ReturnType<any>;
|
|
@@ -201,7 +201,7 @@ describe("init action", () => {
|
|
|
201
201
|
simServResetDockerContainers.mockResolvedValue(true);
|
|
202
202
|
simServResetDockerImages.mockResolvedValue(true);
|
|
203
203
|
|
|
204
|
-
await initAction({...defaultActionOptions, headless: true, resetDb: true}, simulatorService);
|
|
204
|
+
await initAction({...defaultActionOptions, headless: true, resetDb: true, localnetVersion: "v1.0.0"}, simulatorService);
|
|
205
205
|
|
|
206
206
|
expect(log).toHaveBeenCalledWith(
|
|
207
207
|
`GenLayer simulator initialized successfully! `
|
|
@@ -441,4 +441,5 @@ describe("init action", () => {
|
|
|
441
441
|
|
|
442
442
|
expect(error).toHaveBeenCalledWith(errorMsg);
|
|
443
443
|
});
|
|
444
|
+
|
|
444
445
|
});
|
|
@@ -8,7 +8,8 @@ const openFrontendSpy = vi.spyOn(simulatorService, "openFrontend");
|
|
|
8
8
|
const defaultOptions = {
|
|
9
9
|
numValidators: "5",
|
|
10
10
|
headless: false,
|
|
11
|
-
resetDb: false
|
|
11
|
+
resetDb: false,
|
|
12
|
+
localnetVersion: 'latest'
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
vi.mock("inquirer", () => ({
|
|
@@ -73,4 +74,11 @@ describe("init command", () => {
|
|
|
73
74
|
expect(action).toHaveBeenCalledWith({...defaultOptions, headless: true});
|
|
74
75
|
expect(openFrontendSpy).not.toHaveBeenCalled();
|
|
75
76
|
});
|
|
77
|
+
|
|
78
|
+
test("option --localnet-version is accepted", async () => {
|
|
79
|
+
program.parse(["node", "test", "init", "--localnet-version", "v1.0.0"]);
|
|
80
|
+
expect(action).toHaveBeenCalledTimes(1);
|
|
81
|
+
expect(action).toHaveBeenCalledWith({...defaultOptions, localnetVersion: "v1.0.0"});
|
|
82
|
+
expect(openFrontendSpy).not.toHaveBeenCalled();
|
|
83
|
+
});
|
|
76
84
|
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { vi, describe, beforeEach, afterEach, test, expect } from "vitest";
|
|
3
|
+
import { initializeKeygenCommands } from "../../src/commands/keygen";
|
|
4
|
+
import { KeypairCreator } from "../../src/commands/keygen/create";
|
|
5
|
+
|
|
6
|
+
vi.mock("../../src/commands/keygen/create");
|
|
7
|
+
|
|
8
|
+
describe("keygen create command", () => {
|
|
9
|
+
let program: Command;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
program = new Command();
|
|
13
|
+
initializeKeygenCommands(program);
|
|
14
|
+
vi.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
vi.restoreAllMocks();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("keypairCreator.createKeypairAction is called with default options", async () => {
|
|
22
|
+
program.parse(["node", "test", "keygen", "create"]);
|
|
23
|
+
expect(KeypairCreator).toHaveBeenCalledTimes(1);
|
|
24
|
+
expect(KeypairCreator.prototype.createKeypairAction).toHaveBeenCalledWith({
|
|
25
|
+
output: "./keypair.json",
|
|
26
|
+
overwrite: false,
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("keypairCreator.createKeypairAction is called with custom output option", async () => {
|
|
31
|
+
program.parse(["node", "test", "keygen", "create", "--output", "./custom.json"]);
|
|
32
|
+
expect(KeypairCreator).toHaveBeenCalledTimes(1);
|
|
33
|
+
expect(KeypairCreator.prototype.createKeypairAction).toHaveBeenCalledWith({
|
|
34
|
+
output: "./custom.json",
|
|
35
|
+
overwrite: false,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("keypairCreator.createKeypairAction is called with overwrite enabled", async () => {
|
|
40
|
+
program.parse(["node", "test", "keygen", "create", "--overwrite"]);
|
|
41
|
+
expect(KeypairCreator).toHaveBeenCalledTimes(1);
|
|
42
|
+
expect(KeypairCreator.prototype.createKeypairAction).toHaveBeenCalledWith({
|
|
43
|
+
output: "./keypair.json",
|
|
44
|
+
overwrite: true,
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("keypairCreator.createKeypairAction is called with custom output and overwrite enabled", async () => {
|
|
49
|
+
program.parse(["node", "test", "keygen", "create", "--output", "./custom.json", "--overwrite"]);
|
|
50
|
+
expect(KeypairCreator).toHaveBeenCalledTimes(1);
|
|
51
|
+
expect(KeypairCreator.prototype.createKeypairAction).toHaveBeenCalledWith({
|
|
52
|
+
output: "./custom.json",
|
|
53
|
+
overwrite: true,
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("KeypairCreator is instantiated when the command is executed", async () => {
|
|
58
|
+
program.parse(["node", "test", "keygen", "create"]);
|
|
59
|
+
expect(KeypairCreator).toHaveBeenCalledTimes(1);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("throws error for unrecognized options", async () => {
|
|
63
|
+
const keygenCommand = program.commands.find((cmd) => cmd.name() === "keygen");
|
|
64
|
+
const createCommand = keygenCommand?.commands.find((cmd) => cmd.name() === "create");
|
|
65
|
+
|
|
66
|
+
createCommand?.exitOverride();
|
|
67
|
+
expect(() => program.parse(["node", "test", "keygen", "create", "--unknown"])).toThrowError(
|
|
68
|
+
"error: unknown option '--unknown'"
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("keypairCreator.createKeypairAction is called without throwing errors for default options", async () => {
|
|
73
|
+
program.parse(["node", "test", "keygen", "create"]);
|
|
74
|
+
vi.mocked(KeypairCreator.prototype.createKeypairAction).mockReturnValue();
|
|
75
|
+
expect(() => program.parse(["node", "test", "keygen", "create"])).not.toThrow();
|
|
76
|
+
});
|
|
77
|
+
});
|
package/tests/index.test.ts
CHANGED
|
@@ -13,6 +13,10 @@ vi.mock("../src/commands/general", () => ({
|
|
|
13
13
|
initializeGeneralCommands: vi.fn(),
|
|
14
14
|
}));
|
|
15
15
|
|
|
16
|
+
vi.mock("../src/commands/keygen", () => ({
|
|
17
|
+
initializeKeygenCommands: vi.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
16
20
|
|
|
17
21
|
describe("CLI", () => {
|
|
18
22
|
it("should initialize CLI", () => {
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, test, vi, beforeEach, afterEach, expect } from "vitest";
|
|
2
|
+
import { ConfigFileManager } from "../../src/lib/config/ConfigFileManager";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import path from "path";
|
|
6
|
+
|
|
7
|
+
vi.mock("fs");
|
|
8
|
+
|
|
9
|
+
vi.mock("os")
|
|
10
|
+
|
|
11
|
+
describe("ConfigFileManager", () => {
|
|
12
|
+
const mockFolderPath = "/mocked/home/.genlayer";
|
|
13
|
+
const mockConfigFilePath = `${mockFolderPath}/genlayer-config.json`;
|
|
14
|
+
|
|
15
|
+
let configFileManager: ConfigFileManager;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
vi.clearAllMocks();
|
|
19
|
+
vi.mocked(os.homedir).mockReturnValue("/mocked/home");
|
|
20
|
+
configFileManager = new ConfigFileManager();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
vi.restoreAllMocks();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("ensures folder and config file are created if they don't exist", () => {
|
|
28
|
+
vi.mocked(fs.existsSync).mockReturnValue(false);
|
|
29
|
+
|
|
30
|
+
new ConfigFileManager();
|
|
31
|
+
|
|
32
|
+
expect(fs.existsSync).toHaveBeenCalledWith(mockFolderPath);
|
|
33
|
+
expect(fs.mkdirSync).toHaveBeenCalledWith(mockFolderPath, { recursive: true });
|
|
34
|
+
|
|
35
|
+
expect(fs.existsSync).toHaveBeenCalledWith(mockConfigFilePath);
|
|
36
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(mockConfigFilePath, JSON.stringify({}, null, 2));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("does not recreate folder or config file if they exist", () => {
|
|
40
|
+
vi.clearAllMocks();
|
|
41
|
+
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
42
|
+
|
|
43
|
+
new ConfigFileManager();
|
|
44
|
+
|
|
45
|
+
expect(fs.mkdirSync).not.toHaveBeenCalled();
|
|
46
|
+
expect(fs.writeFileSync).not.toHaveBeenCalled();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("getFolderPath returns the correct folder path", () => {
|
|
50
|
+
expect(configFileManager.getFolderPath()).toBe(mockFolderPath);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("getFilePath returns the correct file path for a given file name", () => {
|
|
54
|
+
const fileName = "example.json";
|
|
55
|
+
const expectedFilePath = path.resolve(mockFolderPath, fileName);
|
|
56
|
+
|
|
57
|
+
expect(configFileManager.getFilePath(fileName)).toBe(expectedFilePath);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("getConfig returns the parsed content of the config file", () => {
|
|
61
|
+
const mockConfig = { key: "value" };
|
|
62
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockConfig));
|
|
63
|
+
|
|
64
|
+
const config = configFileManager.getConfig();
|
|
65
|
+
|
|
66
|
+
expect(fs.readFileSync).toHaveBeenCalledWith(mockConfigFilePath, "utf-8");
|
|
67
|
+
expect(config).toEqual(mockConfig);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("getConfigByKey returns the value for a given key", () => {
|
|
71
|
+
const mockConfig = { key: "value" };
|
|
72
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockConfig));
|
|
73
|
+
|
|
74
|
+
const value = configFileManager.getConfigByKey("key");
|
|
75
|
+
|
|
76
|
+
expect(value).toBe("value");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("getConfigByKey returns null for a non-existing key", () => {
|
|
80
|
+
const mockConfig = { key: "value" };
|
|
81
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockConfig));
|
|
82
|
+
|
|
83
|
+
const value = configFileManager.getConfigByKey("nonExistingKey");
|
|
84
|
+
|
|
85
|
+
expect(value).toBeNull();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("writeConfig updates the config file with a new key-value pair", () => {
|
|
89
|
+
const mockConfig = { existingKey: "existingValue" };
|
|
90
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockConfig));
|
|
91
|
+
|
|
92
|
+
configFileManager.writeConfig("newKey", "newValue");
|
|
93
|
+
|
|
94
|
+
const expectedConfig = { existingKey: "existingValue", newKey: "newValue" };
|
|
95
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
|
96
|
+
mockConfigFilePath,
|
|
97
|
+
JSON.stringify(expectedConfig, null, 2)
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("writeConfig overwrites an existing key in the config file", () => {
|
|
102
|
+
const mockConfig = { existingKey: "existingValue" };
|
|
103
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockConfig));
|
|
104
|
+
|
|
105
|
+
configFileManager.writeConfig("existingKey", "updatedValue");
|
|
106
|
+
|
|
107
|
+
const expectedConfig = { existingKey: "updatedValue" };
|
|
108
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
|
109
|
+
mockConfigFilePath,
|
|
110
|
+
JSON.stringify(expectedConfig, null, 2)
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
});
|