@jellylegsai/aether-cli 1.8.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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +110 -0
  3. package/aether-cli-1.0.0.tgz +0 -0
  4. package/aether-cli-1.8.0.tgz +0 -0
  5. package/aether-hub-1.0.5.tgz +0 -0
  6. package/aether-hub-1.1.8.tgz +0 -0
  7. package/aether-hub-1.2.1.tgz +0 -0
  8. package/commands/account.js +280 -0
  9. package/commands/apy.js +499 -0
  10. package/commands/balance.js +241 -0
  11. package/commands/blockhash.js +181 -0
  12. package/commands/broadcast.js +387 -0
  13. package/commands/claim.js +490 -0
  14. package/commands/config.js +851 -0
  15. package/commands/delegations.js +582 -0
  16. package/commands/doctor.js +769 -0
  17. package/commands/emergency.js +667 -0
  18. package/commands/epoch.js +275 -0
  19. package/commands/fees.js +276 -0
  20. package/commands/index.js +78 -0
  21. package/commands/info.js +495 -0
  22. package/commands/init.js +816 -0
  23. package/commands/install.js +666 -0
  24. package/commands/kyc.js +272 -0
  25. package/commands/logs.js +315 -0
  26. package/commands/monitor.js +431 -0
  27. package/commands/multisig.js +701 -0
  28. package/commands/network.js +429 -0
  29. package/commands/nft.js +857 -0
  30. package/commands/ping.js +266 -0
  31. package/commands/price.js +253 -0
  32. package/commands/rewards.js +931 -0
  33. package/commands/sdk-test.js +477 -0
  34. package/commands/sdk.js +656 -0
  35. package/commands/slot.js +155 -0
  36. package/commands/snapshot.js +470 -0
  37. package/commands/stake-info.js +139 -0
  38. package/commands/stake-positions.js +205 -0
  39. package/commands/stake.js +516 -0
  40. package/commands/stats.js +396 -0
  41. package/commands/status.js +327 -0
  42. package/commands/supply.js +391 -0
  43. package/commands/tps.js +238 -0
  44. package/commands/transfer.js +495 -0
  45. package/commands/tx-history.js +346 -0
  46. package/commands/unstake.js +597 -0
  47. package/commands/validator-info.js +657 -0
  48. package/commands/validator-register.js +593 -0
  49. package/commands/validator-start.js +323 -0
  50. package/commands/validator-status.js +227 -0
  51. package/commands/validators.js +626 -0
  52. package/commands/wallet.js +1570 -0
  53. package/index.js +593 -0
  54. package/lib/errors.js +398 -0
  55. package/package.json +76 -0
  56. package/sdk/README.md +210 -0
  57. package/sdk/index.js +1639 -0
  58. package/sdk/package.json +34 -0
  59. package/sdk/rpc.js +254 -0
  60. package/sdk/test.js +85 -0
  61. package/test/doctor.test.js +76 -0
  62. package/validator-identity.json +4 -0
