genlayer 0.38.9 → 0.38.10
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/.eslintignore +2 -0
- package/.github/workflows/cli-docs.yml +124 -0
- package/.github/workflows/publish.yml +55 -0
- package/.github/workflows/smoke.yml +27 -0
- package/.github/workflows/validate-code.yml +51 -0
- package/.prettierignore +19 -0
- package/.prettierrc +12 -0
- package/.release-it.json +66 -0
- package/CHANGELOG.md +545 -0
- package/CLAUDE.md +55 -0
- package/CONTRIBUTING.md +117 -0
- package/dist/index.js +221 -62
- package/docs/api-references/_meta.json +9 -0
- package/docs/api-references/accounts/_meta.json +3 -0
- package/docs/api-references/accounts/account/create.mdx +19 -0
- package/docs/api-references/accounts/account/export.mdx +19 -0
- package/docs/api-references/accounts/account/import.mdx +22 -0
- package/docs/api-references/accounts/account/list.mdx +15 -0
- package/docs/api-references/accounts/account/lock.mdx +16 -0
- package/docs/api-references/accounts/account/remove.mdx +20 -0
- package/docs/api-references/accounts/account/send.mdx +24 -0
- package/docs/api-references/accounts/account/show.mdx +17 -0
- package/docs/api-references/accounts/account/unlock.mdx +17 -0
- package/docs/api-references/accounts/account/use.mdx +19 -0
- package/docs/api-references/accounts/account.mdx +32 -0
- package/docs/api-references/configuration/_meta.json +4 -0
- package/docs/api-references/configuration/config/get.mdx +21 -0
- package/docs/api-references/configuration/config/reset.mdx +21 -0
- package/docs/api-references/configuration/config/set.mdx +21 -0
- package/docs/api-references/configuration/config.mdx +25 -0
- package/docs/api-references/configuration/network/info.mdx +15 -0
- package/docs/api-references/configuration/network/list.mdx +15 -0
- package/docs/api-references/configuration/network/set.mdx +21 -0
- package/docs/api-references/configuration/network.mdx +25 -0
- package/docs/api-references/contracts/_meta.json +7 -0
- package/docs/api-references/contracts/call.mdx +21 -0
- package/docs/api-references/contracts/code.mdx +20 -0
- package/docs/api-references/contracts/deploy.mdx +17 -0
- package/docs/api-references/contracts/schema.mdx +20 -0
- package/docs/api-references/contracts/write.mdx +21 -0
- package/docs/api-references/environment/_meta.json +7 -0
- package/docs/api-references/environment/init.mdx +20 -0
- package/docs/api-references/environment/new.mdx +21 -0
- package/docs/api-references/environment/stop.mdx +15 -0
- package/docs/api-references/environment/up.mdx +20 -0
- package/docs/api-references/environment/update/ollama.mdx +16 -0
- package/docs/api-references/environment/update.mdx +23 -0
- package/docs/api-references/index.mdx +35 -0
- package/docs/api-references/localnet/_meta.json +3 -0
- package/docs/api-references/localnet/localnet/validators/count.mdx +15 -0
- package/docs/api-references/localnet/localnet/validators/create-random.mdx +16 -0
- package/docs/api-references/localnet/localnet/validators/create.mdx +19 -0
- package/docs/api-references/localnet/localnet/validators/delete.mdx +16 -0
- package/docs/api-references/localnet/localnet/validators/get.mdx +16 -0
- package/docs/api-references/localnet/localnet/validators/update.mdx +23 -0
- package/docs/api-references/localnet/localnet/validators.mdx +28 -0
- package/docs/api-references/localnet/localnet.mdx +23 -0
- package/docs/api-references/staking/_meta.json +3 -0
- package/docs/api-references/staking/staking/active-validators.mdx +18 -0
- package/docs/api-references/staking/staking/banned-validators.mdx +18 -0
- package/docs/api-references/staking/staking/delegation-info.mdx +25 -0
- package/docs/api-references/staking/staking/delegator-claim.mdx +26 -0
- package/docs/api-references/staking/staking/delegator-exit.mdx +26 -0
- package/docs/api-references/staking/staking/delegator-join.mdx +26 -0
- package/docs/api-references/staking/staking/epoch-info.mdx +19 -0
- package/docs/api-references/staking/staking/prime-all.mdx +20 -0
- package/docs/api-references/staking/staking/quarantined-validators.mdx +18 -0
- package/docs/api-references/staking/staking/set-identity.mdx +33 -0
- package/docs/api-references/staking/staking/set-operator.mdx +26 -0
- package/docs/api-references/staking/staking/validator-claim.mdx +24 -0
- package/docs/api-references/staking/staking/validator-deposit.mdx +25 -0
- package/docs/api-references/staking/staking/validator-exit.mdx +25 -0
- package/docs/api-references/staking/staking/validator-history.mdx +29 -0
- package/docs/api-references/staking/staking/validator-info.mdx +25 -0
- package/docs/api-references/staking/staking/validator-join.mdx +22 -0
- package/docs/api-references/staking/staking/validator-prime.mdx +25 -0
- package/docs/api-references/staking/staking/validators.mdx +19 -0
- package/docs/api-references/staking/staking/wizard.mdx +20 -0
- package/docs/api-references/staking/staking.mdx +42 -0
- package/docs/api-references/transactions/_meta.json +6 -0
- package/docs/api-references/transactions/appeal-bond.mdx +20 -0
- package/docs/api-references/transactions/appeal.mdx +21 -0
- package/docs/api-references/transactions/receipt.mdx +25 -0
- package/docs/api-references/transactions/trace.mdx +21 -0
- package/docs/delegator-guide.md +203 -0
- package/docs/validator-guide.md +329 -0
- package/esbuild.config.dev.js +17 -0
- package/esbuild.config.js +22 -0
- package/esbuild.config.prod.js +17 -0
- package/eslint.config.js +60 -0
- package/package.json +2 -11
- package/renovate.json +22 -0
- package/scripts/generate-cli-docs.mjs +68 -5
- package/src/commands/account/create.ts +30 -0
- package/src/commands/account/export.ts +106 -0
- package/src/commands/account/import.ts +135 -0
- package/src/commands/account/index.ts +129 -0
- package/src/commands/account/list.ts +34 -0
- package/src/commands/account/lock.ts +39 -0
- package/src/commands/account/remove.ts +30 -0
- package/src/commands/account/send.ts +162 -0
- package/src/commands/account/show.ts +74 -0
- package/src/commands/account/unlock.ts +56 -0
- package/src/commands/account/use.ts +21 -0
- package/src/commands/config/getSetReset.ts +51 -0
- package/src/commands/config/index.ts +30 -0
- package/src/commands/contracts/call.ts +39 -0
- package/src/commands/contracts/code.ts +33 -0
- package/src/commands/contracts/deploy.ts +161 -0
- package/src/commands/contracts/index.ts +150 -0
- package/src/commands/contracts/schema.ts +31 -0
- package/src/commands/contracts/write.ts +49 -0
- package/src/commands/general/index.ts +45 -0
- package/src/commands/general/init.ts +180 -0
- package/src/commands/general/start.ts +128 -0
- package/src/commands/general/stop.ts +26 -0
- package/src/commands/localnet/index.ts +100 -0
- package/src/commands/localnet/validators.ts +269 -0
- package/src/commands/network/index.ts +29 -0
- package/src/commands/network/setNetwork.ts +77 -0
- package/src/commands/scaffold/index.ts +16 -0
- package/src/commands/scaffold/new.ts +34 -0
- package/src/commands/staking/StakingAction.ts +279 -0
- package/src/commands/staking/delegatorClaim.ts +41 -0
- package/src/commands/staking/delegatorExit.ts +56 -0
- package/src/commands/staking/delegatorJoin.ts +44 -0
- package/src/commands/staking/index.ts +357 -0
- package/src/commands/staking/setIdentity.ts +78 -0
- package/src/commands/staking/setOperator.ts +46 -0
- package/src/commands/staking/stakingInfo.ts +584 -0
- package/src/commands/staking/validatorClaim.ts +43 -0
- package/src/commands/staking/validatorDeposit.ts +48 -0
- package/src/commands/staking/validatorExit.ts +63 -0
- package/src/commands/staking/validatorHistory.ts +300 -0
- package/src/commands/staking/validatorJoin.ts +47 -0
- package/src/commands/staking/validatorPrime.ts +73 -0
- package/src/commands/staking/wizard.ts +809 -0
- package/src/commands/transactions/appeal.ts +83 -0
- package/src/commands/transactions/index.ts +60 -0
- package/src/commands/transactions/receipt.ts +90 -0
- package/src/commands/transactions/trace.ts +42 -0
- package/src/commands/update/index.ts +25 -0
- package/src/commands/update/ollama.ts +103 -0
- package/src/lib/actions/BaseAction.ts +301 -0
- package/src/lib/clients/jsonRpcClient.ts +41 -0
- package/src/lib/clients/system.ts +73 -0
- package/src/lib/config/ConfigFileManager.ts +194 -0
- package/src/lib/config/KeychainManager.ts +89 -0
- package/src/lib/config/simulator.ts +68 -0
- package/src/lib/config/text.ts +2 -0
- package/src/lib/errors/missingRequirement.ts +9 -0
- package/src/lib/errors/versionRequired.ts +9 -0
- package/src/lib/interfaces/ISimulatorService.ts +39 -0
- package/src/lib/services/simulator.ts +386 -0
- package/src/types/node-fetch.d.ts +1 -0
- package/tests/actions/appeal.test.ts +141 -0
- package/tests/actions/call.test.ts +94 -0
- package/tests/actions/code.test.ts +87 -0
- package/tests/actions/create.test.ts +65 -0
- package/tests/actions/deploy.test.ts +420 -0
- package/tests/actions/getSetReset.test.ts +88 -0
- package/tests/actions/init.test.ts +483 -0
- package/tests/actions/lock.test.ts +86 -0
- package/tests/actions/new.test.ts +80 -0
- package/tests/actions/ollama.test.ts +193 -0
- package/tests/actions/receipt.test.ts +261 -0
- package/tests/actions/schema.test.ts +94 -0
- package/tests/actions/setNetwork.test.ts +161 -0
- package/tests/actions/staking.test.ts +280 -0
- package/tests/actions/start.test.ts +257 -0
- package/tests/actions/stop.test.ts +77 -0
- package/tests/actions/unlock.test.ts +139 -0
- package/tests/actions/validators.test.ts +750 -0
- package/tests/actions/write.test.ts +102 -0
- package/tests/commands/account.test.ts +146 -0
- package/tests/commands/appeal.test.ts +97 -0
- package/tests/commands/call.test.ts +78 -0
- package/tests/commands/code.test.ts +69 -0
- package/tests/commands/config.test.ts +54 -0
- package/tests/commands/deploy.test.ts +83 -0
- package/tests/commands/init.test.ts +101 -0
- package/tests/commands/localnet.test.ts +131 -0
- package/tests/commands/network.test.ts +60 -0
- package/tests/commands/new.test.ts +68 -0
- package/tests/commands/parseArg.test.ts +156 -0
- package/tests/commands/receipt.test.ts +142 -0
- package/tests/commands/schema.test.ts +67 -0
- package/tests/commands/staking.test.ts +329 -0
- package/tests/commands/stop.test.ts +27 -0
- package/tests/commands/up.test.ts +105 -0
- package/tests/commands/update.test.ts +45 -0
- package/tests/commands/write.test.ts +76 -0
- package/tests/index.test.ts +56 -0
- package/tests/libs/baseAction.test.ts +535 -0
- package/tests/libs/configFileManager.test.ts +118 -0
- package/tests/libs/jsonRpcClient.test.ts +59 -0
- package/tests/libs/keychainManager.test.ts +156 -0
- package/tests/libs/platformCommands.test.ts +78 -0
- package/tests/libs/system.test.ts +148 -0
- package/tests/services/simulator.test.ts +789 -0
- package/tests/smoke.test.ts +134 -0
- package/tests/utils.ts +13 -0
- package/tsconfig.json +120 -0
- package/vitest.config.ts +13 -0
- package/vitest.smoke.config.ts +17 -0
|
@@ -0,0 +1,118 @@
|
|
|
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 mockHome = path.resolve("/mocked/home");
|
|
13
|
+
const mockFolderPath = path.resolve(mockHome, ".genlayer");
|
|
14
|
+
const mockKeystoresPath = path.resolve(mockFolderPath, "keystores");
|
|
15
|
+
const mockConfigFilePath = path.resolve(mockFolderPath, "genlayer-config.json");
|
|
16
|
+
|
|
17
|
+
let configFileManager: ConfigFileManager;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
vi.clearAllMocks();
|
|
21
|
+
vi.mocked(os.homedir).mockReturnValue(mockHome);
|
|
22
|
+
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
23
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({}));
|
|
24
|
+
configFileManager = new ConfigFileManager();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
vi.restoreAllMocks();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("ensures folder and config file are created if they don't exist", () => {
|
|
32
|
+
vi.mocked(fs.existsSync).mockReturnValue(false);
|
|
33
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({}));
|
|
34
|
+
|
|
35
|
+
new ConfigFileManager();
|
|
36
|
+
|
|
37
|
+
expect(fs.existsSync).toHaveBeenCalledWith(mockFolderPath);
|
|
38
|
+
expect(fs.mkdirSync).toHaveBeenCalledWith(mockFolderPath, { recursive: true });
|
|
39
|
+
|
|
40
|
+
expect(fs.existsSync).toHaveBeenCalledWith(mockKeystoresPath);
|
|
41
|
+
expect(fs.mkdirSync).toHaveBeenCalledWith(mockKeystoresPath, { recursive: true });
|
|
42
|
+
|
|
43
|
+
expect(fs.existsSync).toHaveBeenCalledWith(mockConfigFilePath);
|
|
44
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(mockConfigFilePath, JSON.stringify({}, null, 2));
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("does not recreate folder or config file if they exist", () => {
|
|
48
|
+
vi.clearAllMocks();
|
|
49
|
+
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
50
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({}));
|
|
51
|
+
|
|
52
|
+
new ConfigFileManager();
|
|
53
|
+
|
|
54
|
+
expect(fs.mkdirSync).not.toHaveBeenCalled();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("getFolderPath returns the correct folder path", () => {
|
|
58
|
+
expect(configFileManager.getFolderPath()).toBe(mockFolderPath);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("getFilePath returns the correct file path for a given file name", () => {
|
|
62
|
+
const fileName = "example.json";
|
|
63
|
+
const expectedFilePath = path.resolve(mockFolderPath, fileName);
|
|
64
|
+
|
|
65
|
+
expect(configFileManager.getFilePath(fileName)).toBe(expectedFilePath);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("getConfig returns the parsed content of the config file", () => {
|
|
69
|
+
const mockConfig = { key: "value" };
|
|
70
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockConfig));
|
|
71
|
+
|
|
72
|
+
const config = configFileManager.getConfig();
|
|
73
|
+
|
|
74
|
+
expect(fs.readFileSync).toHaveBeenCalledWith(mockConfigFilePath, "utf-8");
|
|
75
|
+
expect(config).toEqual(mockConfig);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("getConfigByKey returns the value for a given key", () => {
|
|
79
|
+
const mockConfig = { key: "value" };
|
|
80
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockConfig));
|
|
81
|
+
|
|
82
|
+
const value = configFileManager.getConfigByKey("key");
|
|
83
|
+
|
|
84
|
+
expect(value).toBe("value");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("getConfigByKey returns null for a non-existing key", () => {
|
|
88
|
+
const mockConfig = { key: "value" };
|
|
89
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockConfig));
|
|
90
|
+
|
|
91
|
+
const value = configFileManager.getConfigByKey("nonExistingKey");
|
|
92
|
+
|
|
93
|
+
expect(value).toBeNull();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("writeConfig updates the config file with a new key-value pair", () => {
|
|
97
|
+
const mockConfig = { existingKey: "existingValue" };
|
|
98
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockConfig));
|
|
99
|
+
|
|
100
|
+
configFileManager.writeConfig("newKey", "newValue");
|
|
101
|
+
|
|
102
|
+
const expectedConfig = { existingKey: "existingValue", newKey: "newValue" };
|
|
103
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
|
104
|
+
mockConfigFilePath,
|
|
105
|
+
JSON.stringify(expectedConfig, null, 2)
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("writeConfig overwrites an existing key in the config file", () => {
|
|
110
|
+
const existingConfig = { existingKey: "existingValue" };
|
|
111
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(existingConfig));
|
|
112
|
+
|
|
113
|
+
configFileManager.writeConfig("existingKey", "newValue");
|
|
114
|
+
|
|
115
|
+
const expectedConfig = { existingKey: "newValue" };
|
|
116
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(mockConfigFilePath, JSON.stringify(expectedConfig, null, 2));
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, beforeEach, test, expect, vi, Mock } from "vitest";
|
|
2
|
+
import fetch from "node-fetch";
|
|
3
|
+
import { JsonRpcClient, JsonRPCParams } from "../../src/lib/clients/jsonRpcClient";
|
|
4
|
+
|
|
5
|
+
vi.mock("node-fetch", () => ({
|
|
6
|
+
default: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
describe("JsonRpcClient - Successful and Unsuccessful Requests", () => {
|
|
10
|
+
let rpcClient: JsonRpcClient;
|
|
11
|
+
const mockServerUrl = "http://mock-server-url.com";
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
rpcClient = new JsonRpcClient(mockServerUrl);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("should make a successful JSON-RPC request and return the JSON response", async () => {
|
|
18
|
+
const mockResponse = { result: "success" };
|
|
19
|
+
|
|
20
|
+
(fetch as Mock).mockResolvedValueOnce({
|
|
21
|
+
ok: true,
|
|
22
|
+
json: async () => mockResponse,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const params: JsonRPCParams = {
|
|
26
|
+
method: "testMethod",
|
|
27
|
+
params: ["param1", "param2"],
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const response = await rpcClient.request(params);
|
|
31
|
+
|
|
32
|
+
expect(fetch).toHaveBeenCalledWith(mockServerUrl, {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: { "Content-Type": "application/json" },
|
|
35
|
+
body: expect.stringContaining('"method":"testMethod"'),
|
|
36
|
+
});
|
|
37
|
+
expect(response).toEqual(mockResponse);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("should return null when the fetch response is not ok", async () => {
|
|
41
|
+
(fetch as Mock).mockResolvedValueOnce({
|
|
42
|
+
ok: false,
|
|
43
|
+
statusText: "Something went wrong",
|
|
44
|
+
json: async () => ({ error: "Something went wrong" }),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const params: JsonRPCParams = {
|
|
48
|
+
method: "testMethod",
|
|
49
|
+
params: ["param1", "param2"],
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
await expect(rpcClient.request(params)).rejects.toThrowError("Something went wrong");
|
|
53
|
+
expect(fetch).toHaveBeenCalledWith(mockServerUrl, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: { "Content-Type": "application/json" },
|
|
56
|
+
body: expect.stringContaining('"method":"testMethod"'),
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {describe, test, vi, beforeEach, afterEach, expect} from "vitest";
|
|
2
|
+
import {KeychainManager} from "../../src/lib/config/KeychainManager";
|
|
3
|
+
import keytar from "keytar";
|
|
4
|
+
|
|
5
|
+
vi.mock("keytar");
|
|
6
|
+
|
|
7
|
+
describe("KeychainManager", () => {
|
|
8
|
+
let keychainManager: KeychainManager;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
vi.clearAllMocks();
|
|
12
|
+
keychainManager = new KeychainManager();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
vi.restoreAllMocks();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe("isKeychainAvailable", () => {
|
|
20
|
+
test("returns true when keychain is available", async () => {
|
|
21
|
+
vi.mocked(keytar.findCredentials).mockResolvedValue([]);
|
|
22
|
+
|
|
23
|
+
const result = await keychainManager.isKeychainAvailable();
|
|
24
|
+
|
|
25
|
+
expect(result).toBe(true);
|
|
26
|
+
expect(keytar.findCredentials).toHaveBeenCalledWith("test-service");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("returns false when keychain is not available", async () => {
|
|
30
|
+
vi.mocked(keytar.findCredentials).mockRejectedValue(new Error("Keychain not available"));
|
|
31
|
+
|
|
32
|
+
const result = await keychainManager.isKeychainAvailable();
|
|
33
|
+
|
|
34
|
+
expect(result).toBe(false);
|
|
35
|
+
expect(keytar.findCredentials).toHaveBeenCalledWith("test-service");
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("storePrivateKey", () => {
|
|
40
|
+
test("successfully stores private key", async () => {
|
|
41
|
+
const privateKey = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890";
|
|
42
|
+
vi.mocked(keytar.setPassword).mockResolvedValue();
|
|
43
|
+
|
|
44
|
+
await keychainManager.storePrivateKey("main", privateKey);
|
|
45
|
+
|
|
46
|
+
expect(keytar.setPassword).toHaveBeenCalledWith("genlayer-cli", "account:main", privateKey);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("handles storage error", async () => {
|
|
50
|
+
const privateKey = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890";
|
|
51
|
+
vi.mocked(keytar.setPassword).mockRejectedValue(new Error("Storage failed"));
|
|
52
|
+
|
|
53
|
+
await expect(keychainManager.storePrivateKey("main", privateKey)).rejects.toThrow("Storage failed");
|
|
54
|
+
expect(keytar.setPassword).toHaveBeenCalledWith("genlayer-cli", "account:main", privateKey);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("getPrivateKey", () => {
|
|
59
|
+
test("returns private key when it exists", async () => {
|
|
60
|
+
const expectedKey = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890";
|
|
61
|
+
vi.mocked(keytar.getPassword).mockResolvedValue(expectedKey);
|
|
62
|
+
|
|
63
|
+
const result = await keychainManager.getPrivateKey("main");
|
|
64
|
+
|
|
65
|
+
expect(result).toBe(expectedKey);
|
|
66
|
+
expect(keytar.getPassword).toHaveBeenCalledWith("genlayer-cli", "account:main");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("returns null when private key does not exist", async () => {
|
|
70
|
+
vi.mocked(keytar.getPassword).mockResolvedValue(null);
|
|
71
|
+
|
|
72
|
+
const result = await keychainManager.getPrivateKey("main");
|
|
73
|
+
|
|
74
|
+
expect(result).toBeNull();
|
|
75
|
+
expect(keytar.getPassword).toHaveBeenCalledWith("genlayer-cli", "account:main");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("returns null on retrieval error", async () => {
|
|
79
|
+
vi.mocked(keytar.getPassword).mockRejectedValue(new Error("Retrieval failed"));
|
|
80
|
+
|
|
81
|
+
const result = await keychainManager.getPrivateKey("main");
|
|
82
|
+
|
|
83
|
+
expect(result).toBeNull();
|
|
84
|
+
expect(keytar.getPassword).toHaveBeenCalledWith("genlayer-cli", "account:main");
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("removePrivateKey", () => {
|
|
89
|
+
test("successfully removes private key", async () => {
|
|
90
|
+
vi.mocked(keytar.deletePassword).mockResolvedValue(true);
|
|
91
|
+
|
|
92
|
+
const result = await keychainManager.removePrivateKey("main");
|
|
93
|
+
|
|
94
|
+
expect(result).toBe(true);
|
|
95
|
+
expect(keytar.deletePassword).toHaveBeenCalledWith("genlayer-cli", "account:main");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("returns false when key does not exist", async () => {
|
|
99
|
+
vi.mocked(keytar.deletePassword).mockResolvedValue(false);
|
|
100
|
+
|
|
101
|
+
const result = await keychainManager.removePrivateKey("main");
|
|
102
|
+
|
|
103
|
+
expect(result).toBe(false);
|
|
104
|
+
expect(keytar.deletePassword).toHaveBeenCalledWith("genlayer-cli", "account:main");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("returns false on removal error", async () => {
|
|
108
|
+
vi.mocked(keytar.deletePassword).mockRejectedValue(new Error("Removal failed"));
|
|
109
|
+
|
|
110
|
+
const result = await keychainManager.removePrivateKey("main");
|
|
111
|
+
|
|
112
|
+
expect(result).toBe(false);
|
|
113
|
+
expect(keytar.deletePassword).toHaveBeenCalledWith("genlayer-cli", "account:main");
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("listUnlockedAccounts", () => {
|
|
118
|
+
test("returns list of unlocked account names", async () => {
|
|
119
|
+
vi.mocked(keytar.findCredentials).mockResolvedValue([
|
|
120
|
+
{account: "account:main", password: "key1"},
|
|
121
|
+
{account: "account:validator", password: "key2"},
|
|
122
|
+
{account: "other:something", password: "key3"},
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
const result = await keychainManager.listUnlockedAccounts();
|
|
126
|
+
|
|
127
|
+
expect(result).toEqual(["main", "validator"]);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("returns empty array when no accounts", async () => {
|
|
131
|
+
vi.mocked(keytar.findCredentials).mockResolvedValue([]);
|
|
132
|
+
|
|
133
|
+
const result = await keychainManager.listUnlockedAccounts();
|
|
134
|
+
|
|
135
|
+
expect(result).toEqual([]);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe("isAccountUnlocked", () => {
|
|
140
|
+
test("returns true when account is unlocked", async () => {
|
|
141
|
+
vi.mocked(keytar.getPassword).mockResolvedValue("some-key");
|
|
142
|
+
|
|
143
|
+
const result = await keychainManager.isAccountUnlocked("main");
|
|
144
|
+
|
|
145
|
+
expect(result).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("returns false when account is locked", async () => {
|
|
149
|
+
vi.mocked(keytar.getPassword).mockResolvedValue(null);
|
|
150
|
+
|
|
151
|
+
const result = await keychainManager.isAccountUnlocked("main");
|
|
152
|
+
|
|
153
|
+
expect(result).toBe(false);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {describe, test, expect} from "vitest";
|
|
2
|
+
import {DEFAULT_RUN_SIMULATOR_COMMAND} from "../../src/lib/config/simulator";
|
|
3
|
+
|
|
4
|
+
describe("Platform-specific command generation", () => {
|
|
5
|
+
describe("DEFAULT_RUN_SIMULATOR_COMMAND", () => {
|
|
6
|
+
test("win32 command does not use start cmd.exe", () => {
|
|
7
|
+
const cmds = DEFAULT_RUN_SIMULATOR_COMMAND("/some/path", "");
|
|
8
|
+
expect(cmds.win32).not.toContain("start cmd.exe");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("win32 command quotes the path", () => {
|
|
12
|
+
const pathWithSpaces = "C:\\Program Files\\genlayer";
|
|
13
|
+
const cmds = DEFAULT_RUN_SIMULATOR_COMMAND(pathWithSpaces, "");
|
|
14
|
+
expect(cmds.win32).toContain(`"${pathWithSpaces}"`);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("win32 command uses -d flag for detached mode", () => {
|
|
18
|
+
const cmds = DEFAULT_RUN_SIMULATOR_COMMAND("/path", "");
|
|
19
|
+
expect(cmds.win32).toContain("up -d");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("win32 command uses cd /d for drive changes", () => {
|
|
23
|
+
const cmds = DEFAULT_RUN_SIMULATOR_COMMAND("D:\\projects\\genlayer", "");
|
|
24
|
+
expect(cmds.win32).toContain("cd /d");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("linux command uses nohup with -d flag", () => {
|
|
28
|
+
const cmds = DEFAULT_RUN_SIMULATOR_COMMAND("/path", "");
|
|
29
|
+
expect(cmds.linux).toContain("nohup");
|
|
30
|
+
expect(cmds.linux).toContain("up -d");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("linux command quotes the path", () => {
|
|
34
|
+
const cmds = DEFAULT_RUN_SIMULATOR_COMMAND("/path with spaces", "");
|
|
35
|
+
expect(cmds.linux).toContain('"/path with spaces"');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("darwin command uses osascript", () => {
|
|
39
|
+
const cmds = DEFAULT_RUN_SIMULATOR_COMMAND("/path", "");
|
|
40
|
+
expect(cmds.darwin).toContain("osascript");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("darwin command quotes the path for spaces", () => {
|
|
44
|
+
const cmds = DEFAULT_RUN_SIMULATOR_COMMAND("/path with spaces", "");
|
|
45
|
+
expect(cmds.darwin).toContain("/path with spaces");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("all platforms include profiles when provided", () => {
|
|
49
|
+
const profiles = "--profile frontend --profile ollama";
|
|
50
|
+
const cmds = DEFAULT_RUN_SIMULATOR_COMMAND("/path", profiles);
|
|
51
|
+
expect(cmds.darwin).toContain(profiles);
|
|
52
|
+
expect(cmds.win32).toContain(profiles);
|
|
53
|
+
expect(cmds.linux).toContain(profiles);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("all platforms include docker compose build and up", () => {
|
|
57
|
+
const cmds = DEFAULT_RUN_SIMULATOR_COMMAND("/path", "");
|
|
58
|
+
for (const platform of ["darwin", "win32", "linux"] as const) {
|
|
59
|
+
expect(cmds[platform]).toContain("docker compose build");
|
|
60
|
+
expect(cmds[platform]).toContain("docker compose -p genlayer");
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("all platforms handle empty profiles", () => {
|
|
65
|
+
const cmds = DEFAULT_RUN_SIMULATOR_COMMAND("/path", "");
|
|
66
|
+
for (const platform of ["darwin", "win32", "linux"] as const) {
|
|
67
|
+
expect(cmds[platform]).toContain("docker compose -p genlayer up");
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("paths with spaces are quoted on win32 and linux", () => {
|
|
72
|
+
const spacePath = "/some path/with spaces";
|
|
73
|
+
const cmds = DEFAULT_RUN_SIMULATOR_COMMAND(spacePath, "");
|
|
74
|
+
expect(cmds.win32).toContain(`"${spacePath}"`);
|
|
75
|
+
expect(cmds.linux).toContain(`"${spacePath}"`);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,148 @@
|
|
|
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
|
+
} from "../../src/lib/clients/system";
|
|
9
|
+
import { MissingRequirementError } from "../../src/lib/errors/missingRequirement";
|
|
10
|
+
import open from "open";
|
|
11
|
+
|
|
12
|
+
vi.mock("open");
|
|
13
|
+
vi.mock("util");
|
|
14
|
+
|
|
15
|
+
describe("System Functions - Success Paths", () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
vi.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("openUrl opens URL successfully", async () => {
|
|
21
|
+
const openSpy = vi.mocked(open).mockResolvedValue({} as any);
|
|
22
|
+
const url = "https://example.com";
|
|
23
|
+
await openUrl(url);
|
|
24
|
+
expect(openSpy).toHaveBeenCalledWith(url);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("getVersion retrieves tool version", async () => {
|
|
28
|
+
vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.resolve({
|
|
29
|
+
stdout: "git v1.2.3",
|
|
30
|
+
stderr: ""
|
|
31
|
+
}));
|
|
32
|
+
const version = await getVersion("git");
|
|
33
|
+
expect(version).toBe("1.2.3");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("checkCommand verifies a command exists", async () => {
|
|
37
|
+
vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.resolve({ stdout: "", stderr: "" }));
|
|
38
|
+
const result = await checkCommand("node --version", "node");
|
|
39
|
+
expect(result).toBe(undefined);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("executeCommand executes a command successfully", async () => {
|
|
43
|
+
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("linux");
|
|
44
|
+
vi.mocked(util.promisify).mockReturnValueOnce((param: string) => Promise.resolve({
|
|
45
|
+
stdout: param,
|
|
46
|
+
stderr: ""
|
|
47
|
+
}));
|
|
48
|
+
const result = await executeCommand({
|
|
49
|
+
linux: "echo linux",
|
|
50
|
+
win32: "echo win32",
|
|
51
|
+
darwin: "echo darwin",
|
|
52
|
+
},
|
|
53
|
+
"echo");
|
|
54
|
+
expect(result.stdout).toBe("echo linux");
|
|
55
|
+
platformSpy.mockRestore();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("System Functions - Error Paths", () => {
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
vi.clearAllMocks();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("getVersion throws an error if the command fails", async () => {
|
|
65
|
+
vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.resolve({
|
|
66
|
+
stdout: "",
|
|
67
|
+
stderr: "command not found"
|
|
68
|
+
}));
|
|
69
|
+
const toolName = "nonexistent";
|
|
70
|
+
await expect(getVersion(toolName)).rejects.toThrow(`Error getting ${toolName} version.`);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("getVersion returns '' if stdout is empty", async () => {
|
|
74
|
+
vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.resolve({
|
|
75
|
+
stdout: "",
|
|
76
|
+
stderr: ""
|
|
77
|
+
}));
|
|
78
|
+
const result = await getVersion('git');
|
|
79
|
+
expect(result).toBe("");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("getVersion throw error if stdout undefined", async () => {
|
|
83
|
+
vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.resolve({
|
|
84
|
+
stderr: ""
|
|
85
|
+
}));
|
|
86
|
+
const toolName = "nonexistent";
|
|
87
|
+
await expect(getVersion(toolName)).rejects.toThrow(`Error getting ${toolName} version.`);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("checkCommand returns false if the command does not exist", async () => {
|
|
91
|
+
vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.reject({
|
|
92
|
+
stdout: "",
|
|
93
|
+
stderr: "command not found"
|
|
94
|
+
}));
|
|
95
|
+
const toolName = 'nonexistent';
|
|
96
|
+
await expect(checkCommand(`${toolName} --version`, toolName)).rejects.toThrow(new MissingRequirementError(toolName));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("executeCommand throws an error if the command fails", async () => {
|
|
100
|
+
vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.reject(new Error("Execution failed")));
|
|
101
|
+
await expect(executeCommand({
|
|
102
|
+
linux: "echo hello",
|
|
103
|
+
win32: "echo hello",
|
|
104
|
+
darwin: "echo hello",
|
|
105
|
+
},
|
|
106
|
+
"echo")).rejects.toThrow("Execution failed");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("throws error when command execution fails", async () => {
|
|
110
|
+
vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.reject(new Error("Execution error.")));
|
|
111
|
+
await expect(executeCommand({
|
|
112
|
+
linux: "echo no toolname",
|
|
113
|
+
win32: "echo no toolname",
|
|
114
|
+
darwin: "echo no toolname",
|
|
115
|
+
})).rejects.toThrow(
|
|
116
|
+
"Error executing echo no toolname: Execution error."
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("throws error when command execution fails (toolname)", async () => {
|
|
121
|
+
vi.mocked(util.promisify).mockReturnValueOnce(() => Promise.reject(new Error("Execution error.")));
|
|
122
|
+
await expect(executeCommand({
|
|
123
|
+
linux: "echo linux",
|
|
124
|
+
win32: "echo win32",
|
|
125
|
+
darwin: "echo darwin",
|
|
126
|
+
},
|
|
127
|
+
"echo")).rejects.toThrow(
|
|
128
|
+
"Error executing echo: Execution error."
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("throws an error for unsupported platform in executeCommand", () => {
|
|
133
|
+
const unsupportedPlatform = "unsupportedOS";
|
|
134
|
+
const originalPlatform = process.platform;
|
|
135
|
+
Object.defineProperty(process, "platform", {
|
|
136
|
+
value: unsupportedPlatform,
|
|
137
|
+
});
|
|
138
|
+
const cmdsByPlatform = {
|
|
139
|
+
linux: "echo Linux",
|
|
140
|
+
darwin: "echo macOS",
|
|
141
|
+
win32: "echo Windows",
|
|
142
|
+
};
|
|
143
|
+
expect(executeCommand(cmdsByPlatform)).rejects.toThrow(
|
|
144
|
+
`Unsupported platform: ${unsupportedPlatform}.`
|
|
145
|
+
);
|
|
146
|
+
Object.defineProperty(process, "platform", { value: originalPlatform });
|
|
147
|
+
});
|
|
148
|
+
});
|