genlayer 0.18.1 → 0.18.2-beta.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/CHANGELOG.md +7 -0
- package/dist/index.js +49 -29
- package/package.json +1 -1
- package/src/commands/contracts/call.ts +7 -49
- package/src/commands/contracts/deploy.ts +3 -2
- package/src/commands/contracts/index.ts +36 -11
- package/src/commands/contracts/write.ts +49 -0
- package/src/commands/general/init.ts +15 -20
- package/src/commands/general/start.ts +14 -10
- package/src/commands/keygen/create.ts +2 -2
- package/tests/actions/call.test.ts +22 -86
- package/tests/actions/create.test.ts +8 -8
- package/tests/actions/init.test.ts +173 -102
- package/tests/actions/start.test.ts +48 -24
- package/tests/actions/write.test.ts +102 -0
- package/tests/commands/write.test.ts +76 -0
- package/tests/libs/baseAction.test.ts +37 -26
- package/tests/services/simulator.test.ts +98 -102
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {describe, test, vi, beforeEach, afterEach, expect} from "vitest";
|
|
2
|
+
import {createClient, createAccount} from "genlayer-js";
|
|
3
|
+
import {WriteAction} from "../../src/commands/contracts/write";
|
|
4
|
+
|
|
5
|
+
vi.mock("genlayer-js");
|
|
6
|
+
|
|
7
|
+
describe("WriteAction", () => {
|
|
8
|
+
let writeAction: WriteAction;
|
|
9
|
+
const mockClient = {
|
|
10
|
+
writeContract: vi.fn(),
|
|
11
|
+
waitForTransactionReceipt: vi.fn(),
|
|
12
|
+
initializeConsensusSmartContract: vi.fn(),
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const mockPrivateKey = "mocked_private_key";
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
vi.clearAllMocks();
|
|
19
|
+
vi.mocked(createClient).mockReturnValue(mockClient as any);
|
|
20
|
+
vi.mocked(createAccount).mockReturnValue({privateKey: mockPrivateKey} as any);
|
|
21
|
+
writeAction = new WriteAction();
|
|
22
|
+
vi.spyOn(writeAction as any, "getPrivateKey").mockResolvedValue(mockPrivateKey);
|
|
23
|
+
|
|
24
|
+
vi.spyOn(writeAction as any, "startSpinner").mockImplementation(() => {});
|
|
25
|
+
vi.spyOn(writeAction as any, "succeedSpinner").mockImplementation(() => {});
|
|
26
|
+
vi.spyOn(writeAction as any, "failSpinner").mockImplementation(() => {});
|
|
27
|
+
vi.spyOn(writeAction as any, "log").mockImplementation(() => {});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
vi.restoreAllMocks();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("calls writeContract successfully", async () => {
|
|
35
|
+
const options = {args: [42, "Update"]};
|
|
36
|
+
const mockHash = "0xMockedTransactionHash";
|
|
37
|
+
const mockReceipt = {status: "success"};
|
|
38
|
+
|
|
39
|
+
vi.mocked(mockClient.writeContract).mockResolvedValue(mockHash);
|
|
40
|
+
vi.mocked(mockClient.waitForTransactionReceipt).mockResolvedValue(mockReceipt);
|
|
41
|
+
|
|
42
|
+
await writeAction.write({
|
|
43
|
+
contractAddress: "0xMockedContract",
|
|
44
|
+
method: "updateData",
|
|
45
|
+
...options,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(mockClient.writeContract).toHaveBeenCalledWith({
|
|
49
|
+
address: "0xMockedContract",
|
|
50
|
+
functionName: "updateData",
|
|
51
|
+
args: [42, "Update"],
|
|
52
|
+
value: 0n,
|
|
53
|
+
});
|
|
54
|
+
expect(writeAction["log"]).toHaveBeenCalledWith("Write Transaction Hash:", mockHash);
|
|
55
|
+
expect(writeAction["succeedSpinner"]).toHaveBeenCalledWith(
|
|
56
|
+
"Write operation successfully executed",
|
|
57
|
+
mockReceipt,
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("handles writeContract errors", async () => {
|
|
62
|
+
vi.mocked(mockClient.writeContract).mockRejectedValue(new Error("Mocked write error"));
|
|
63
|
+
|
|
64
|
+
await writeAction.write({contractAddress: "0xMockedContract", method: "updateData", args: [1]});
|
|
65
|
+
|
|
66
|
+
expect(writeAction["failSpinner"]).toHaveBeenCalledWith(
|
|
67
|
+
"Error during write operation",
|
|
68
|
+
expect.any(Error),
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("uses custom RPC URL for write operations", async () => {
|
|
73
|
+
const options = {args: [42, "Update"], rpc: "https://custom-rpc-url.com"};
|
|
74
|
+
const mockHash = "0xMockedTransactionHash";
|
|
75
|
+
const mockReceipt = {status: "success"};
|
|
76
|
+
|
|
77
|
+
vi.mocked(mockClient.writeContract).mockResolvedValue(mockHash);
|
|
78
|
+
vi.mocked(mockClient.waitForTransactionReceipt).mockResolvedValue(mockReceipt);
|
|
79
|
+
|
|
80
|
+
await writeAction.write({
|
|
81
|
+
contractAddress: "0xMockedContract",
|
|
82
|
+
method: "updateData",
|
|
83
|
+
...options,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(createClient).toHaveBeenCalledWith(
|
|
87
|
+
expect.objectContaining({
|
|
88
|
+
endpoint: "https://custom-rpc-url.com",
|
|
89
|
+
}),
|
|
90
|
+
);
|
|
91
|
+
expect(mockClient.writeContract).toHaveBeenCalledWith({
|
|
92
|
+
address: "0xMockedContract",
|
|
93
|
+
functionName: "updateData",
|
|
94
|
+
args: [42, "Update"],
|
|
95
|
+
value: 0n,
|
|
96
|
+
});
|
|
97
|
+
expect(writeAction["succeedSpinner"]).toHaveBeenCalledWith(
|
|
98
|
+
"Write operation successfully executed",
|
|
99
|
+
mockReceipt,
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {Command} from "commander";
|
|
2
|
+
import {WriteAction} from "../../src/commands/contracts/write";
|
|
3
|
+
import {vi, describe, beforeEach, afterEach, test, expect} from "vitest";
|
|
4
|
+
import {initializeContractsCommands} from "../../src/commands/contracts";
|
|
5
|
+
|
|
6
|
+
vi.mock("../../src/commands/contracts/write");
|
|
7
|
+
vi.mock("esbuild", () => ({
|
|
8
|
+
buildSync: vi.fn(),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
describe("write command", () => {
|
|
12
|
+
let program: Command;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
program = new Command();
|
|
16
|
+
initializeContractsCommands(program);
|
|
17
|
+
vi.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
vi.restoreAllMocks();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("WriteAction.write is called with default options", async () => {
|
|
25
|
+
program.parse(["node", "test", "write", "0xMockedContract", "setData"]);
|
|
26
|
+
expect(WriteAction).toHaveBeenCalledTimes(1);
|
|
27
|
+
expect(WriteAction.prototype.write).toHaveBeenCalledWith({
|
|
28
|
+
contractAddress: "0xMockedContract",
|
|
29
|
+
method: "setData",
|
|
30
|
+
args: [],
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("WriteAction.write is called with positional arguments and options", async () => {
|
|
35
|
+
program.parse([
|
|
36
|
+
"node",
|
|
37
|
+
"test",
|
|
38
|
+
"write",
|
|
39
|
+
"0xMockedContract",
|
|
40
|
+
"updateCounter",
|
|
41
|
+
"--args",
|
|
42
|
+
"100",
|
|
43
|
+
"someString",
|
|
44
|
+
"true",
|
|
45
|
+
"--rpc",
|
|
46
|
+
"https://custom-rpc-url-for-write.com",
|
|
47
|
+
]);
|
|
48
|
+
expect(WriteAction).toHaveBeenCalledTimes(1);
|
|
49
|
+
expect(WriteAction.prototype.write).toHaveBeenCalledWith({
|
|
50
|
+
contractAddress: "0xMockedContract",
|
|
51
|
+
method: "updateCounter",
|
|
52
|
+
args: [100, "someString", true],
|
|
53
|
+
rpc: "https://custom-rpc-url-for-write.com",
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("WriteAction is instantiated when the write command is executed", async () => {
|
|
58
|
+
program.parse(["node", "test", "write", "0xMockedContract", "anotherMethod"]);
|
|
59
|
+
expect(WriteAction).toHaveBeenCalledTimes(1);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("throws error for unrecognized options", async () => {
|
|
63
|
+
const writeCommand = program.commands.find(cmd => cmd.name() === "write");
|
|
64
|
+
writeCommand?.exitOverride();
|
|
65
|
+
expect(() =>
|
|
66
|
+
program.parse(["node", "test", "write", "0xMockedContract", "someMethod", "--invalid-option"]),
|
|
67
|
+
).toThrowError("error: unknown option '--invalid-option'");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("WriteAction.write is called without throwing errors for valid options", async () => {
|
|
71
|
+
program.parse(["node", "test", "write", "0xMockedContract", "validMethod"]);
|
|
72
|
+
vi.mocked(WriteAction.prototype.write).mockResolvedValueOnce(undefined);
|
|
73
|
+
// Need to parse again inside expect to ensure the mockResolvedValueOnce is used for the assertion context
|
|
74
|
+
expect(() => program.parse(["node", "test", "write", "0xMockedContract", "validMethod"])).not.toThrow();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {describe, test, vi, beforeEach, afterEach, expect, Mock} from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import {BaseAction} from "../../src/lib/actions/BaseAction";
|
|
3
3
|
import inquirer from "inquirer";
|
|
4
|
-
import ora, {
|
|
4
|
+
import ora, {Ora} from "ora";
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
|
|
7
7
|
vi.mock("inquirer");
|
|
@@ -61,14 +61,14 @@ describe("BaseAction", () => {
|
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
test("should confirm prompt and proceed when confirmed", async () => {
|
|
64
|
-
vi.mocked(inquirer.prompt).mockResolvedValue({
|
|
64
|
+
vi.mocked(inquirer.prompt).mockResolvedValue({confirmAction: true});
|
|
65
65
|
|
|
66
66
|
await expect(baseAction["confirmPrompt"]("Are you sure?")).resolves.not.toThrow();
|
|
67
67
|
expect(inquirer.prompt).toHaveBeenCalled();
|
|
68
68
|
});
|
|
69
69
|
|
|
70
70
|
test("should confirm prompt and exit when declined", async () => {
|
|
71
|
-
vi.mocked(inquirer.prompt).mockResolvedValue({
|
|
71
|
+
vi.mocked(inquirer.prompt).mockResolvedValue({confirmAction: false});
|
|
72
72
|
const processExitSpy = vi.spyOn(process, "exit").mockImplementation(() => {
|
|
73
73
|
throw new Error("process exited");
|
|
74
74
|
});
|
|
@@ -103,7 +103,7 @@ describe("BaseAction", () => {
|
|
|
103
103
|
});
|
|
104
104
|
|
|
105
105
|
test("should log a success message with data", () => {
|
|
106
|
-
const data = {
|
|
106
|
+
const data = {key: "value"};
|
|
107
107
|
|
|
108
108
|
baseAction["logSuccess"]("Success message", data);
|
|
109
109
|
|
|
@@ -117,14 +117,22 @@ describe("BaseAction", () => {
|
|
|
117
117
|
baseAction["logError"]("Error message", error);
|
|
118
118
|
|
|
119
119
|
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("✖ Error message"));
|
|
120
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
121
|
+
chalk.red(
|
|
122
|
+
JSON.stringify(
|
|
123
|
+
{
|
|
124
|
+
name: error.name,
|
|
125
|
+
message: error.message,
|
|
126
|
+
},
|
|
127
|
+
null,
|
|
128
|
+
2,
|
|
129
|
+
),
|
|
130
|
+
),
|
|
131
|
+
);
|
|
124
132
|
});
|
|
125
133
|
|
|
126
134
|
test("should log an info message with data", () => {
|
|
127
|
-
const data = {
|
|
135
|
+
const data = {info: "This is some info"};
|
|
128
136
|
|
|
129
137
|
baseAction["logInfo"]("Info message", data);
|
|
130
138
|
|
|
@@ -133,7 +141,7 @@ describe("BaseAction", () => {
|
|
|
133
141
|
});
|
|
134
142
|
|
|
135
143
|
test("should log a warning message with data", () => {
|
|
136
|
-
const data = {
|
|
144
|
+
const data = {warning: "This is a warning"};
|
|
137
145
|
|
|
138
146
|
baseAction["logWarning"]("Warning message", data);
|
|
139
147
|
|
|
@@ -142,7 +150,7 @@ describe("BaseAction", () => {
|
|
|
142
150
|
});
|
|
143
151
|
|
|
144
152
|
test("should succeed the spinner with a message and log result if data is provided", () => {
|
|
145
|
-
const mockData = {
|
|
153
|
+
const mockData = {key: "value"};
|
|
146
154
|
|
|
147
155
|
baseAction["succeedSpinner"]("Success", mockData);
|
|
148
156
|
|
|
@@ -156,11 +164,11 @@ describe("BaseAction", () => {
|
|
|
156
164
|
(error as any).code = 500;
|
|
157
165
|
|
|
158
166
|
const result = (baseAction as any).formatOutput(error);
|
|
159
|
-
expect(result).toBe(JSON.stringify({
|
|
167
|
+
expect(result).toBe(JSON.stringify({name: "Error", message: "Something went wrong", code: 500}, null, 2));
|
|
160
168
|
});
|
|
161
169
|
|
|
162
170
|
test("should format an object as JSON string", () => {
|
|
163
|
-
const data = {
|
|
171
|
+
const data = {key: "value", num: 42};
|
|
164
172
|
const result = (baseAction as any).formatOutput(data);
|
|
165
173
|
|
|
166
174
|
expect(result).toBe(JSON.stringify(data, null, 2));
|
|
@@ -176,16 +184,20 @@ describe("BaseAction", () => {
|
|
|
176
184
|
const testMap = new Map<string, any>([
|
|
177
185
|
["key1", "value1"],
|
|
178
186
|
["key2", 42],
|
|
179
|
-
["key3", {
|
|
187
|
+
["key3", {nested: "object"}],
|
|
180
188
|
]);
|
|
181
|
-
|
|
189
|
+
|
|
182
190
|
const result = (baseAction as any).formatOutput(testMap);
|
|
183
|
-
const expected = JSON.stringify(
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
191
|
+
const expected = JSON.stringify(
|
|
192
|
+
{
|
|
193
|
+
key1: "value1",
|
|
194
|
+
key2: 42,
|
|
195
|
+
key3: {nested: "object"},
|
|
196
|
+
},
|
|
197
|
+
null,
|
|
198
|
+
2,
|
|
199
|
+
);
|
|
200
|
+
|
|
189
201
|
expect(result).toBe(expected);
|
|
190
202
|
});
|
|
191
203
|
|
|
@@ -198,7 +210,6 @@ describe("BaseAction", () => {
|
|
|
198
210
|
getKeypairPath: vi.fn(),
|
|
199
211
|
setKeypairPath: vi.fn(),
|
|
200
212
|
} as any;
|
|
201
|
-
|
|
202
213
|
});
|
|
203
214
|
|
|
204
215
|
test("should return private key when it exists", async () => {
|
|
@@ -214,15 +225,15 @@ describe("BaseAction", () => {
|
|
|
214
225
|
vi.mocked(baseAction["keypairManager"].getPrivateKey)
|
|
215
226
|
.mockReturnValueOnce(undefined)
|
|
216
227
|
.mockReturnValueOnce(mockPrivateKey);
|
|
217
|
-
|
|
218
|
-
|
|
228
|
+
vi.mocked(inquirer.prompt).mockResolvedValue({confirmAction: true});
|
|
229
|
+
await baseAction["getPrivateKey"]();
|
|
219
230
|
|
|
220
231
|
expect(baseAction["keypairManager"].createKeypair).toHaveBeenCalled();
|
|
221
232
|
});
|
|
222
233
|
|
|
223
234
|
test("should exit when private key doesn't exist and user declines", async () => {
|
|
224
235
|
vi.mocked(baseAction["keypairManager"].getPrivateKey).mockReturnValueOnce(undefined);
|
|
225
|
-
vi.mocked(inquirer.prompt).mockResolvedValue({
|
|
236
|
+
vi.mocked(inquirer.prompt).mockResolvedValue({confirmAction: false});
|
|
226
237
|
vi.spyOn(process, "exit").mockImplementation(() => {
|
|
227
238
|
throw new Error("process exited");
|
|
228
239
|
});
|