genlayer 0.32.0 → 0.32.1
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 +2 -0
- package/README.md +1 -1
- package/dist/index.js +1379 -221
- package/docs/delegator-guide.md +6 -6
- package/docs/validator-guide.md +49 -18
- package/package.json +2 -2
- package/src/commands/account/create.ts +10 -4
- package/src/commands/account/export.ts +106 -0
- package/src/commands/account/import.ts +85 -31
- package/src/commands/account/index.ts +77 -18
- package/src/commands/account/list.ts +34 -0
- package/src/commands/account/lock.ts +16 -7
- package/src/commands/account/remove.ts +30 -0
- package/src/commands/account/send.ts +14 -8
- package/src/commands/account/show.ts +22 -8
- package/src/commands/account/unlock.ts +20 -10
- package/src/commands/account/use.ts +21 -0
- package/src/commands/network/index.ts +18 -3
- package/src/commands/network/setNetwork.ts +38 -22
- package/src/commands/staking/StakingAction.ts +51 -19
- package/src/commands/staking/delegatorJoin.ts +2 -0
- package/src/commands/staking/index.ts +29 -2
- package/src/commands/staking/setIdentity.ts +5 -0
- package/src/commands/staking/stakingInfo.ts +29 -21
- package/src/commands/staking/wizard.ts +802 -0
- package/src/lib/actions/BaseAction.ts +71 -45
- package/src/lib/config/ConfigFileManager.ts +143 -0
- package/src/lib/config/KeychainManager.ts +23 -7
- package/tests/actions/create.test.ts +30 -10
- package/tests/actions/deploy.test.ts +7 -0
- package/tests/actions/lock.test.ts +28 -8
- package/tests/actions/unlock.test.ts +44 -26
- package/tests/commands/account.test.ts +43 -18
- package/tests/commands/network.test.ts +10 -10
- package/tests/commands/staking.test.ts +122 -0
- package/tests/libs/baseAction.test.ts +64 -41
- package/tests/libs/configFileManager.test.ts +8 -1
- package/tests/libs/keychainManager.test.ts +56 -16
- package/src/lib/interfaces/KeystoreData.ts +0 -5
|
@@ -28,10 +28,18 @@ describe("BaseAction", () => {
|
|
|
28
28
|
let consoleErrorSpy: any;
|
|
29
29
|
let processExitSpy: any;
|
|
30
30
|
|
|
31
|
+
// Standard web3 keystore format
|
|
31
32
|
const mockKeystoreData = {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
address: "1234567890123456789012345678901234567890",
|
|
34
|
+
crypto: {
|
|
35
|
+
cipher: "aes-128-ctr",
|
|
36
|
+
ciphertext: "test",
|
|
37
|
+
cipherparams: {iv: "test"},
|
|
38
|
+
kdf: "scrypt",
|
|
39
|
+
kdfparams: {},
|
|
40
|
+
mac: "test"
|
|
41
|
+
},
|
|
42
|
+
version: 3
|
|
35
43
|
};
|
|
36
44
|
|
|
37
45
|
const mockWallet = {
|
|
@@ -81,8 +89,11 @@ describe("BaseAction", () => {
|
|
|
81
89
|
vi.spyOn(baseAction as any, "getConfigByKey").mockReturnValue("./test-keypair.json");
|
|
82
90
|
vi.spyOn(baseAction as any, "getFilePath").mockImplementation(() => "./test-keypair.json");
|
|
83
91
|
vi.spyOn(baseAction as any, "writeConfig").mockImplementation(() => {});
|
|
84
|
-
vi.spyOn(baseAction as any, "getConfig").mockReturnValue({});
|
|
85
|
-
|
|
92
|
+
vi.spyOn(baseAction as any, "getConfig").mockReturnValue({activeAccount: "default"});
|
|
93
|
+
vi.spyOn(baseAction as any, "resolveAccountName").mockReturnValue("default");
|
|
94
|
+
vi.spyOn(baseAction as any, "getKeystorePath").mockReturnValue("/mocked/home/.genlayer/keystores/default.json");
|
|
95
|
+
vi.spyOn(baseAction as any, "getActiveAccount").mockReturnValue("default");
|
|
96
|
+
|
|
86
97
|
// Mock keychainManager methods
|
|
87
98
|
vi.spyOn(baseAction["keychainManager"], "isKeychainAvailable").mockResolvedValue(false);
|
|
88
99
|
vi.spyOn(baseAction["keychainManager"], "getPrivateKey").mockResolvedValue(null);
|
|
@@ -262,16 +273,16 @@ describe("BaseAction", () => {
|
|
|
262
273
|
const account = await baseAction["getAccount"](false);
|
|
263
274
|
|
|
264
275
|
expect((account as any).privateKey).toBe(mockWallet.privateKey);
|
|
265
|
-
expect(existsSync).toHaveBeenCalledWith("
|
|
266
|
-
expect(readFileSync).toHaveBeenCalledWith("
|
|
276
|
+
expect(existsSync).toHaveBeenCalledWith("/mocked/home/.genlayer/keystores/default.json");
|
|
277
|
+
expect(readFileSync).toHaveBeenCalledWith("/mocked/home/.genlayer/keystores/default.json", "utf-8");
|
|
267
278
|
});
|
|
268
279
|
|
|
269
280
|
test("should return address when called with readOnly=true", async () => {
|
|
270
281
|
const address = await baseAction["getAccount"](true);
|
|
271
|
-
|
|
282
|
+
|
|
272
283
|
expect(address).toBe(mockKeystoreData.address);
|
|
273
|
-
expect(existsSync).toHaveBeenCalledWith("
|
|
274
|
-
expect(readFileSync).toHaveBeenCalledWith("
|
|
284
|
+
expect(existsSync).toHaveBeenCalledWith("/mocked/home/.genlayer/keystores/default.json");
|
|
285
|
+
expect(readFileSync).toHaveBeenCalledWith("/mocked/home/.genlayer/keystores/default.json", "utf-8");
|
|
275
286
|
});
|
|
276
287
|
|
|
277
288
|
test("should create new keypair when keystore file does not exist", async () => {
|
|
@@ -285,7 +296,7 @@ describe("BaseAction", () => {
|
|
|
285
296
|
|
|
286
297
|
expect((account as any).privateKey).toBe(mockWallet.privateKey);
|
|
287
298
|
expect(inquirer.prompt).toHaveBeenCalledWith(expect.arrayContaining([
|
|
288
|
-
expect.objectContaining({message: chalk.yellow("
|
|
299
|
+
expect.objectContaining({message: chalk.yellow("Account 'default' not found. Would you like to create it?")})
|
|
289
300
|
]));
|
|
290
301
|
});
|
|
291
302
|
|
|
@@ -304,7 +315,7 @@ describe("BaseAction", () => {
|
|
|
304
315
|
const account = await baseAction["getAccount"](false);
|
|
305
316
|
|
|
306
317
|
expect((account as any).privateKey).toBe(mockWallet.privateKey);
|
|
307
|
-
expect(baseAction["keychainManager"].getPrivateKey).
|
|
318
|
+
expect(baseAction["keychainManager"].getPrivateKey).toHaveBeenCalledWith("default");
|
|
308
319
|
expect(inquirer.prompt).not.toHaveBeenCalled();
|
|
309
320
|
});
|
|
310
321
|
|
|
@@ -320,14 +331,14 @@ describe("BaseAction", () => {
|
|
|
320
331
|
expect((account as any).privateKey).toBe(mockWallet.privateKey);
|
|
321
332
|
expect(mockSpinner.fail).toHaveBeenCalledWith(chalk.red("Invalid keystore format. Expected encrypted keystore file."));
|
|
322
333
|
expect(inquirer.prompt).toHaveBeenCalledWith(expect.arrayContaining([
|
|
323
|
-
expect.objectContaining({message:
|
|
334
|
+
expect.objectContaining({message: "Would you like to recreate account 'default'?"})
|
|
324
335
|
]));
|
|
325
336
|
});
|
|
326
337
|
|
|
327
338
|
test("should decrypt keystore successfully on first attempt", async () => {
|
|
328
339
|
vi.mocked(inquirer.prompt).mockResolvedValue({password: "correct-password"});
|
|
329
340
|
|
|
330
|
-
const result = await baseAction["decryptKeystore"](mockKeystoreData);
|
|
341
|
+
const result = await baseAction["decryptKeystore"](JSON.stringify(mockKeystoreData));
|
|
331
342
|
|
|
332
343
|
expect(result).toBe(mockWallet.privateKey);
|
|
333
344
|
expect(inquirer.prompt).toHaveBeenCalledWith(expect.arrayContaining([
|
|
@@ -339,12 +350,12 @@ describe("BaseAction", () => {
|
|
|
339
350
|
vi.mocked(ethers.Wallet.fromEncryptedJson)
|
|
340
351
|
.mockRejectedValueOnce(new Error("Incorrect password"))
|
|
341
352
|
.mockResolvedValueOnce(mockWallet as any);
|
|
342
|
-
|
|
353
|
+
|
|
343
354
|
vi.mocked(inquirer.prompt)
|
|
344
355
|
.mockResolvedValueOnce({password: "wrong-password"})
|
|
345
356
|
.mockResolvedValueOnce({password: "correct-password"});
|
|
346
357
|
|
|
347
|
-
const result = await baseAction["decryptKeystore"](mockKeystoreData);
|
|
358
|
+
const result = await baseAction["decryptKeystore"](JSON.stringify(mockKeystoreData));
|
|
348
359
|
|
|
349
360
|
expect(result).toBe(mockWallet.privateKey);
|
|
350
361
|
expect(inquirer.prompt).toHaveBeenCalledTimes(2);
|
|
@@ -357,8 +368,8 @@ describe("BaseAction", () => {
|
|
|
357
368
|
vi.mocked(ethers.Wallet.fromEncryptedJson).mockRejectedValue(new Error("Incorrect password"));
|
|
358
369
|
vi.mocked(inquirer.prompt).mockResolvedValue({password: "wrong-password"});
|
|
359
370
|
|
|
360
|
-
await expect(baseAction["decryptKeystore"](mockKeystoreData)).rejects.toThrow("process exited");
|
|
361
|
-
|
|
371
|
+
await expect(baseAction["decryptKeystore"](JSON.stringify(mockKeystoreData))).rejects.toThrow("process exited");
|
|
372
|
+
|
|
362
373
|
expect(inquirer.prompt).toHaveBeenCalledTimes(3);
|
|
363
374
|
expect(mockSpinner.fail).toHaveBeenCalledWith(chalk.red("Maximum password attempts exceeded (3/3)."));
|
|
364
375
|
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
@@ -370,22 +381,22 @@ describe("BaseAction", () => {
|
|
|
370
381
|
.mockResolvedValueOnce({password: "test-password"})
|
|
371
382
|
.mockResolvedValueOnce({password: "test-password"});
|
|
372
383
|
|
|
373
|
-
const result = await baseAction["
|
|
384
|
+
const result = await baseAction["createKeypairByName"]("test-account", false);
|
|
374
385
|
|
|
375
386
|
expect(result).toBe(mockWallet.privateKey);
|
|
376
387
|
expect(ethers.Wallet.createRandom).toHaveBeenCalled();
|
|
377
388
|
expect(mockWallet.encrypt).toHaveBeenCalledWith("test-password");
|
|
378
389
|
expect(writeFileSync).toHaveBeenCalled();
|
|
379
|
-
expect(baseAction["keychainManager"].removePrivateKey).
|
|
390
|
+
expect(baseAction["keychainManager"].removePrivateKey).toHaveBeenCalledWith("test-account");
|
|
380
391
|
});
|
|
381
392
|
|
|
382
|
-
test("should fail when
|
|
393
|
+
test("should fail when account exists and overwrite is false", async () => {
|
|
383
394
|
vi.mocked(existsSync).mockReturnValue(true);
|
|
384
395
|
|
|
385
|
-
await expect(baseAction["
|
|
386
|
-
|
|
396
|
+
await expect(baseAction["createKeypairByName"]("test-account", false)).rejects.toThrow("process exited");
|
|
397
|
+
|
|
387
398
|
expect(mockSpinner.fail).toHaveBeenCalledWith(
|
|
388
|
-
chalk.red("
|
|
399
|
+
chalk.red("Account 'test-account' already exists. Use '--overwrite' to replace it.")
|
|
389
400
|
);
|
|
390
401
|
});
|
|
391
402
|
|
|
@@ -395,8 +406,8 @@ describe("BaseAction", () => {
|
|
|
395
406
|
.mockResolvedValueOnce({password: "password1"})
|
|
396
407
|
.mockResolvedValueOnce({password: "password2"});
|
|
397
408
|
|
|
398
|
-
await expect(baseAction["
|
|
399
|
-
|
|
409
|
+
await expect(baseAction["createKeypairByName"]("test-account", false)).rejects.toThrow("process exited");
|
|
410
|
+
|
|
400
411
|
expect(mockSpinner.fail).toHaveBeenCalledWith(chalk.red("Passwords do not match"));
|
|
401
412
|
});
|
|
402
413
|
|
|
@@ -406,50 +417,62 @@ describe("BaseAction", () => {
|
|
|
406
417
|
.mockResolvedValueOnce({password: "short"})
|
|
407
418
|
.mockResolvedValueOnce({password: "short"});
|
|
408
419
|
|
|
409
|
-
await expect(baseAction["
|
|
410
|
-
|
|
420
|
+
await expect(baseAction["createKeypairByName"]("test-account", false)).rejects.toThrow("process exited");
|
|
421
|
+
|
|
411
422
|
expect(mockSpinner.fail).toHaveBeenCalledWith(chalk.red("Password must be at least 8 characters long"));
|
|
412
423
|
});
|
|
413
424
|
|
|
414
|
-
test("should overwrite existing
|
|
425
|
+
test("should overwrite existing account when overwrite is true", async () => {
|
|
415
426
|
vi.mocked(existsSync).mockReturnValue(true);
|
|
416
427
|
vi.mocked(inquirer.prompt)
|
|
417
428
|
.mockResolvedValueOnce({password: "test-password"})
|
|
418
429
|
.mockResolvedValueOnce({password: "test-password"});
|
|
419
430
|
|
|
420
|
-
const result = await baseAction["
|
|
431
|
+
const result = await baseAction["createKeypairByName"]("test-account", true);
|
|
421
432
|
|
|
422
433
|
expect(result).toBe(mockWallet.privateKey);
|
|
423
434
|
expect(writeFileSync).toHaveBeenCalled();
|
|
424
|
-
expect(baseAction["keychainManager"].removePrivateKey).
|
|
435
|
+
expect(baseAction["keychainManager"].removePrivateKey).toHaveBeenCalledWith("test-account");
|
|
425
436
|
});
|
|
426
437
|
|
|
427
438
|
test("should return true for valid keystore format", () => {
|
|
439
|
+
// Standard web3 keystore format
|
|
440
|
+
const validKeystore = {
|
|
441
|
+
address: "1234567890123456789012345678901234567890",
|
|
442
|
+
crypto: {cipher: "aes-128-ctr", ciphertext: "test"},
|
|
443
|
+
version: 3,
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const result = baseAction["isValidKeystoreFormat"](validKeystore);
|
|
447
|
+
expect(result).toBe(true);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
test("should return true for keystore with uppercase Crypto field", () => {
|
|
451
|
+
// Some tools use uppercase 'Crypto'
|
|
428
452
|
const validKeystore = {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
453
|
+
address: "1234567890123456789012345678901234567890",
|
|
454
|
+
Crypto: {cipher: "aes-128-ctr", ciphertext: "test"},
|
|
455
|
+
version: 3,
|
|
432
456
|
};
|
|
433
457
|
|
|
434
458
|
const result = baseAction["isValidKeystoreFormat"](validKeystore);
|
|
435
459
|
expect(result).toBe(true);
|
|
436
460
|
});
|
|
437
461
|
|
|
438
|
-
test("should return false for
|
|
462
|
+
test("should return false for keystore missing crypto field", () => {
|
|
439
463
|
const invalidKeystore = {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
address: "0x1234567890123456789012345678901234567890",
|
|
464
|
+
address: "1234567890123456789012345678901234567890",
|
|
465
|
+
version: 3,
|
|
443
466
|
};
|
|
444
467
|
|
|
445
468
|
const result = baseAction["isValidKeystoreFormat"](invalidKeystore);
|
|
446
469
|
expect(result).toBe(false);
|
|
447
470
|
});
|
|
448
471
|
|
|
449
|
-
test("should return false for keystore missing
|
|
472
|
+
test("should return false for keystore missing address", () => {
|
|
450
473
|
const invalidKeystore = {
|
|
451
|
-
|
|
452
|
-
|
|
474
|
+
crypto: {cipher: "aes-128-ctr"},
|
|
475
|
+
version: 3,
|
|
453
476
|
};
|
|
454
477
|
|
|
455
478
|
const result = baseAction["isValidKeystoreFormat"](invalidKeystore);
|
|
@@ -10,6 +10,7 @@ vi.mock("os")
|
|
|
10
10
|
|
|
11
11
|
describe("ConfigFileManager", () => {
|
|
12
12
|
const mockFolderPath = "/mocked/home/.genlayer";
|
|
13
|
+
const mockKeystoresPath = `${mockFolderPath}/keystores`;
|
|
13
14
|
const mockConfigFilePath = `${mockFolderPath}/genlayer-config.json`;
|
|
14
15
|
|
|
15
16
|
let configFileManager: ConfigFileManager;
|
|
@@ -17,6 +18,8 @@ describe("ConfigFileManager", () => {
|
|
|
17
18
|
beforeEach(() => {
|
|
18
19
|
vi.clearAllMocks();
|
|
19
20
|
vi.mocked(os.homedir).mockReturnValue("/mocked/home");
|
|
21
|
+
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
22
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({}));
|
|
20
23
|
configFileManager = new ConfigFileManager();
|
|
21
24
|
});
|
|
22
25
|
|
|
@@ -26,12 +29,16 @@ describe("ConfigFileManager", () => {
|
|
|
26
29
|
|
|
27
30
|
test("ensures folder and config file are created if they don't exist", () => {
|
|
28
31
|
vi.mocked(fs.existsSync).mockReturnValue(false);
|
|
32
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({}));
|
|
29
33
|
|
|
30
34
|
new ConfigFileManager();
|
|
31
35
|
|
|
32
36
|
expect(fs.existsSync).toHaveBeenCalledWith(mockFolderPath);
|
|
33
37
|
expect(fs.mkdirSync).toHaveBeenCalledWith(mockFolderPath, { recursive: true });
|
|
34
38
|
|
|
39
|
+
expect(fs.existsSync).toHaveBeenCalledWith(mockKeystoresPath);
|
|
40
|
+
expect(fs.mkdirSync).toHaveBeenCalledWith(mockKeystoresPath, { recursive: true });
|
|
41
|
+
|
|
35
42
|
expect(fs.existsSync).toHaveBeenCalledWith(mockConfigFilePath);
|
|
36
43
|
expect(fs.writeFileSync).toHaveBeenCalledWith(mockConfigFilePath, JSON.stringify({}, null, 2));
|
|
37
44
|
});
|
|
@@ -39,11 +46,11 @@ describe("ConfigFileManager", () => {
|
|
|
39
46
|
test("does not recreate folder or config file if they exist", () => {
|
|
40
47
|
vi.clearAllMocks();
|
|
41
48
|
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
49
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({}));
|
|
42
50
|
|
|
43
51
|
new ConfigFileManager();
|
|
44
52
|
|
|
45
53
|
expect(fs.mkdirSync).not.toHaveBeenCalled();
|
|
46
|
-
expect(fs.writeFileSync).not.toHaveBeenCalled();
|
|
47
54
|
});
|
|
48
55
|
|
|
49
56
|
test("getFolderPath returns the correct folder path", () => {
|
|
@@ -41,17 +41,17 @@ describe("KeychainManager", () => {
|
|
|
41
41
|
const privateKey = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890";
|
|
42
42
|
vi.mocked(keytar.setPassword).mockResolvedValue();
|
|
43
43
|
|
|
44
|
-
await keychainManager.storePrivateKey(privateKey);
|
|
44
|
+
await keychainManager.storePrivateKey("main", privateKey);
|
|
45
45
|
|
|
46
|
-
expect(keytar.setPassword).toHaveBeenCalledWith("genlayer-cli", "
|
|
46
|
+
expect(keytar.setPassword).toHaveBeenCalledWith("genlayer-cli", "account:main", privateKey);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
test("handles storage error", async () => {
|
|
50
50
|
const privateKey = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890";
|
|
51
51
|
vi.mocked(keytar.setPassword).mockRejectedValue(new Error("Storage failed"));
|
|
52
52
|
|
|
53
|
-
await expect(keychainManager.storePrivateKey(privateKey)).rejects.toThrow("Storage failed");
|
|
54
|
-
expect(keytar.setPassword).toHaveBeenCalledWith("genlayer-cli", "
|
|
53
|
+
await expect(keychainManager.storePrivateKey("main", privateKey)).rejects.toThrow("Storage failed");
|
|
54
|
+
expect(keytar.setPassword).toHaveBeenCalledWith("genlayer-cli", "account:main", privateKey);
|
|
55
55
|
});
|
|
56
56
|
});
|
|
57
57
|
|
|
@@ -60,26 +60,26 @@ describe("KeychainManager", () => {
|
|
|
60
60
|
const expectedKey = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890";
|
|
61
61
|
vi.mocked(keytar.getPassword).mockResolvedValue(expectedKey);
|
|
62
62
|
|
|
63
|
-
const result = await keychainManager.getPrivateKey();
|
|
63
|
+
const result = await keychainManager.getPrivateKey("main");
|
|
64
64
|
|
|
65
65
|
expect(result).toBe(expectedKey);
|
|
66
|
-
expect(keytar.getPassword).toHaveBeenCalledWith("genlayer-cli", "
|
|
66
|
+
expect(keytar.getPassword).toHaveBeenCalledWith("genlayer-cli", "account:main");
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
test("returns null when private key does not exist", async () => {
|
|
70
70
|
vi.mocked(keytar.getPassword).mockResolvedValue(null);
|
|
71
71
|
|
|
72
|
-
const result = await keychainManager.getPrivateKey();
|
|
72
|
+
const result = await keychainManager.getPrivateKey("main");
|
|
73
73
|
|
|
74
74
|
expect(result).toBeNull();
|
|
75
|
-
expect(keytar.getPassword).toHaveBeenCalledWith("genlayer-cli", "
|
|
75
|
+
expect(keytar.getPassword).toHaveBeenCalledWith("genlayer-cli", "account:main");
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
test("handles retrieval error", async () => {
|
|
79
79
|
vi.mocked(keytar.getPassword).mockRejectedValue(new Error("Retrieval failed"));
|
|
80
80
|
|
|
81
|
-
await expect(keychainManager.getPrivateKey()).rejects.toThrow("Retrieval failed");
|
|
82
|
-
expect(keytar.getPassword).toHaveBeenCalledWith("genlayer-cli", "
|
|
81
|
+
await expect(keychainManager.getPrivateKey("main")).rejects.toThrow("Retrieval failed");
|
|
82
|
+
expect(keytar.getPassword).toHaveBeenCalledWith("genlayer-cli", "account:main");
|
|
83
83
|
});
|
|
84
84
|
});
|
|
85
85
|
|
|
@@ -87,26 +87,66 @@ describe("KeychainManager", () => {
|
|
|
87
87
|
test("successfully removes private key", async () => {
|
|
88
88
|
vi.mocked(keytar.deletePassword).mockResolvedValue(true);
|
|
89
89
|
|
|
90
|
-
const result = await keychainManager.removePrivateKey();
|
|
90
|
+
const result = await keychainManager.removePrivateKey("main");
|
|
91
91
|
|
|
92
92
|
expect(result).toBe(true);
|
|
93
|
-
expect(keytar.deletePassword).toHaveBeenCalledWith("genlayer-cli", "
|
|
93
|
+
expect(keytar.deletePassword).toHaveBeenCalledWith("genlayer-cli", "account:main");
|
|
94
94
|
});
|
|
95
95
|
|
|
96
96
|
test("returns false when key does not exist", async () => {
|
|
97
97
|
vi.mocked(keytar.deletePassword).mockResolvedValue(false);
|
|
98
98
|
|
|
99
|
-
const result = await keychainManager.removePrivateKey();
|
|
99
|
+
const result = await keychainManager.removePrivateKey("main");
|
|
100
100
|
|
|
101
101
|
expect(result).toBe(false);
|
|
102
|
-
expect(keytar.deletePassword).toHaveBeenCalledWith("genlayer-cli", "
|
|
102
|
+
expect(keytar.deletePassword).toHaveBeenCalledWith("genlayer-cli", "account:main");
|
|
103
103
|
});
|
|
104
104
|
|
|
105
105
|
test("handles removal error", async () => {
|
|
106
106
|
vi.mocked(keytar.deletePassword).mockRejectedValue(new Error("Removal failed"));
|
|
107
107
|
|
|
108
|
-
await expect(keychainManager.removePrivateKey()).rejects.toThrow("Removal failed");
|
|
109
|
-
expect(keytar.deletePassword).toHaveBeenCalledWith("genlayer-cli", "
|
|
108
|
+
await expect(keychainManager.removePrivateKey("main")).rejects.toThrow("Removal failed");
|
|
109
|
+
expect(keytar.deletePassword).toHaveBeenCalledWith("genlayer-cli", "account:main");
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe("listUnlockedAccounts", () => {
|
|
114
|
+
test("returns list of unlocked account names", async () => {
|
|
115
|
+
vi.mocked(keytar.findCredentials).mockResolvedValue([
|
|
116
|
+
{account: "account:main", password: "key1"},
|
|
117
|
+
{account: "account:validator", password: "key2"},
|
|
118
|
+
{account: "other:something", password: "key3"},
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
const result = await keychainManager.listUnlockedAccounts();
|
|
122
|
+
|
|
123
|
+
expect(result).toEqual(["main", "validator"]);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("returns empty array when no accounts", async () => {
|
|
127
|
+
vi.mocked(keytar.findCredentials).mockResolvedValue([]);
|
|
128
|
+
|
|
129
|
+
const result = await keychainManager.listUnlockedAccounts();
|
|
130
|
+
|
|
131
|
+
expect(result).toEqual([]);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe("isAccountUnlocked", () => {
|
|
136
|
+
test("returns true when account is unlocked", async () => {
|
|
137
|
+
vi.mocked(keytar.getPassword).mockResolvedValue("some-key");
|
|
138
|
+
|
|
139
|
+
const result = await keychainManager.isAccountUnlocked("main");
|
|
140
|
+
|
|
141
|
+
expect(result).toBe(true);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("returns false when account is locked", async () => {
|
|
145
|
+
vi.mocked(keytar.getPassword).mockResolvedValue(null);
|
|
146
|
+
|
|
147
|
+
const result = await keychainManager.isAccountUnlocked("main");
|
|
148
|
+
|
|
149
|
+
expect(result).toBe(false);
|
|
110
150
|
});
|
|
111
151
|
});
|
|
112
152
|
});
|