@@ -0,0 +1,477 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * aether-cli sdk-test
4
+ *
5
+ * Comprehensive SDK test suite - exercises all major SDK functions
6
+ * with REAL HTTP RPC calls to verify the SDK works end-to-end.
7
+ *
8
+ * Uses @jellylegsai/aether-sdk for all blockchain interactions.
9
+ * No stubs, no mocks - every function makes actual RPC calls.
10
+ *
11
+ * Usage:
12
+ * aether sdk-test Run full test suite
13
+ * aether sdk-test --rpc <url> Test against specific RPC endpoint
14
+ * aether sdk-test --quick Run only essential tests (slot, balance, health)
15
+ * aether sdk-test --json JSON output for CI/monitoring
16
+ *
17
+ * Default RPC: http://127.0.0.1:8899 (or AETHER_RPC env var)
18
+ */
19
+
20
+ const path = require('path');
21
+ const sdkPath = path.join(__dirname, '..', 'sdk', 'index.js');
22
+ const aether = require(sdkPath);
23
+
24
+ // ANSI colors
25
+ const C = {
26
+ reset: '\x1b[0m',
27
+ bright: '\x1b[1m',
28
+ dim: '\x1b[2m',
29
+ red: '\x1b[31m',
30
+ green: '\x1b[32m',
31
+ yellow: '\x1b[33m',
32
+ cyan: '\x1b[36m',
33
+ magenta: '\x1b[35m',
34
+ };
35
+
36
+ const CLI_VERSION = '1.0.0';
37
+
38
+ // Test results storage
39
+ const testResults = {
40
+ passed: 0,
41
+ failed: 0,
42
+ skipped: 0,
43
+ tests: [],
44
+ };
45
+
46
+ // ---------------------------------------------------------------------------
47
+ // Test Helpers
48
+ // ---------------------------------------------------------------------------
49
+
50
+ function recordTest(name, passed, error = null, data = null) {
51
+ testResults.tests.push({ name, passed, error, data });
52
+ if (passed) {
53
+ testResults.passed++;
54
+ } else if (error === 'SKIPPED') {
55
+ testResults.skipped++;
56
+ } else {
57
+ testResults.failed++;
58
+ }
59
+ }
60
+
61
+ function printTestResult(name, passed, error, data, asJson) {
62
+ if (asJson) return;
63
+
64
+ const icon = passed ? `${C.green}✓${C.reset}` : error === 'SKIPPED' ? `${C.yellow}○${C.reset}` : `${C.red}✗${C.reset}`;
65
+ const status = passed ? `${C.green}PASS${C.reset}` : error === 'SKIPPED' ? `${C.yellow}SKIP${C.reset}` : `${C.red}FAIL${C.reset}`;
66
+
67
+ console.log(` ${icon} ${name.padEnd(35)} ${status}`);
68
+
69
+ if (data && !passed && error !== 'SKIPPED') {
70
+ console.log(` ${C.red}Error: ${error}${C.reset}`);
71
+ } else if (data && passed) {
72
+ console.log(` ${C.dim}${formatData(data)}${C.reset}`);
73
+ } else if (error && error !== 'SKIPPED') {
74
+ console.log(` ${C.red}${error}${C.reset}`);
75
+ }
76
+ }
77
+
78
+ function formatData(data) {
79
+ if (typeof data === 'number') return data.toLocaleString();
80
+ if (typeof data === 'string') return data.length > 60 ? data.substring(0, 60) + '...' : data;
81
+ if (typeof data === 'object') {
82
+ try {
83
+ return JSON.stringify(data).substring(0, 80);
84
+ } catch {
85
+ return '[object]';
86
+ }
87
+ }
88
+ return String(data);
89
+ }
90
+
91
+ function getDefaultRpc() {
92
+ return process.env.AETHER_RPC || aether.DEFAULT_RPC_URL || 'http://127.0.0.1:8899';
93
+ }
94
+
95
+ // ---------------------------------------------------------------------------
96
+ // Test Cases - All use REAL RPC calls via SDK
97
+ // ---------------------------------------------------------------------------
98
+
99
+ async function testGetSlot(client) {
100
+ const name = 'getSlot()';
101
+ try {
102
+ const slot = await client.getSlot();
103
+ const passed = typeof slot === 'number' && slot >= 0;
104
+ recordTest(name, passed, passed ? null : 'Invalid slot number', slot);
105
+ printTestResult(name, passed, passed ? null : 'Invalid slot number', slot, false);
106
+ return passed;
107
+ } catch (err) {
108
+ recordTest(name, false, err.message);
109
+ printTestResult(name, false, err.message, null, false);
110
+ return false;
111
+ }
112
+ }
113
+
114
+ async function testGetBlockHeight(client) {
115
+ const name = 'getBlockHeight()';
116
+ try {
117
+ const height = await client.getBlockHeight();
118
+ const passed = typeof height === 'number' && height >= 0;
119
+ recordTest(name, passed, passed ? null : 'Invalid block height', height);
120
+ printTestResult(name, passed, passed ? null : 'Invalid block height', height, false);
121
+ return passed;
122
+ } catch (err) {
123
+ recordTest(name, false, err.message);
124
+ printTestResult(name, false, err.message, null, false);
125
+ return false;
126
+ }
127
+ }
128
+
129
+ async function testGetHealth(client) {
130
+ const name = 'getHealth()';
131
+ try {
132
+ const health = await client.getHealth();
133
+ const passed = health === 'ok' || health === 'healthy' || typeof health === 'string';
134
+ recordTest(name, passed, passed ? null : 'Unhealthy node', health);
135
+ printTestResult(name, passed, passed ? null : 'Unhealthy node', health, false);
136
+ return passed;
137
+ } catch (err) {
138
+ recordTest(name, false, err.message);
139
+ printTestResult(name, false, err.message, null, false);
140
+ return false;
141
+ }
142
+ }
143
+
144
+ async function testGetVersion(client) {
145
+ const name = 'getVersion()';
146
+ try {
147
+ const version = await client.getVersion();
148
+ const passed = version && (version.aetherCore || version.featureSet || Object.keys(version).length > 0);
149
+ recordTest(name, passed, passed ? null : 'Empty version info', version);
150
+ printTestResult(name, passed, passed ? null : 'Empty version info', version, false);
151
+ return passed;
152
+ } catch (err) {
153
+ recordTest(name, false, err.message);
154
+ printTestResult(name, false, err.message, null, false);
155
+ return false;
156
+ }
157
+ }
158
+
159
+ async function testGetEpochInfo(client) {
160
+ const name = 'getEpochInfo()';
161
+ try {
162
+ const epoch = await client.getEpochInfo();
163
+ const passed = epoch && (epoch.epoch !== undefined || epoch.slot !== undefined);
164
+ recordTest(name, passed, passed ? null : 'Invalid epoch info', epoch);
165
+ printTestResult(name, passed, passed ? null : 'Invalid epoch info', epoch, false);
166
+ return passed;
167
+ } catch (err) {
168
+ recordTest(name, false, err.message);
169
+ printTestResult(name, false, err.message, null, false);
170
+ return false;
171
+ }
172
+ }
173
+
174
+ async function testGetSupply(client) {
175
+ const name = 'getSupply()';
176
+ try {
177
+ const supply = await client.getSupply();
178
+ const passed = supply && (supply.total !== undefined || supply.circulating !== undefined);
179
+ recordTest(name, passed, passed ? null : 'Invalid supply info', supply);
180
+ printTestResult(name, passed, passed ? null : 'Invalid supply info', supply, false);
181
+ return passed;
182
+ } catch (err) {
183
+ recordTest(name, false, err.message);
184
+ printTestResult(name, false, err.message, null, false);
185
+ return false;
186
+ }
187
+ }
188
+
189
+ async function testGetTPS(client) {
190
+ const name = 'getTPS()';
191
+ try {
192
+ const tps = await client.getTPS();
193
+ const passed = tps !== null && tps !== undefined;
194
+ recordTest(name, passed, passed ? null : 'Invalid TPS', tps);
195
+ printTestResult(name, passed, passed ? null : 'Invalid TPS', tps, false);
196
+ return passed;
197
+ } catch (err) {
198
+ recordTest(name, false, err.message);
199
+ printTestResult(name, false, err.message, null, false);
200
+ return false;
201
+ }
202
+ }
203
+
204
+ async function testGetFees(client) {
205
+ const name = 'getFees()';
206
+ try {
207
+ const fees = await client.getFees();
208
+ const passed = fees && Object.keys(fees).length > 0;
209
+ recordTest(name, passed, passed ? null : 'Empty fee info', fees);
210
+ printTestResult(name, passed, passed ? null : 'Empty fee info', fees, false);
211
+ return passed;
212
+ } catch (err) {
213
+ recordTest(name, false, err.message);
214
+ printTestResult(name, false, err.message, null, false);
215
+ return false;
216
+ }
217
+ }
218
+
219
+ async function testGetValidators(client) {
220
+ const name = 'getValidators()';
221
+ try {
222
+ const validators = await client.getValidators();
223
+ const passed = Array.isArray(validators);
224
+ recordTest(name, passed, passed ? null : 'Not an array', validators ? validators.length : null);
225
+ printTestResult(name, passed, passed ? null : 'Not an array', validators ? validators.length : null, false);
226
+ return passed;
227
+ } catch (err) {
228
+ recordTest(name, false, err.message);
229
+ printTestResult(name, false, err.message, null, false);
230
+ return false;
231
+ }
232
+ }
233
+
234
+ async function testGetClusterPeers(client) {
235
+ const name = 'getClusterPeers()';
236
+ try {
237
+ const peers = await client.getClusterPeers();
238
+ const passed = Array.isArray(peers);
239
+ recordTest(name, passed, passed ? null : 'Not an array', peers ? peers.length : null);
240
+ printTestResult(name, passed, passed ? null : 'Not an array', peers ? peers.length : null, false);
241
+ return passed;
242
+ } catch (err) {
243
+ recordTest(name, false, err.message);
244
+ printTestResult(name, false, err.message, null, false);
245
+ return false;
246
+ }
247
+ }
248
+
249
+ async function testGetRecentBlockhash(client) {
250
+ const name = 'getRecentBlockhash()';
251
+ try {
252
+ const blockhash = await client.getRecentBlockhash();
253
+ const passed = blockhash && (blockhash.blockhash || blockhash.value);
254
+ recordTest(name, passed, passed ? null : 'No blockhash returned', blockhash);
255
+ printTestResult(name, passed, passed ? null : 'No blockhash returned', blockhash, false);
256
+ return passed;
257
+ } catch (err) {
258
+ recordTest(name, false, err.message);
259
+ printTestResult(name, false, err.message, null, false);
260
+ return false;
261
+ }
262
+ }
263
+
264
+ async function testGetAccountInfo(client, testAddress) {
265
+ const name = 'getAccountInfo()';
266
+ try {
267
+ // Use system program address as test (always exists)
268
+ const address = testAddress || '11111111111111111111111111111111';
269
+ const account = await client.getAccountInfo(address);
270
+ const passed = account && (account.lamports !== undefined || account.owner !== undefined);
271
+ recordTest(name, passed, passed ? null : 'Invalid account info', account);
272
+ printTestResult(name, passed, passed ? null : 'Invalid account info', account, false);
273
+ return passed;
274
+ } catch (err) {
275
+ recordTest(name, false, err.message);
276
+ printTestResult(name, false, err.message, null, false);
277
+ return false;
278
+ }
279
+ }
280
+
281
+ async function testGetBalance(client, testAddress) {
282
+ const name = 'getBalance()';
283
+ try {
284
+ const address = testAddress || '11111111111111111111111111111111';
285
+ const balance = await client.getBalance(address);
286
+ const passed = typeof balance === 'number' && balance >= 0;
287
+ recordTest(name, passed, passed ? null : 'Invalid balance', balance);
288
+ printTestResult(name, passed, passed ? null : 'Invalid balance', balance, false);
289
+ return passed;
290
+ } catch (err) {
291
+ recordTest(name, false, err.message);
292
+ printTestResult(name, false, err.message, null, false);
293
+ return false;
294
+ }
295
+ }
296
+
297
+ async function testGetSlotProduction(client) {
298
+ const name = 'getSlotProduction()';
299
+ try {
300
+ const stats = await client.getSlotProduction();
301
+ const passed = stats && Object.keys(stats).length > 0;
302
+ recordTest(name, passed, passed ? null : 'Empty slot production stats', stats);
303
+ printTestResult(name, passed, passed ? null : 'Empty slot production stats', stats, false);
304
+ return passed;
305
+ } catch (err) {
306
+ recordTest(name, false, err.message);
307
+ printTestResult(name, false, err.message, null, false);
308
+ return false;
309
+ }
310
+ }
311
+
312
+ async function testConvenienceFunctions(rpcUrl) {
313
+ const name = 'Convenience Functions';
314
+ try {
315
+ // Test multiple convenience functions
316
+ const [slot, health, blockHeight] = await Promise.all([
317
+ aether.getSlot(),
318
+ aether.getHealth(),
319
+ aether.getBlockHeight(),
320
+ ]);
321
+
322
+ const passed = typeof slot === 'number' && health && typeof blockHeight === 'number';
323
+ recordTest(name, passed, passed ? null : 'One or more functions failed', { slot, health, blockHeight });
324
+ printTestResult(name, passed, passed ? null : 'Function failed', { slot, health, blockHeight }, false);
325
+ return passed;
326
+ } catch (err) {
327
+ recordTest(name, false, err.message);
328
+ printTestResult(name, false, err.message, null, false);
329
+ return false;
330
+ }
331
+ }
332
+
333
+ async function testPingUtility(rpcUrl) {
334
+ const name = 'ping()';
335
+ try {
336
+ const result = await aether.ping(rpcUrl);
337
+ const passed = result && result.ok === true && result.latency >= 0;
338
+ recordTest(name, passed, passed ? null : 'Ping failed', result);
339
+ printTestResult(name, passed, passed ? null : 'Ping failed', result, false);
340
+ return passed;
341
+ } catch (err) {
342
+ recordTest(name, false, err.message);
343
+ printTestResult(name, false, err.message, null, false);
344
+ return false;
345
+ }
346
+ }
347
+
348
+ // ---------------------------------------------------------------------------
349
+ // Test Runner
350
+ // ---------------------------------------------------------------------------
351
+
352
+ async function runFullTestSuite(client, rpcUrl) {
353
+ console.log(`\n${C.bright}${C.cyan}═══ Aether SDK Test Suite ═══${C.reset}\n`);
354
+ console.log(` ${C.dim}RPC Endpoint: ${rpcUrl}${C.reset}`);
355
+ console.log(` ${C.dim}Testing @jellylegsai/aether-sdk v1.0.0${C.reset}\n`);
356
+ console.log(` ${C.bright}Running tests...${C.reset}\n`);
357
+
358
+ // Core RPC methods
359
+ console.log(` ${C.cyan}── Core RPC Methods ──${C.reset}`);
360
+ await testGetSlot(client);
361
+ await testGetBlockHeight(client);
362
+ await testGetHealth(client);
363
+ await testGetVersion(client);
364
+ await testGetEpochInfo(client);
365
+
366
+ console.log(`\n ${C.cyan}── Network & Supply ──${C.reset}`);
367
+ await testGetSupply(client);
368
+ await testGetTPS(client);
369
+ await testGetFees(client);
370
+ await testGetValidators(client);
371
+ await testGetClusterPeers(client);
372
+
373
+ console.log(`\n ${C.cyan}── Transaction Support ──${C.reset}`);
374
+ await testGetRecentBlockhash(client);
375
+ await testGetSlotProduction(client);
376
+
377
+ console.log(`\n ${C.cyan}── Account Operations ──${C.reset}`);
378
+ await testGetAccountInfo(client);
379
+ await testGetBalance(client);
380
+
381
+ console.log(`\n ${C.cyan}── Convenience Functions ──${C.reset}`);
382
+ await testConvenienceFunctions(rpcUrl);
383
+ await testPingUtility(rpcUrl);
384
+ }
385
+
386
+ async function runQuickTest(client, rpcUrl) {
387
+ console.log(`\n${C.bright}${C.cyan}═══ Aether SDK Quick Test ═══${C.reset}\n`);
388
+ console.log(` ${C.dim}RPC Endpoint: ${rpcUrl}${C.reset}\n`);
389
+
390
+ console.log(` ${C.cyan}── Essential Tests ──${C.reset}`);
391
+ await testGetSlot(client);
392
+ await testGetHealth(client);
393
+ await testGetBalance(client);
394
+ }
395
+
396
+ function printSummary(asJson) {
397
+ if (asJson) {
398
+ console.log(JSON.stringify({
399
+ total: testResults.passed + testResults.failed + testResults.skipped,
400
+ passed: testResults.passed,
401
+ failed: testResults.failed,
402
+ skipped: testResults.skipped,
403
+ tests: testResults.tests,
404
+ cli_version: CLI_VERSION,
405
+ timestamp: new Date().toISOString(),
406
+ }, null, 2));
407
+ return;
408
+ }
409
+
410
+ console.log(`\n${C.bright}${C.cyan}═══ Test Summary ═══${C.reset}\n`);
411
+
412
+ const total = testResults.passed + testResults.failed + testResults.skipped;
413
+ const passRate = total > 0 ? ((testResults.passed / total) * 100).toFixed(1) : 0;
414
+
415
+ console.log(` ${C.bright}Total:${C.reset} ${total}`);
416
+ console.log(` ${C.green}Passed:${C.reset} ${testResults.passed}`);
417
+ console.log(` ${C.red}Failed:${C.reset} ${testResults.failed}`);
418
+ console.log(` ${C.yellow}Skipped:${C.reset} ${testResults.skipped}`);
419
+ console.log(` ${C.bright}Pass Rate:${C.reset} ${passRate}%\n`);
420
+
421
+ if (testResults.failed > 0) {
422
+ console.log(` ${C.red}── Failed Tests ──${C.reset}`);
423
+ testResults.tests.filter(t => !t.passed && t.error !== 'SKIPPED').forEach(t => {
424
+ console.log(` ${C.red}✗ ${t.name}: ${t.error}${C.reset}`);
425
+ });
426
+ console.log();
427
+ }
428
+
429
+ if (testResults.failed === 0) {
430
+ console.log(` ${C.green}✓ All tests passed!${C.reset}\n`);
431
+ }
432
+ }
433
+
434
+ // ---------------------------------------------------------------------------
435
+ // Main
436
+ // ---------------------------------------------------------------------------
437
+
438
+ async function sdkTestCommand() {
439
+ const args = process.argv.slice(2);
440
+ const asJson = args.includes('--json') || args.includes('-j');
441
+ const isQuick = args.includes('--quick') || args.includes('-q');
442
+
443
+ const rpcIdx = args.findIndex(a => a === '--rpc' || a === '-r');
444
+ const rpcUrl = rpcIdx !== -1 && args[rpcIdx + 1] ? args[rpcIdx + 1] : getDefaultRpc();
445
+
446
+ if (!asJson) {
447
+ console.log(`\n${C.bright}${C.cyan}╔═══════════════════════════════════════════════════════════╗${C.reset}`);
448
+ console.log(`${C.bright}${C.cyan}║ AETHER SDK COMPREHENSIVE TEST SUITE ║${C.reset}`);
449
+ console.log(`${C.bright}${C.cyan}╚═══════════════════════════════════════════════════════════╝${C.reset}\n`);
450
+ }
451
+
452
+ const client = new aether.AetherClient({ rpcUrl });
453
+
454
+ if (isQuick) {
455
+ await runQuickTest(client, rpcUrl);
456
+ } else {
457
+ await runFullTestSuite(client, rpcUrl);
458
+ }
459
+
460
+ printSummary(asJson);
461
+
462
+ // Exit with error if tests failed
463
+ if (testResults.failed > 0) {
464
+ process.exit(1);
465
+ }
466
+ }
467
+
468
+ // Export for module use
469
+ module.exports = { sdkTestCommand };
470
+
471
+ // Run if called directly
472
+ if (require.main === module) {
473
+ sdkTestCommand().catch(err => {
474
+ console.error(`${C.red}✗ Test suite failed: ${err.message}${C.reset}`);
475
+ process.exit(1);
476
+ });
477
+ }