diff --git a/src/commands/transactions/index.ts b/src/commands/transactions/index.ts index 38d2529e..1876c4c2 100644 --- a/src/commands/transactions/index.ts +++ b/src/commands/transactions/index.ts @@ -18,6 +18,8 @@ export function initializeTransactionsCommands(program: Command) { .option("--retries ", "Number of retries", (value) => parseIntOption(value, 100), 100) .option("--interval ", "Interval between retries in milliseconds", (value) => parseIntOption(value, 5000), 5000) .option("--rpc ", "RPC URL for the network") + .option("--stdout", "Print only stdout from the receipt") + .option("--stderr", "Print only stderr from the receipt") .action(async (txId: TransactionHash, options: ReceiptOptions) => { const receiptAction = new ReceiptAction(); diff --git a/src/commands/transactions/receipt.ts b/src/commands/transactions/receipt.ts index 93b6ecce..d9165c4d 100644 --- a/src/commands/transactions/receipt.ts +++ b/src/commands/transactions/receipt.ts @@ -7,6 +7,8 @@ export interface ReceiptParams { retries?: number; interval?: number; rpc?: string; + stdout?: boolean; + stderr?: boolean; } export interface ReceiptOptions extends Omit {} @@ -37,6 +39,8 @@ export class ReceiptAction extends BaseAction { retries, interval, rpc, + stdout, + stderr, }: ReceiptParams): Promise { const client = await this.getClient(rpc); await client.initializeConsensusSmartContract(); @@ -55,7 +59,29 @@ export class ReceiptAction extends BaseAction { retries, interval, }); - + + // If specific output flags are provided, print only those fields + if (stdout || stderr) { + const stdoutValue = (result as any)?.consensus_data?.leader_receipt[0]?.genvm_result?.stdout; + const stderrValue = (result as any)?.consensus_data?.leader_receipt[0]?.genvm_result?.stderr; + + if (stdout && stderr) { + this.succeedSpinner("Transaction stdout and stderr", { stdout: stdoutValue, stderr: stderrValue }); + return; + } + + if (stdout) { + this.succeedSpinner("Transaction stdout retrieved successfully", stdoutValue); + return; + } + + if (stderr) { + this.succeedSpinner("Transaction stderr retrieved successfully", stderrValue); + return; + } + } + + // Default behavior (no flags): show full receipt result this.succeedSpinner("Transaction receipt retrieved successfully", result); } catch (error) { this.failSpinner("Error retrieving transaction receipt", error); diff --git a/tests/actions/receipt.test.ts b/tests/actions/receipt.test.ts index 382562c7..3875cff3 100644 --- a/tests/actions/receipt.test.ts +++ b/tests/actions/receipt.test.ts @@ -170,4 +170,92 @@ describe("ReceiptAction", () => { } }); + test("prints only stdout when --stdout is provided", async () => { + const mockReceipt = { + consensus_data: { + leader_receipt: [ + { + genvm_result: { + stdout: "program stdout", + stderr: "program stderr", + }, + }, + ], + }, + }; + + vi.mocked(mockClient.waitForTransactionReceipt).mockResolvedValue(mockReceipt as any); + + await receiptAction.receipt({ + txId: mockTxId, + retries: defaultRetries, + interval: defaultInterval, + stdout: true, + } as ReceiptParams); + + expect(receiptAction["succeedSpinner"]).toHaveBeenCalledWith( + "Transaction stdout retrieved successfully", + "program stdout", + ); + }); + + test("prints only stderr when --stderr is provided", async () => { + const mockReceipt = { + consensus_data: { + leader_receipt: [ + { + genvm_result: { + stdout: "program stdout", + stderr: "program stderr", + }, + }, + ], + }, + }; + + vi.mocked(mockClient.waitForTransactionReceipt).mockResolvedValue(mockReceipt as any); + + await receiptAction.receipt({ + txId: mockTxId, + retries: defaultRetries, + interval: defaultInterval, + stderr: true, + } as ReceiptParams); + + expect(receiptAction["succeedSpinner"]).toHaveBeenCalledWith( + "Transaction stderr retrieved successfully", + "program stderr", + ); + }); + + test("prints both stdout and stderr when both flags are provided", async () => { + const mockReceipt = { + consensus_data: { + leader_receipt: [ + { + genvm_result: { + stdout: "program stdout", + stderr: "program stderr", + }, + }, + ], + }, + }; + + vi.mocked(mockClient.waitForTransactionReceipt).mockResolvedValue(mockReceipt as any); + + await receiptAction.receipt({ + txId: mockTxId, + retries: defaultRetries, + interval: defaultInterval, + stdout: true, + stderr: true, + } as ReceiptParams); + + expect(receiptAction["succeedSpinner"]).toHaveBeenCalledWith( + "Transaction stdout and stderr", + { stdout: "program stdout", stderr: "program stderr" }, + ); + }); + }); \ No newline at end of file diff --git a/tests/commands/receipt.test.ts b/tests/commands/receipt.test.ts index ea8293cb..496b2a36 100644 --- a/tests/commands/receipt.test.ts +++ b/tests/commands/receipt.test.ts @@ -105,4 +105,38 @@ describe("receipt command", () => { interval: 5000, }); }); + + test("parses --stdout flag", async () => { + program.parse(["node", "test", "receipt", mockTxId, "--stdout"]); + expect(ReceiptAction.prototype.receipt).toHaveBeenCalledWith({ + txId: mockTxId, + status: "FINALIZED", + retries: 100, + interval: 5000, + stdout: true, + }); + }); + + test("parses --stderr flag", async () => { + program.parse(["node", "test", "receipt", mockTxId, "--stderr"]); + expect(ReceiptAction.prototype.receipt).toHaveBeenCalledWith({ + txId: mockTxId, + status: "FINALIZED", + retries: 100, + interval: 5000, + stderr: true, + }); + }); + + test("parses both --stdout and --stderr flags", async () => { + program.parse(["node", "test", "receipt", mockTxId, "--stdout", "--stderr"]); + expect(ReceiptAction.prototype.receipt).toHaveBeenCalledWith({ + txId: mockTxId, + status: "FINALIZED", + retries: 100, + interval: 5000, + stdout: true, + stderr: true, + }); + }); }); \ No newline at end of file