diff --git a/src/commands/contracts/call.ts b/src/commands/contracts/call.ts index 5b00cdd3..d0b095cb 100644 --- a/src/commands/contracts/call.ts +++ b/src/commands/contracts/call.ts @@ -1,7 +1,5 @@ -import { createClient, createAccount } from "genlayer-js"; import { simulator } from "genlayer-js/chains"; import type { GenLayerClient } from "genlayer-js/types"; -import { getPrivateKey } from "../../lib/accounts/getPrivateKey"; import { BaseAction } from "../../lib/actions/BaseAction"; export interface CallOptions { @@ -9,15 +7,8 @@ export interface CallOptions { } export class CallAction extends BaseAction{ - private genlayerClient: GenLayerClient; - constructor() { super(); - this.genlayerClient = createClient({ - chain: simulator, - endpoint: process.env.VITE_JSON_RPC_SERVER_URL, - account: createAccount(getPrivateKey() as any), - }); } async call({ @@ -29,9 +20,10 @@ export class CallAction extends BaseAction{ method: string; args: any[]; }): Promise { + const client = await this.getClient(); this.startSpinner(`Calling method ${method} on contract at ${contractAddress}...`); - const contractSchema = await this.genlayerClient.getContractSchema(contractAddress); + const contractSchema = await client.getContractSchema(contractAddress); if(!contractSchema.methods.hasOwnProperty(method)){ this.failSpinner(`method ${method} not found.`); @@ -50,7 +42,8 @@ export class CallAction extends BaseAction{ private async executeRead(contractAddress: string, method: string, args: any[]): Promise { try { - const result = await this.genlayerClient.readContract({ + const client = await this.getClient(); + const result = await client.readContract({ address: contractAddress as any, functionName: method, args, @@ -63,13 +56,14 @@ export class CallAction extends BaseAction{ private async executeWrite(contractAddress: string, method: string, args: any[]): Promise { try { - const hash = await this.genlayerClient.writeContract({ + const client = await this.getClient(); + const hash = await client.writeContract({ address: contractAddress as any, functionName: method, args, value: 0n, }); - const result = await this.genlayerClient.waitForTransactionReceipt({ + const result = await client.waitForTransactionReceipt({ hash, retries: 15, interval: 2000, diff --git a/src/commands/contracts/deploy.ts b/src/commands/contracts/deploy.ts index 66e3aef3..d2971955 100644 --- a/src/commands/contracts/deploy.ts +++ b/src/commands/contracts/deploy.ts @@ -1,9 +1,7 @@ import fs from "fs"; import path from "path"; -import { createClient, createAccount } from "genlayer-js"; import { simulator } from "genlayer-js/chains"; import type { GenLayerClient } from "genlayer-js/types"; -import { getPrivateKey } from "../../lib/accounts/getPrivateKey"; import { BaseAction } from "../../lib/actions/BaseAction"; import { pathToFileURL } from "url"; import { TransactionStatus } from "genlayer-js/types"; @@ -15,16 +13,10 @@ export interface DeployOptions { } export class DeployAction extends BaseAction { - private genlayerClient: GenLayerClient; private readonly deployDir = path.resolve(process.cwd(), "deploy"); constructor() { super(); - this.genlayerClient = createClient({ - chain: simulator, - endpoint: process.env.VITE_JSON_RPC_SERVER_URL, - account: createAccount(getPrivateKey() as any), - }); } private readContractCode(contractPath: string): string { @@ -63,7 +55,8 @@ export class DeployAction extends BaseAction { this.failSpinner(`No "default" function found in: ${filePath}`); return } - await module.default(this.genlayerClient); + const client = await this.getClient(); + await module.default(client); this.succeedSpinner(`Successfully executed: ${filePath}`); } catch (error) { this.failSpinner(`Error executing: ${filePath}`, error); @@ -112,8 +105,9 @@ export class DeployAction extends BaseAction { async deploy(options: DeployOptions): Promise { try { + const client = await this.getClient(); this.startSpinner("Setting up the deployment environment..."); - await this.genlayerClient.initializeConsensusSmartContract(); + await client.initializeConsensusSmartContract(); if (!options.contract) { this.failSpinner("No contract specified for deployment."); @@ -133,8 +127,8 @@ export class DeployAction extends BaseAction { this.setSpinnerText("Starting contract deployment..."); this.log("Deployment Parameters:", deployParams); - const hash = (await this.genlayerClient.deployContract(deployParams)) as any; - const result = await this.genlayerClient.waitForTransactionReceipt({ + const hash = (await client.deployContract(deployParams)) as any; + const result = await client.waitForTransactionReceipt({ hash, retries: 15, interval: 2000, diff --git a/src/commands/keygen/create.ts b/src/commands/keygen/create.ts index 8509d090..1878d31c 100644 --- a/src/commands/keygen/create.ts +++ b/src/commands/keygen/create.ts @@ -1,5 +1,3 @@ -import { writeFileSync, existsSync } from "fs"; -import { ethers } from "ethers"; import { BaseAction } from "../../lib/actions/BaseAction"; export interface CreateKeypairOptions { @@ -7,36 +5,18 @@ export interface CreateKeypairOptions { overwrite: boolean; } -export class KeypairCreator extends BaseAction{ - +export class KeypairCreator extends BaseAction { constructor() { - super() + super(); } createKeypairAction(options: CreateKeypairOptions) { try { this.startSpinner(`Creating keypair...`); - const outputPath = this.getFilePath(options.output); - - if(existsSync(outputPath) && !options.overwrite) { - this.failSpinner( - `The file at ${outputPath} already exists. Use the '--overwrite' option to replace it.` - ); - return; - } - - const wallet = ethers.Wallet.createRandom(); - const keypairData = { - address: wallet.address, - privateKey: wallet.privateKey, - }; - - writeFileSync(outputPath, JSON.stringify(keypairData, null, 2)); - - this.writeConfig('keyPairPath', outputPath); - this.succeedSpinner(`Keypair successfully created and saved to: ${outputPath}`); + this.keypairManager.createKeypair(options.output, options.overwrite); + this.succeedSpinner(`Keypair successfully created and saved to: ${options.output}`); } catch (error) { - this.failSpinner("Failed to generate keypair:", error); + this.failSpinner("Failed to generate keypair", error); } } } \ No newline at end of file diff --git a/src/lib/accounts/KeypairManager.ts b/src/lib/accounts/KeypairManager.ts new file mode 100644 index 00000000..3d4831dc --- /dev/null +++ b/src/lib/accounts/KeypairManager.ts @@ -0,0 +1,43 @@ +import { writeFileSync, existsSync, readFileSync } from "fs"; +import { ethers } from "ethers"; +import path from "path"; +import { ConfigFileManager } from "../config/ConfigFileManager"; + +export class KeypairManager extends ConfigFileManager { + constructor() { + super(); + } + + getPrivateKey(): string | undefined { + const keypairPath = this.getConfigByKey("keyPairPath"); + + if (!keypairPath || !existsSync(keypairPath)) { + return "" + } + + const keypairData = JSON.parse(readFileSync(keypairPath, "utf-8")); + + if (!keypairData.privateKey) { + return "" + } + + return keypairData.privateKey; + } + + createKeypair(outputPath = "./keypair.json", overwrite: boolean = false): void { + const finalOutputPath = this.getFilePath(outputPath); + + if(existsSync(finalOutputPath) && !overwrite) { + throw new Error(`The file at ${finalOutputPath} already exists. Use the '--overwrite' option to replace it.`); + } + + const wallet = ethers.Wallet.createRandom(); + const keypairData = { + address: wallet.address, + privateKey: wallet.privateKey, + }; + + writeFileSync(finalOutputPath, JSON.stringify(keypairData, null, 2)); + this.writeConfig('keyPairPath', finalOutputPath); + } +} \ No newline at end of file diff --git a/src/lib/accounts/getPrivateKey.ts b/src/lib/accounts/getPrivateKey.ts deleted file mode 100644 index 42001249..00000000 --- a/src/lib/accounts/getPrivateKey.ts +++ /dev/null @@ -1,21 +0,0 @@ -import fs from "fs"; -import { ConfigFileManager } from "../config/ConfigFileManager"; - -export function getPrivateKey(): string { - const configFileManager: ConfigFileManager = new ConfigFileManager(); - const keypairPath = configFileManager.getConfigByKey("keyPairPath"); - - if (!keypairPath || !fs.existsSync(keypairPath)) { - console.error("Keypair file not found. Please generate or specify a valid keypair path."); - process.exit(1); - } - - const keypairData = JSON.parse(fs.readFileSync(keypairPath, "utf-8")); - - if (!keypairData.privateKey) { - console.error("Invalid keypair file. Private key is missing."); - process.exit(1); - } - - return keypairData.privateKey; -} \ No newline at end of file diff --git a/src/lib/actions/BaseAction.ts b/src/lib/actions/BaseAction.ts index 6702a803..2ce4a211 100644 --- a/src/lib/actions/BaseAction.ts +++ b/src/lib/actions/BaseAction.ts @@ -2,14 +2,41 @@ import { ConfigFileManager } from "../../lib/config/ConfigFileManager"; import ora, { Ora } from "ora"; import chalk from "chalk"; import inquirer from "inquirer"; - +import { KeypairManager } from "../accounts/KeypairManager"; +import { createClient, createAccount } from "genlayer-js"; +import { localnet } from "genlayer-js/chains"; +import type { GenLayerClient } from "genlayer-js/types"; export class BaseAction extends ConfigFileManager { + protected keypairManager: KeypairManager; private spinner: Ora; + private _genlayerClient: GenLayerClient | null = null; constructor() { super() this.spinner = ora({ text: "", spinner: "dots" }); + this.keypairManager = new KeypairManager(); + } + + protected async getClient(): Promise> { + if (!this._genlayerClient) { + this._genlayerClient = createClient({ + chain: localnet, + endpoint: process.env.VITE_JSON_RPC_SERVER_URL, + account: createAccount(await this.getPrivateKey() as any), + }); + } + return this._genlayerClient; + } + + protected async getPrivateKey() { + const privateKey = this.keypairManager.getPrivateKey(); + if (privateKey) { + return privateKey; + } + await this.confirmPrompt("Keypair file not found. Would you like to create a new keypair?"); + this.keypairManager.createKeypair(); + return this.keypairManager.getPrivateKey(); } protected async confirmPrompt(message: string): Promise { diff --git a/tests/actions/call.test.ts b/tests/actions/call.test.ts index 6386e05e..1f7a10ca 100644 --- a/tests/actions/call.test.ts +++ b/tests/actions/call.test.ts @@ -1,10 +1,8 @@ import { describe, test, vi, beforeEach, afterEach, expect } from "vitest"; import { createClient, createAccount } from "genlayer-js"; import { CallAction } from "../../src/commands/contracts/call"; -import { getPrivateKey } from "../../src/lib/accounts/getPrivateKey"; vi.mock("genlayer-js"); -vi.mock("../../src/lib/accounts/getPrivateKey"); describe("CallAction", () => { let callActions: CallAction; @@ -21,8 +19,8 @@ describe("CallAction", () => { vi.clearAllMocks(); vi.mocked(createClient).mockReturnValue(mockClient as any); vi.mocked(createAccount).mockReturnValue({ privateKey: mockPrivateKey } as any); - vi.mocked(getPrivateKey).mockReturnValue(mockPrivateKey); callActions = new CallAction(); + vi.spyOn(callActions as any, "getPrivateKey").mockResolvedValue(mockPrivateKey); vi.spyOn(callActions as any, "startSpinner").mockImplementation(() => {}); vi.spyOn(callActions as any, "succeedSpinner").mockImplementation(() => {}); diff --git a/tests/actions/create.test.ts b/tests/actions/create.test.ts index e111c2ba..2eff9449 100644 --- a/tests/actions/create.test.ts +++ b/tests/actions/create.test.ts @@ -20,10 +20,15 @@ describe("KeypairCreator", () => { address: "0xMockedAddress", privateKey: "0xMockedPrivateKey", }; - keypairCreator = new KeypairCreator(); + + const mockKeypairManager = { + createKeypair: vi.fn(), + }; beforeEach(() => { vi.clearAllMocks(); + keypairCreator = new KeypairCreator(); + (keypairCreator as any).keypairManager = mockKeypairManager; vi.spyOn(keypairCreator as any, "startSpinner").mockImplementation(() => {}); vi.spyOn(keypairCreator as any, "succeedSpinner").mockImplementation(() => {}); vi.spyOn(keypairCreator as any, "failSpinner").mockImplementation(() => {}); @@ -43,86 +48,20 @@ describe("KeypairCreator", () => { keypairCreator.createKeypairAction(options); expect(keypairCreator["startSpinner"]).toHaveBeenCalledWith("Creating keypair..."); - expect(ethers.Wallet.createRandom).toHaveBeenCalledTimes(1); - expect(keypairCreator["getFilePath"]).toHaveBeenCalledWith("keypair.json"); - - - expect(writeFileSync).toHaveBeenCalledWith( - "/mocked/path/keypair.json", - JSON.stringify( - { - address: mockWallet.address, - privateKey: mockWallet.privateKey, - }, - null, - 2 - ) - ); - - expect(keypairCreator["writeConfig"]).toHaveBeenCalledWith( - "keyPairPath", - "/mocked/path/keypair.json" - ); - + expect(mockKeypairManager.createKeypair).toHaveBeenCalledWith(options.output, options.overwrite); expect(keypairCreator["succeedSpinner"]).toHaveBeenCalledWith( - "Keypair successfully created and saved to: /mocked/path/keypair.json" - ); - }); - - test("skips creation if file exists and overwrite is false", () => { - vi.mocked(existsSync).mockReturnValue(true); - const options = { output: "keypair.json", overwrite: false }; - - keypairCreator.createKeypairAction(options); - - expect(ethers.Wallet.createRandom).not.toHaveBeenCalled(); - expect(writeFileSync).not.toHaveBeenCalled(); - expect(keypairCreator["failSpinner"]).toHaveBeenCalledWith( - "The file at /mocked/path/keypair.json already exists. Use the '--overwrite' option to replace it." - ); - }); - - test("overwrites the file if overwrite is true", () => { - vi.mocked(existsSync).mockReturnValue(true); - const options = { output: "keypair.json", overwrite: true }; - - keypairCreator.createKeypairAction(options); - - expect(keypairCreator["startSpinner"]).toHaveBeenCalledWith("Creating keypair..."); - expect(ethers.Wallet.createRandom).toHaveBeenCalledTimes(1); - expect(keypairCreator["getFilePath"]).toHaveBeenCalledWith("keypair.json"); - - expect(writeFileSync).toHaveBeenCalledWith( - "/mocked/path/keypair.json", - JSON.stringify( - { - address: mockWallet.address, - privateKey: mockWallet.privateKey, - }, - null, - 2 - ) - ); - - expect(keypairCreator["writeConfig"]).toHaveBeenCalledWith( - "keyPairPath", - "/mocked/path/keypair.json" - ); - - expect(keypairCreator["succeedSpinner"]).toHaveBeenCalledWith( - "Keypair successfully created and saved to: /mocked/path/keypair.json" + "Keypair successfully created and saved to: keypair.json" ); }); test("handles errors during keypair creation", () => { - const mockError = new Error("Mocked write error"); - - vi.mocked(writeFileSync).mockImplementation(() => { + const mockError = new Error("Mocked creation error"); + mockKeypairManager.createKeypair.mockImplementation(() => { throw mockError; }); keypairCreator.createKeypairAction({ output: "keypair.json", overwrite: true }); - expect(keypairCreator["failSpinner"]).toHaveBeenCalledWith("Failed to generate keypair:", mockError); + expect(keypairCreator["failSpinner"]).toHaveBeenCalledWith("Failed to generate keypair", mockError); }); }); diff --git a/tests/actions/deploy.test.ts b/tests/actions/deploy.test.ts index 8e8dfbdd..9df3ccc6 100644 --- a/tests/actions/deploy.test.ts +++ b/tests/actions/deploy.test.ts @@ -2,7 +2,6 @@ import { describe, test, vi, beforeEach, afterEach, expect } from "vitest"; import fs from "fs"; import { createClient, createAccount } from "genlayer-js"; import { DeployAction, DeployOptions } from "../../src/commands/contracts/deploy"; -import { getPrivateKey } from "../../src/lib/accounts/getPrivateKey"; import { buildSync } from "esbuild"; import { pathToFileURL } from "url"; @@ -11,7 +10,6 @@ vi.mock("genlayer-js"); vi.mock("esbuild", () => ({ buildSync: vi.fn(), })); -vi.mock("../../src/lib/accounts/getPrivateKey"); describe("DeployAction", () => { let deployer: DeployAction; @@ -27,8 +25,8 @@ describe("DeployAction", () => { vi.clearAllMocks(); vi.mocked(createClient).mockReturnValue(mockClient as any); vi.mocked(createAccount).mockReturnValue({ privateKey: mockPrivateKey } as any); - vi.mocked(getPrivateKey).mockReturnValue(mockPrivateKey); deployer = new DeployAction(); + vi.spyOn(deployer as any, "getPrivateKey").mockResolvedValue(mockPrivateKey); vi.spyOn(deployer as any, "startSpinner").mockImplementation(() => {}); vi.spyOn(deployer as any, "succeedSpinner").mockImplementation(() => {}); @@ -286,7 +284,7 @@ describe("DeployAction", () => { await deployer["executeJsScript"](filePath); - expect(mockFn).toHaveBeenCalledWith(deployer["genlayerClient"]); + expect(mockFn).toHaveBeenCalledWith(mockClient); expect(deployer["succeedSpinner"]).toHaveBeenCalledWith(`Successfully executed: ${filePath}`); }); diff --git a/tests/libs/accounts/KeypairManager.test.ts b/tests/libs/accounts/KeypairManager.test.ts new file mode 100644 index 00000000..aa8e5597 --- /dev/null +++ b/tests/libs/accounts/KeypairManager.test.ts @@ -0,0 +1,110 @@ +import { describe, test, vi, beforeEach, afterEach, expect } from "vitest"; +import { writeFileSync, existsSync, readFileSync } from "fs"; +import { ethers } from "ethers"; +import { KeypairManager } from "../../../src/lib/accounts/KeypairManager"; + +vi.mock("fs"); +vi.mock("ethers"); + +describe("KeypairManager", () => { + let keypairManager: KeypairManager; + const mockPrivateKey = "0xMockedPrivateKey"; + const mockAddress = "0xMockedAddress"; + const mockKeypairPath = "/mocked/path/keypair.json"; + const mockKeypairData = { + address: mockAddress, + privateKey: mockPrivateKey, + }; + + beforeEach(() => { + vi.clearAllMocks(); + keypairManager = new KeypairManager(); + vi.spyOn(keypairManager as any, "getConfigByKey").mockReturnValue(mockKeypairPath); + vi.spyOn(keypairManager as any, "writeConfig").mockImplementation(() => {}); + vi.spyOn(keypairManager as any, "getFilePath").mockReturnValue(mockKeypairPath); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe("getPrivateKey", () => { + test("should return private key when keypair file exists and contains private key", () => { + vi.mocked(existsSync).mockReturnValue(true); + vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockKeypairData)); + + const result = keypairManager.getPrivateKey(); + + expect(result).toBe(mockPrivateKey); + expect(existsSync).toHaveBeenCalledWith(mockKeypairPath); + expect(readFileSync).toHaveBeenCalledWith(mockKeypairPath, "utf-8"); + }); + + test("should return empty string when keypair file does not exist", () => { + vi.mocked(existsSync).mockReturnValue(false); + + const result = keypairManager.getPrivateKey(); + + expect(result).toBe(""); + expect(existsSync).toHaveBeenCalledWith(mockKeypairPath); + expect(readFileSync).not.toHaveBeenCalled(); + }); + + test("should return empty string when keypair file exists but has no private key", () => { + vi.mocked(existsSync).mockReturnValue(true); + vi.mocked(readFileSync).mockReturnValue(JSON.stringify({ address: mockAddress })); + + const result = keypairManager.getPrivateKey(); + + expect(result).toBe(""); + expect(existsSync).toHaveBeenCalledWith(mockKeypairPath); + expect(readFileSync).toHaveBeenCalledWith(mockKeypairPath, "utf-8"); + }); + }); + + describe("createKeypair", () => { + test("should create new keypair and save it to file", () => { + const mockWallet = { + address: mockAddress, + privateKey: mockPrivateKey, + }; + vi.mocked(ethers.Wallet.createRandom).mockReturnValue(mockWallet as any); + vi.mocked(existsSync).mockReturnValue(false); + + keypairManager.createKeypair(); + + expect(ethers.Wallet.createRandom).toHaveBeenCalled(); + expect(writeFileSync).toHaveBeenCalledWith( + mockKeypairPath, + JSON.stringify(mockKeypairData, null, 2) + ); + expect(keypairManager["writeConfig"]).toHaveBeenCalledWith("keyPairPath", mockKeypairPath); + }); + + test("should throw error when file exists and overwrite is false", () => { + vi.mocked(existsSync).mockReturnValue(true); + + expect(() => keypairManager.createKeypair()).toThrow( + `The file at ${mockKeypairPath} already exists. Use the '--overwrite' option to replace it.` + ); + }); + + test("should overwrite existing file when overwrite is true", () => { + const mockWallet = { + address: mockAddress, + privateKey: mockPrivateKey, + }; + vi.mocked(ethers.Wallet.createRandom).mockReturnValue(mockWallet as any); + vi.mocked(existsSync).mockReturnValue(true); + + keypairManager.createKeypair("./keypair.json", true); + + expect(ethers.Wallet.createRandom).toHaveBeenCalled(); + expect(writeFileSync).toHaveBeenCalledWith( + mockKeypairPath, + JSON.stringify(mockKeypairData, null, 2) + ); + expect(keypairManager["writeConfig"]).toHaveBeenCalledWith("keyPairPath", mockKeypairPath); + }); + }); +}); \ No newline at end of file diff --git a/tests/libs/baseAction.test.ts b/tests/libs/baseAction.test.ts index 118eb61d..be1aeafc 100644 --- a/tests/libs/baseAction.test.ts +++ b/tests/libs/baseAction.test.ts @@ -189,4 +189,44 @@ describe("BaseAction", () => { expect(result).toBe(expected); }); + const mockPrivateKey = "mocked_private_key"; + + beforeEach(() => { + baseAction["keypairManager"] = { + getPrivateKey: vi.fn(), + createKeypair: vi.fn(), + getKeypairPath: vi.fn(), + setKeypairPath: vi.fn(), + } as any; + + }); + + test("should return private key when it exists", async () => { + vi.mocked(baseAction["keypairManager"].getPrivateKey).mockReturnValue(mockPrivateKey); + + const result = await baseAction["getPrivateKey"](); + + expect(result).toBe(mockPrivateKey); + expect(baseAction["keypairManager"].createKeypair).not.toHaveBeenCalled(); + }); + + test("should create new keypair when private key doesn't exist and user confirms", async () => { + vi.mocked(baseAction["keypairManager"].getPrivateKey) + .mockReturnValueOnce(undefined) + .mockReturnValueOnce(mockPrivateKey); + vi.mocked(inquirer.prompt).mockResolvedValue({ confirmAction: true }); + await baseAction["getPrivateKey"](); + + expect(baseAction["keypairManager"].createKeypair).toHaveBeenCalled(); + }); + + test("should exit when private key doesn't exist and user declines", async () => { + vi.mocked(baseAction["keypairManager"].getPrivateKey).mockReturnValueOnce(undefined); + vi.mocked(inquirer.prompt).mockResolvedValue({ confirmAction: false }); + vi.spyOn(process, "exit").mockImplementation(() => { + throw new Error("process exited"); + }); + + await expect(baseAction["getPrivateKey"]()).rejects.toThrow("process exited"); + }); }); diff --git a/tests/libs/getPrivateKey.test.ts b/tests/libs/getPrivateKey.test.ts deleted file mode 100644 index 780ac798..00000000 --- a/tests/libs/getPrivateKey.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { describe, test, vi, beforeEach, afterEach, expect } from "vitest"; -import fs from "fs"; -import { getPrivateKey } from "../../src/lib/accounts/getPrivateKey"; -import { ConfigFileManager } from "../../src/lib/config/ConfigFileManager"; - -vi.mock("fs"); -vi.mock("../../src/lib/config/ConfigFileManager"); - -describe("getPrivateKey", () => { - new ConfigFileManager(); - - beforeEach(() => { - vi.clearAllMocks(); - }); - - afterEach(() => { - vi.restoreAllMocks(); - }); - - test("returns the private key if the file exists and is valid", () => { - const mockPath = "/mocked/path/keypair.json"; - const mockPrivateKey = "0xMockedPrivateKey"; - const mockKeypairData = { privateKey: mockPrivateKey }; - - vi.mocked(ConfigFileManager.prototype.getConfigByKey).mockReturnValue(mockPath); - vi.mocked(fs.existsSync).mockReturnValue(true); - vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockKeypairData)); - - const privateKey = getPrivateKey(); - - expect(ConfigFileManager.prototype.getConfigByKey).toHaveBeenCalledWith("keyPairPath"); - expect(fs.existsSync).toHaveBeenCalledWith(mockPath); - expect(fs.readFileSync).toHaveBeenCalledWith(mockPath, "utf-8"); - expect(privateKey).toBe(mockPrivateKey); - }); - - test("exits if the keypair path is missing in the config", () => { - const consoleErrorSpy = vi.spyOn(console, "error"); - const processExitSpy = vi.spyOn(process, "exit").mockImplementation(() => { - throw new Error("process.exit"); - }); - - vi.mocked(ConfigFileManager.prototype.getConfigByKey).mockReturnValue(null); - - expect(() => getPrivateKey()).toThrowError("process.exit"); - - expect(ConfigFileManager.prototype.getConfigByKey).toHaveBeenCalledWith("keyPairPath"); - expect(consoleErrorSpy).toHaveBeenCalledWith( - "Keypair file not found. Please generate or specify a valid keypair path." - ); - expect(processExitSpy).toHaveBeenCalledWith(1); - }); - - test("exits if the keypair file does not exist", () => { - const consoleErrorSpy = vi.spyOn(console, "error"); - const processExitSpy = vi.spyOn(process, "exit").mockImplementation(() => { - throw new Error("process.exit"); - }); - - const mockPath = "/mocked/path/keypair.json"; - - vi.mocked(ConfigFileManager.prototype.getConfigByKey).mockReturnValue(mockPath); - vi.mocked(fs.existsSync).mockReturnValue(false); - - expect(() => getPrivateKey()).toThrowError("process.exit"); - - expect(ConfigFileManager.prototype.getConfigByKey).toHaveBeenCalledWith("keyPairPath"); - expect(fs.existsSync).toHaveBeenCalledWith(mockPath); - expect(consoleErrorSpy).toHaveBeenCalledWith( - "Keypair file not found. Please generate or specify a valid keypair path." - ); - expect(processExitSpy).toHaveBeenCalledWith(1); - }); - - test("exits if the private key is missing in the keypair file", () => { - const consoleErrorSpy = vi.spyOn(console, "error"); - const processExitSpy = vi.spyOn(process, "exit").mockImplementation(() => { - throw new Error("process.exit"); - }); - - const mockPath = "/mocked/path/keypair.json"; - const mockKeypairData = { notPrivateKey: "SomeOtherData" }; // Invalid keypair data - - vi.mocked(ConfigFileManager.prototype.getConfigByKey).mockReturnValue(mockPath); - vi.mocked(fs.existsSync).mockReturnValue(true); - vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockKeypairData)); - - expect(() => getPrivateKey()).toThrowError("process.exit"); - - expect(ConfigFileManager.prototype.getConfigByKey).toHaveBeenCalledWith("keyPairPath"); - expect(fs.existsSync).toHaveBeenCalledWith(mockPath); - expect(fs.readFileSync).toHaveBeenCalledWith(mockPath, "utf-8"); - expect(consoleErrorSpy).toHaveBeenCalledWith("Invalid keypair file. Private key is missing."); - expect(processExitSpy).toHaveBeenCalledWith(1); - }); -});