perp-cli 0.3.3
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/LICENSE +21 -0
- package/README.md +293 -0
- package/dist/__tests__/alert-logic.test.d.ts +1 -0
- package/dist/__tests__/alert-logic.test.js +107 -0
- package/dist/__tests__/arb-auto-3dex.test.d.ts +1 -0
- package/dist/__tests__/arb-auto-3dex.test.js +397 -0
- package/dist/__tests__/arb-history-stats.test.d.ts +1 -0
- package/dist/__tests__/arb-history-stats.test.js +176 -0
- package/dist/__tests__/arb-logic.test.d.ts +1 -0
- package/dist/__tests__/arb-logic.test.js +84 -0
- package/dist/__tests__/arb-manage.test.d.ts +1 -0
- package/dist/__tests__/arb-manage.test.js +253 -0
- package/dist/__tests__/arb-new-features.test.d.ts +1 -0
- package/dist/__tests__/arb-new-features.test.js +457 -0
- package/dist/__tests__/arb-sizing.test.d.ts +1 -0
- package/dist/__tests__/arb-sizing.test.js +48 -0
- package/dist/__tests__/arb-state.test.d.ts +1 -0
- package/dist/__tests__/arb-state.test.js +284 -0
- package/dist/__tests__/arb-userflow.test.d.ts +1 -0
- package/dist/__tests__/arb-userflow.test.js +945 -0
- package/dist/__tests__/arb-utils.test.d.ts +1 -0
- package/dist/__tests__/arb-utils.test.js +264 -0
- package/dist/__tests__/bot-conditions.test.d.ts +1 -0
- package/dist/__tests__/bot-conditions.test.js +341 -0
- package/dist/__tests__/client-id-tracker.test.d.ts +1 -0
- package/dist/__tests__/client-id-tracker.test.js +137 -0
- package/dist/__tests__/commands/new-atomic-commands.test.d.ts +1 -0
- package/dist/__tests__/commands/new-atomic-commands.test.js +502 -0
- package/dist/__tests__/commands/order-intent.test.d.ts +1 -0
- package/dist/__tests__/commands/order-intent.test.js +600 -0
- package/dist/__tests__/commands/trade-commands.test.d.ts +1 -0
- package/dist/__tests__/commands/trade-commands.test.js +821 -0
- package/dist/__tests__/config.test.d.ts +1 -0
- package/dist/__tests__/config.test.js +86 -0
- package/dist/__tests__/cross-chain-margin.test.d.ts +1 -0
- package/dist/__tests__/cross-chain-margin.test.js +287 -0
- package/dist/__tests__/dex-asset-map.test.d.ts +1 -0
- package/dist/__tests__/dex-asset-map.test.js +191 -0
- package/dist/__tests__/errors.test.d.ts +1 -0
- package/dist/__tests__/errors.test.js +110 -0
- package/dist/__tests__/event-stream.test.d.ts +1 -0
- package/dist/__tests__/event-stream.test.js +276 -0
- package/dist/__tests__/exchanges/interface.test.d.ts +1 -0
- package/dist/__tests__/exchanges/interface.test.js +132 -0
- package/dist/__tests__/exchanges/mock-adapter.d.ts +69 -0
- package/dist/__tests__/exchanges/mock-adapter.js +137 -0
- package/dist/__tests__/execution-log.test.d.ts +1 -0
- package/dist/__tests__/execution-log.test.js +106 -0
- package/dist/__tests__/funding-calc.test.d.ts +1 -0
- package/dist/__tests__/funding-calc.test.js +71 -0
- package/dist/__tests__/funding-history.test.d.ts +1 -0
- package/dist/__tests__/funding-history.test.js +343 -0
- package/dist/__tests__/funding-rates.test.d.ts +1 -0
- package/dist/__tests__/funding-rates.test.js +342 -0
- package/dist/__tests__/funding.test.d.ts +1 -0
- package/dist/__tests__/funding.test.js +173 -0
- package/dist/__tests__/gap-logic.test.d.ts +1 -0
- package/dist/__tests__/gap-logic.test.js +43 -0
- package/dist/__tests__/hip3-dex.test.d.ts +1 -0
- package/dist/__tests__/hip3-dex.test.js +234 -0
- package/dist/__tests__/integration/agent-features.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/agent-features.integration.test.js +553 -0
- package/dist/__tests__/integration/atomic-commands.integration.test.d.ts +13 -0
- package/dist/__tests__/integration/atomic-commands.integration.test.js +246 -0
- package/dist/__tests__/integration/bridge-simulation.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/bridge-simulation.integration.test.js +453 -0
- package/dist/__tests__/integration/bridge-strict.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/bridge-strict.integration.test.js +812 -0
- package/dist/__tests__/integration/bridge.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/bridge.integration.test.js +309 -0
- package/dist/__tests__/integration/cli-e2e.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/cli-e2e.integration.test.js +202 -0
- package/dist/__tests__/integration/dex-arb.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/dex-arb.integration.test.js +116 -0
- package/dist/__tests__/integration/envelope-consistency.integration.test.d.ts +13 -0
- package/dist/__tests__/integration/envelope-consistency.integration.test.js +205 -0
- package/dist/__tests__/integration/hip3-dex.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/hip3-dex.integration.test.js +147 -0
- package/dist/__tests__/integration/hyperliquid.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/hyperliquid.integration.test.js +79 -0
- package/dist/__tests__/integration/lighter.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/lighter.integration.test.js +53 -0
- package/dist/__tests__/integration/new-commands-e2e.integration.test.d.ts +9 -0
- package/dist/__tests__/integration/new-commands-e2e.integration.test.js +236 -0
- package/dist/__tests__/integration/order-verification.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/order-verification.integration.test.js +321 -0
- package/dist/__tests__/integration/pacifica.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/pacifica.integration.test.js +75 -0
- package/dist/__tests__/integration/response-shapes.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/response-shapes.integration.test.js +278 -0
- package/dist/__tests__/liquidity.test.d.ts +1 -0
- package/dist/__tests__/liquidity.test.js +225 -0
- package/dist/__tests__/plan-executor.test.d.ts +1 -0
- package/dist/__tests__/plan-executor.test.js +314 -0
- package/dist/__tests__/position-history.test.d.ts +1 -0
- package/dist/__tests__/position-history.test.js +367 -0
- package/dist/__tests__/retry.test.d.ts +1 -0
- package/dist/__tests__/retry.test.js +310 -0
- package/dist/__tests__/risk-assessment.test.d.ts +1 -0
- package/dist/__tests__/risk-assessment.test.js +145 -0
- package/dist/__tests__/security-adversarial.test.d.ts +1 -0
- package/dist/__tests__/security-adversarial.test.js +574 -0
- package/dist/__tests__/strategies.test.d.ts +1 -0
- package/dist/__tests__/strategies.test.js +539 -0
- package/dist/__tests__/trade-execution.test.d.ts +1 -0
- package/dist/__tests__/trade-execution.test.js +129 -0
- package/dist/__tests__/trade-validator.test.d.ts +1 -0
- package/dist/__tests__/trade-validator.test.js +655 -0
- package/dist/__tests__/utils.test.d.ts +1 -0
- package/dist/__tests__/utils.test.js +76 -0
- package/dist/api/public/hyperliquid.d.ts +18 -0
- package/dist/api/public/hyperliquid.js +82 -0
- package/dist/api/public/index.d.ts +8 -0
- package/dist/api/public/index.js +8 -0
- package/dist/api/public/lighter.d.ts +24 -0
- package/dist/api/public/lighter.js +100 -0
- package/dist/api/public/pacifica.d.ts +17 -0
- package/dist/api/public/pacifica.js +54 -0
- package/dist/api/public/urls.d.ts +12 -0
- package/dist/api/public/urls.js +33 -0
- package/dist/arb/history-stats.d.ts +44 -0
- package/dist/arb/history-stats.js +135 -0
- package/dist/arb/index.d.ts +4 -0
- package/dist/arb/index.js +4 -0
- package/dist/arb/sizing.d.ts +23 -0
- package/dist/arb/sizing.js +96 -0
- package/dist/arb/state.d.ts +51 -0
- package/dist/arb/state.js +112 -0
- package/dist/arb/utils.d.ts +81 -0
- package/dist/arb/utils.js +267 -0
- package/dist/arb-history-stats.d.ts +5 -0
- package/dist/arb-history-stats.js +5 -0
- package/dist/arb-sizing.d.ts +5 -0
- package/dist/arb-sizing.js +5 -0
- package/dist/arb-state.d.ts +5 -0
- package/dist/arb-state.js +5 -0
- package/dist/arb-utils.d.ts +5 -0
- package/dist/arb-utils.js +5 -0
- package/dist/bot/conditions.d.ts +32 -0
- package/dist/bot/conditions.js +141 -0
- package/dist/bot/config.d.ts +76 -0
- package/dist/bot/config.js +160 -0
- package/dist/bot/engine.d.ts +8 -0
- package/dist/bot/engine.js +519 -0
- package/dist/bot/presets.d.ts +11 -0
- package/dist/bot/presets.js +296 -0
- package/dist/bridge-engine.d.ts +133 -0
- package/dist/bridge-engine.js +1487 -0
- package/dist/cache.d.ts +25 -0
- package/dist/cache.js +99 -0
- package/dist/cli-spec.d.ts +50 -0
- package/dist/cli-spec.js +75 -0
- package/dist/client-id-tracker.d.ts +25 -0
- package/dist/client-id-tracker.js +76 -0
- package/dist/commands/account.d.ts +3 -0
- package/dist/commands/account.js +425 -0
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.js +386 -0
- package/dist/commands/alert.d.ts +2 -0
- package/dist/commands/alert.js +421 -0
- package/dist/commands/analytics.d.ts +3 -0
- package/dist/commands/analytics.js +311 -0
- package/dist/commands/arb/index.d.ts +3 -0
- package/dist/commands/arb/index.js +921 -0
- package/dist/commands/arb-auto.d.ts +54 -0
- package/dist/commands/arb-auto.js +1328 -0
- package/dist/commands/arb-manage.d.ts +5 -0
- package/dist/commands/arb-manage.js +5 -0
- package/dist/commands/arb.d.ts +2 -0
- package/dist/commands/arb.js +347 -0
- package/dist/commands/backtest.d.ts +2 -0
- package/dist/commands/backtest.js +327 -0
- package/dist/commands/bot.d.ts +3 -0
- package/dist/commands/bot.js +412 -0
- package/dist/commands/bridge.d.ts +2 -0
- package/dist/commands/bridge.js +396 -0
- package/dist/commands/dashboard.d.ts +3 -0
- package/dist/commands/dashboard.js +176 -0
- package/dist/commands/deposit.d.ts +4 -0
- package/dist/commands/deposit.js +573 -0
- package/dist/commands/dex.d.ts +3 -0
- package/dist/commands/dex.js +114 -0
- package/dist/commands/env.d.ts +2 -0
- package/dist/commands/env.js +136 -0
- package/dist/commands/funding.d.ts +2 -0
- package/dist/commands/funding.js +347 -0
- package/dist/commands/gap.d.ts +2 -0
- package/dist/commands/gap.js +305 -0
- package/dist/commands/health.d.ts +2 -0
- package/dist/commands/health.js +67 -0
- package/dist/commands/history.d.ts +2 -0
- package/dist/commands/history.js +235 -0
- package/dist/commands/init.d.ts +15 -0
- package/dist/commands/init.js +266 -0
- package/dist/commands/jobs.d.ts +2 -0
- package/dist/commands/jobs.js +133 -0
- package/dist/commands/manage.d.ts +4 -0
- package/dist/commands/manage.js +309 -0
- package/dist/commands/market.d.ts +3 -0
- package/dist/commands/market.js +225 -0
- package/dist/commands/plan.d.ts +3 -0
- package/dist/commands/plan.js +95 -0
- package/dist/commands/portfolio.d.ts +3 -0
- package/dist/commands/portfolio.js +169 -0
- package/dist/commands/rebalance.d.ts +3 -0
- package/dist/commands/rebalance.js +293 -0
- package/dist/commands/risk.d.ts +3 -0
- package/dist/commands/risk.js +169 -0
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.js +202 -0
- package/dist/commands/settings.d.ts +2 -0
- package/dist/commands/settings.js +102 -0
- package/dist/commands/stream.d.ts +5 -0
- package/dist/commands/stream.js +123 -0
- package/dist/commands/trade.d.ts +3 -0
- package/dist/commands/trade.js +1273 -0
- package/dist/commands/wallet.d.ts +14 -0
- package/dist/commands/wallet.js +602 -0
- package/dist/commands/withdraw.d.ts +3 -0
- package/dist/commands/withdraw.js +187 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +68 -0
- package/dist/cross-chain-margin.d.ts +46 -0
- package/dist/cross-chain-margin.js +107 -0
- package/dist/dashboard/server.d.ts +80 -0
- package/dist/dashboard/server.js +340 -0
- package/dist/dashboard/ui.d.ts +4 -0
- package/dist/dashboard/ui.js +538 -0
- package/dist/dashboard/ws-feeds.d.ts +29 -0
- package/dist/dashboard/ws-feeds.js +660 -0
- package/dist/dex-asset-map.d.ts +80 -0
- package/dist/dex-asset-map.js +201 -0
- package/dist/errors.d.ts +109 -0
- package/dist/errors.js +84 -0
- package/dist/event-stream.d.ts +25 -0
- package/dist/event-stream.js +168 -0
- package/dist/exchanges/hyperliquid.d.ts +212 -0
- package/dist/exchanges/hyperliquid.js +931 -0
- package/dist/exchanges/interface.d.ts +95 -0
- package/dist/exchanges/interface.js +5 -0
- package/dist/exchanges/lighter.d.ts +159 -0
- package/dist/exchanges/lighter.js +793 -0
- package/dist/exchanges/pacifica.d.ts +51 -0
- package/dist/exchanges/pacifica.js +248 -0
- package/dist/execution-log.d.ts +36 -0
- package/dist/execution-log.js +102 -0
- package/dist/funding/history.d.ts +63 -0
- package/dist/funding/history.js +266 -0
- package/dist/funding/index.d.ts +3 -0
- package/dist/funding/index.js +3 -0
- package/dist/funding/normalize.d.ts +39 -0
- package/dist/funding/normalize.js +66 -0
- package/dist/funding/rates.d.ts +45 -0
- package/dist/funding/rates.js +172 -0
- package/dist/funding-history.d.ts +5 -0
- package/dist/funding-history.js +5 -0
- package/dist/funding-rates.d.ts +5 -0
- package/dist/funding-rates.js +5 -0
- package/dist/funding.d.ts +5 -0
- package/dist/funding.js +5 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +458 -0
- package/dist/jobs.d.ts +37 -0
- package/dist/jobs.js +152 -0
- package/dist/liquidity.d.ts +34 -0
- package/dist/liquidity.js +100 -0
- package/dist/mcp-server.d.ts +9 -0
- package/dist/mcp-server.js +1206 -0
- package/dist/pacifica/client.d.ts +111 -0
- package/dist/pacifica/client.js +310 -0
- package/dist/pacifica/constants.d.ts +27 -0
- package/dist/pacifica/constants.js +47 -0
- package/dist/pacifica/deposit.d.ts +14 -0
- package/dist/pacifica/deposit.js +78 -0
- package/dist/pacifica/index.d.ts +6 -0
- package/dist/pacifica/index.js +11 -0
- package/dist/pacifica/signing.d.ts +49 -0
- package/dist/pacifica/signing.js +97 -0
- package/dist/pacifica/types/account.d.ts +42 -0
- package/dist/pacifica/types/account.js +1 -0
- package/dist/pacifica/types/index.d.ts +6 -0
- package/dist/pacifica/types/index.js +6 -0
- package/dist/pacifica/types/lake.d.ts +18 -0
- package/dist/pacifica/types/lake.js +1 -0
- package/dist/pacifica/types/market.d.ts +64 -0
- package/dist/pacifica/types/market.js +1 -0
- package/dist/pacifica/types/order.d.ts +92 -0
- package/dist/pacifica/types/order.js +1 -0
- package/dist/pacifica/types/position.d.ts +25 -0
- package/dist/pacifica/types/position.js +1 -0
- package/dist/pacifica/types/ws.d.ts +34 -0
- package/dist/pacifica/types/ws.js +41 -0
- package/dist/pacifica/ws-client.d.ts +42 -0
- package/dist/pacifica/ws-client.js +180 -0
- package/dist/plan-executor.d.ts +48 -0
- package/dist/plan-executor.js +280 -0
- package/dist/position-history.d.ts +68 -0
- package/dist/position-history.js +222 -0
- package/dist/rebalance.d.ts +64 -0
- package/dist/rebalance.js +142 -0
- package/dist/retry.d.ts +74 -0
- package/dist/retry.js +129 -0
- package/dist/risk.d.ts +48 -0
- package/dist/risk.js +156 -0
- package/dist/settings.d.ts +19 -0
- package/dist/settings.js +45 -0
- package/dist/shared-api.d.ts +5 -0
- package/dist/shared-api.js +5 -0
- package/dist/strategies/dca.d.ts +25 -0
- package/dist/strategies/dca.js +114 -0
- package/dist/strategies/funding-arb.d.ts +15 -0
- package/dist/strategies/funding-arb.js +281 -0
- package/dist/strategies/grid.d.ts +34 -0
- package/dist/strategies/grid.js +185 -0
- package/dist/strategies/trailing-stop.d.ts +17 -0
- package/dist/strategies/trailing-stop.js +121 -0
- package/dist/strategies/twap.d.ts +20 -0
- package/dist/strategies/twap.js +78 -0
- package/dist/trade-validator.d.ts +39 -0
- package/dist/trade-validator.js +154 -0
- package/dist/utils.d.ts +38 -0
- package/dist/utils.js +110 -0
- package/package.json +63 -0
- package/skills/perp-cli/SKILL.md +149 -0
- package/skills/perp-cli/references/commands.md +143 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests verifying JSON envelope consistency across all CLI commands.
|
|
3
|
+
*
|
|
4
|
+
* Every --json output must:
|
|
5
|
+
* 1. Be valid JSON (single object, no extra text)
|
|
6
|
+
* 2. Have ok: boolean
|
|
7
|
+
* 3. If ok=true: have data and meta.timestamp
|
|
8
|
+
* 4. If ok=false: have error.code, error.message, and meta.timestamp
|
|
9
|
+
*
|
|
10
|
+
* These tests spawn the real CLI process to catch any console.log leaks,
|
|
11
|
+
* chalk output in JSON mode, or missing envelope wrappers.
|
|
12
|
+
*/
|
|
13
|
+
import "dotenv/config";
|
|
14
|
+
import { execSync } from "child_process";
|
|
15
|
+
import { describe, it, expect } from "vitest";
|
|
16
|
+
const CLI_CWD = "/Users/hik/Documents/GitHub/pacifica/packages/cli";
|
|
17
|
+
const CLI_CMD = "npx tsx src/index.ts";
|
|
18
|
+
function runCliSafe(args) {
|
|
19
|
+
try {
|
|
20
|
+
const stdout = execSync(`${CLI_CMD} ${args}`, {
|
|
21
|
+
encoding: "utf-8",
|
|
22
|
+
cwd: CLI_CWD,
|
|
23
|
+
timeout: 25000,
|
|
24
|
+
env: { ...process.env, NODE_NO_WARNINGS: "1" },
|
|
25
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
26
|
+
});
|
|
27
|
+
return { stdout, stderr: "", exitCode: 0 };
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
const e = err;
|
|
31
|
+
return {
|
|
32
|
+
stdout: e.stdout ?? "",
|
|
33
|
+
stderr: e.stderr ?? "",
|
|
34
|
+
exitCode: e.status ?? 1,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function validateEnvelope(raw, label) {
|
|
39
|
+
// 1. Must be valid JSON
|
|
40
|
+
let parsed;
|
|
41
|
+
try {
|
|
42
|
+
parsed = JSON.parse(raw);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
throw new Error(`[${label}] stdout is not valid JSON:\n${raw.slice(0, 500)}`);
|
|
46
|
+
}
|
|
47
|
+
// 2. Must have ok: boolean
|
|
48
|
+
expect(typeof parsed.ok).toBe("boolean");
|
|
49
|
+
// 3. Must have meta.timestamp
|
|
50
|
+
expect(parsed.meta).toBeDefined();
|
|
51
|
+
expect(typeof parsed.meta.timestamp).toBe("string");
|
|
52
|
+
expect(parsed.meta.timestamp.length).toBeGreaterThan(0);
|
|
53
|
+
// Verify ISO 8601
|
|
54
|
+
const ts = new Date(parsed.meta.timestamp);
|
|
55
|
+
expect(ts.getTime()).toBeGreaterThan(0);
|
|
56
|
+
if (parsed.ok) {
|
|
57
|
+
// 4a. Success: must have data
|
|
58
|
+
expect(parsed.data).toBeDefined();
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// 4b. Error: must have error.code and error.message
|
|
62
|
+
expect(parsed.error).toBeDefined();
|
|
63
|
+
expect(typeof parsed.error.code).toBe("string");
|
|
64
|
+
expect(parsed.error.code.length).toBeGreaterThan(0);
|
|
65
|
+
expect(typeof parsed.error.message).toBe("string");
|
|
66
|
+
}
|
|
67
|
+
return parsed;
|
|
68
|
+
}
|
|
69
|
+
describe("JSON Envelope Consistency", { timeout: 30000 }, () => {
|
|
70
|
+
// ── Commands that require no adapter (always work) ──
|
|
71
|
+
describe("no-adapter commands", () => {
|
|
72
|
+
it("api-spec: valid success envelope", () => {
|
|
73
|
+
const { stdout } = runCliSafe("--json api-spec");
|
|
74
|
+
const env = validateEnvelope(stdout, "api-spec");
|
|
75
|
+
expect(env.ok).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
it("plan example: valid JSON output", () => {
|
|
78
|
+
const { stdout } = runCliSafe("--json plan example");
|
|
79
|
+
const parsed = JSON.parse(stdout);
|
|
80
|
+
// plan example wraps in envelope in --json mode
|
|
81
|
+
if (parsed.ok !== undefined) {
|
|
82
|
+
// Envelope mode
|
|
83
|
+
expect(parsed.ok).toBe(true);
|
|
84
|
+
expect(parsed.data).toBeDefined();
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// Raw plan JSON (legacy)
|
|
88
|
+
expect(parsed.steps || parsed.version).toBeDefined();
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
// ── Error paths ──
|
|
93
|
+
describe("error envelopes", () => {
|
|
94
|
+
it("unknown command: CLI_ERROR envelope", () => {
|
|
95
|
+
const { stdout } = runCliSafe("--json nonexistentcommand999");
|
|
96
|
+
const env = validateEnvelope(stdout, "unknown command");
|
|
97
|
+
expect(env.ok).toBe(false);
|
|
98
|
+
expect(env.error.code).toBe("CLI_ERROR");
|
|
99
|
+
});
|
|
100
|
+
it("plan validate with bad file: error envelope", () => {
|
|
101
|
+
const { stdout } = runCliSafe("--json plan validate /tmp/__no_file_here_99.json");
|
|
102
|
+
const env = validateEnvelope(stdout, "plan validate bad file");
|
|
103
|
+
expect(env.ok).toBe(false);
|
|
104
|
+
expect(env.error.message).toBeTruthy();
|
|
105
|
+
});
|
|
106
|
+
it("stdout has no extra text before/after JSON", () => {
|
|
107
|
+
const { stdout } = runCliSafe("--json api-spec");
|
|
108
|
+
// Trim whitespace, should start with { and end with }
|
|
109
|
+
const trimmed = stdout.trim();
|
|
110
|
+
expect(trimmed.startsWith("{")).toBe(true);
|
|
111
|
+
expect(trimmed.endsWith("}")).toBe(true);
|
|
112
|
+
// No extra lines
|
|
113
|
+
const lines = trimmed.split("\n").filter((l) => l.trim().length > 0);
|
|
114
|
+
// All lines should be part of the JSON (indented)
|
|
115
|
+
const reparsed = JSON.parse(trimmed);
|
|
116
|
+
expect(reparsed.ok).toBeDefined();
|
|
117
|
+
});
|
|
118
|
+
it("stderr is empty in --json mode for error paths", () => {
|
|
119
|
+
const { stderr } = runCliSafe("--json nonexistentcommand999");
|
|
120
|
+
// In JSON mode, errors go to stdout as JSON, not stderr
|
|
121
|
+
// stderr should be empty or only contain warnings
|
|
122
|
+
// (Commander may still write to stderr in some cases)
|
|
123
|
+
// We mainly verify that stdout has the JSON envelope
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
// ── Commands that need HL adapter (read-only) ──
|
|
127
|
+
const HAS_KEY = !!(process.env.HYPERLIQUID_PRIVATE_KEY || process.env.HL_PRIVATE_KEY);
|
|
128
|
+
describe.skipIf(!HAS_KEY)("HL adapter commands — envelope validation", () => {
|
|
129
|
+
it("market list: success envelope with array data", () => {
|
|
130
|
+
const { stdout } = runCliSafe("--json -e hyperliquid market list");
|
|
131
|
+
const env = validateEnvelope(stdout, "market list");
|
|
132
|
+
expect(env.ok).toBe(true);
|
|
133
|
+
expect(Array.isArray(env.data)).toBe(true);
|
|
134
|
+
expect(env.data.length).toBeGreaterThan(0);
|
|
135
|
+
});
|
|
136
|
+
it("market mid BTC: success envelope with mid price", () => {
|
|
137
|
+
const { stdout } = runCliSafe("--json -e hyperliquid market mid BTC");
|
|
138
|
+
const env = validateEnvelope(stdout, "market mid BTC");
|
|
139
|
+
expect(env.ok).toBe(true);
|
|
140
|
+
const data = env.data;
|
|
141
|
+
expect(data.symbol).toBe("BTC");
|
|
142
|
+
expect(data.mid).toBeDefined();
|
|
143
|
+
});
|
|
144
|
+
it("market info BTC: success envelope with market info", () => {
|
|
145
|
+
const { stdout } = runCliSafe("--json -e hyperliquid market info BTC");
|
|
146
|
+
const env = validateEnvelope(stdout, "market info BTC");
|
|
147
|
+
expect(env.ok).toBe(true);
|
|
148
|
+
});
|
|
149
|
+
it("market book BTC: success envelope with orderbook", () => {
|
|
150
|
+
const { stdout } = runCliSafe("--json -e hyperliquid market book BTC");
|
|
151
|
+
const env = validateEnvelope(stdout, "market book BTC");
|
|
152
|
+
expect(env.ok).toBe(true);
|
|
153
|
+
const data = env.data;
|
|
154
|
+
expect(data.bids).toBeDefined();
|
|
155
|
+
expect(data.asks).toBeDefined();
|
|
156
|
+
});
|
|
157
|
+
it("account info: success envelope with balance", () => {
|
|
158
|
+
const { stdout } = runCliSafe("--json -e hyperliquid account info");
|
|
159
|
+
const env = validateEnvelope(stdout, "account info");
|
|
160
|
+
expect(env.ok).toBe(true);
|
|
161
|
+
const data = env.data;
|
|
162
|
+
expect(data.equity).toBeDefined();
|
|
163
|
+
expect(data.available).toBeDefined();
|
|
164
|
+
});
|
|
165
|
+
it("account positions: success envelope with array", () => {
|
|
166
|
+
const { stdout } = runCliSafe("--json -e hyperliquid account positions");
|
|
167
|
+
const env = validateEnvelope(stdout, "account positions");
|
|
168
|
+
expect(env.ok).toBe(true);
|
|
169
|
+
expect(Array.isArray(env.data)).toBe(true);
|
|
170
|
+
});
|
|
171
|
+
it("account orders: success envelope with array", () => {
|
|
172
|
+
const { stdout } = runCliSafe("--json -e hyperliquid account orders");
|
|
173
|
+
const env = validateEnvelope(stdout, "account orders");
|
|
174
|
+
expect(env.ok).toBe(true);
|
|
175
|
+
expect(Array.isArray(env.data)).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
it("status: success envelope with exchange, balance, positions, orders", () => {
|
|
178
|
+
const { stdout } = runCliSafe("--json -e hyperliquid status");
|
|
179
|
+
const env = validateEnvelope(stdout, "status");
|
|
180
|
+
expect(env.ok).toBe(true);
|
|
181
|
+
const data = env.data;
|
|
182
|
+
expect(data.exchange).toBe("hyperliquid");
|
|
183
|
+
expect(data.balance).toBeDefined();
|
|
184
|
+
expect(data.positions).toBeDefined();
|
|
185
|
+
expect(data.orders).toBeDefined();
|
|
186
|
+
});
|
|
187
|
+
it("account margin XYZFAKE: POSITION_NOT_FOUND error envelope", () => {
|
|
188
|
+
const { stdout } = runCliSafe("--json -e hyperliquid account margin XYZFAKE");
|
|
189
|
+
const env = validateEnvelope(stdout, "account margin XYZFAKE");
|
|
190
|
+
expect(env.ok).toBe(false);
|
|
191
|
+
expect(env.error.code).toBe("POSITION_NOT_FOUND");
|
|
192
|
+
});
|
|
193
|
+
it("trade fills: success envelope with array", () => {
|
|
194
|
+
const { stdout } = runCliSafe("--json -e hyperliquid trade fills");
|
|
195
|
+
const env = validateEnvelope(stdout, "trade fills");
|
|
196
|
+
expect(env.ok).toBe(true);
|
|
197
|
+
expect(Array.isArray(env.data)).toBe(true);
|
|
198
|
+
});
|
|
199
|
+
it("health: success envelope with healthy flag", () => {
|
|
200
|
+
const { stdout } = runCliSafe("--json -e hyperliquid health");
|
|
201
|
+
const env = validateEnvelope(stdout, "health");
|
|
202
|
+
expect(env.ok).toBe(true);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
/**
|
|
3
|
+
* Integration tests for HIP-3 deployed perp dex support.
|
|
4
|
+
*
|
|
5
|
+
* These tests hit the real Hyperliquid mainnet API (read-only)
|
|
6
|
+
* to verify our parsing of allPerpMetas and dex-specific queries.
|
|
7
|
+
*
|
|
8
|
+
* No private key needed — all calls are public info endpoints.
|
|
9
|
+
*
|
|
10
|
+
* Run: pnpm --filter perp-cli test -- --testPathPattern hip3-dex.integration
|
|
11
|
+
*/
|
|
12
|
+
const HL_INFO_URL = "https://api.hyperliquid.xyz/info";
|
|
13
|
+
async function infoPost(body) {
|
|
14
|
+
const res = await fetch(HL_INFO_URL, {
|
|
15
|
+
method: "POST",
|
|
16
|
+
headers: { "Content-Type": "application/json" },
|
|
17
|
+
body: JSON.stringify(body),
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok)
|
|
20
|
+
throw new Error(`HTTP ${res.status}`);
|
|
21
|
+
return res.json();
|
|
22
|
+
}
|
|
23
|
+
describe("HIP-3 Integration — allPerpMetas", () => {
|
|
24
|
+
it("returns an array with native perps as first entry", async () => {
|
|
25
|
+
const data = await infoPost({ type: "allPerpMetas" });
|
|
26
|
+
expect(Array.isArray(data)).toBe(true);
|
|
27
|
+
expect(data.length).toBeGreaterThanOrEqual(1);
|
|
28
|
+
// First entry: native perps (BTC, ETH, etc.)
|
|
29
|
+
const native = data[0];
|
|
30
|
+
expect(native).toHaveProperty("universe");
|
|
31
|
+
expect(Array.isArray(native.universe)).toBe(true);
|
|
32
|
+
expect(native.universe.length).toBeGreaterThan(100); // 200+ native perps
|
|
33
|
+
const btc = native.universe.find((a) => a.name === "BTC");
|
|
34
|
+
expect(btc).toBeTruthy();
|
|
35
|
+
expect(btc.maxLeverage).toBeGreaterThanOrEqual(40);
|
|
36
|
+
}, 15000);
|
|
37
|
+
it("has deployed dexes after index 0", async () => {
|
|
38
|
+
const data = await infoPost({ type: "allPerpMetas" });
|
|
39
|
+
expect(data.length).toBeGreaterThan(1); // at least 1 deployed dex
|
|
40
|
+
// Each deployed dex entry has universe with prefixed assets
|
|
41
|
+
for (let i = 1; i < data.length; i++) {
|
|
42
|
+
const entry = data[i];
|
|
43
|
+
expect(entry).toHaveProperty("universe");
|
|
44
|
+
expect(entry).toHaveProperty("collateralToken");
|
|
45
|
+
if (entry.universe.length === 0)
|
|
46
|
+
continue; // skip empty
|
|
47
|
+
// Assets should have a prefix with colon
|
|
48
|
+
const firstName = entry.universe[0].name;
|
|
49
|
+
expect(firstName).toContain(":");
|
|
50
|
+
}
|
|
51
|
+
}, 15000);
|
|
52
|
+
it("extracts known dex names (xyz, vntl, etc.)", async () => {
|
|
53
|
+
const data = await infoPost({ type: "allPerpMetas" });
|
|
54
|
+
const dexNames = [];
|
|
55
|
+
for (let i = 1; i < data.length; i++) {
|
|
56
|
+
const universe = data[i]?.universe ?? [];
|
|
57
|
+
if (universe.length === 0)
|
|
58
|
+
continue;
|
|
59
|
+
const first = universe[0].name;
|
|
60
|
+
const colon = first.indexOf(":");
|
|
61
|
+
if (colon > 0)
|
|
62
|
+
dexNames.push(first.slice(0, colon));
|
|
63
|
+
}
|
|
64
|
+
expect(dexNames.length).toBeGreaterThan(0);
|
|
65
|
+
// xyz is one of the most established deployed dexes
|
|
66
|
+
expect(dexNames).toContain("xyz");
|
|
67
|
+
console.log("Discovered deployed dexes:", dexNames.join(", "));
|
|
68
|
+
}, 15000);
|
|
69
|
+
});
|
|
70
|
+
describe("HIP-3 Integration — dex-specific metaAndAssetCtxs", () => {
|
|
71
|
+
it("fetches xyz dex markets with mark prices", async () => {
|
|
72
|
+
const data = await infoPost({ type: "metaAndAssetCtxs", dex: "xyz" });
|
|
73
|
+
expect(Array.isArray(data)).toBe(true);
|
|
74
|
+
expect(data.length).toBe(2); // [meta, ctxs]
|
|
75
|
+
const universe = data[0]?.universe ?? [];
|
|
76
|
+
const ctxs = data[1] ?? [];
|
|
77
|
+
expect(universe.length).toBeGreaterThan(0);
|
|
78
|
+
expect(ctxs.length).toBe(universe.length);
|
|
79
|
+
// All assets should have xyz: prefix
|
|
80
|
+
for (const asset of universe) {
|
|
81
|
+
expect(asset.name.startsWith("xyz:")).toBe(true);
|
|
82
|
+
}
|
|
83
|
+
// Check TSLA exists and has valid price
|
|
84
|
+
const tslaIdx = universe.findIndex((a) => a.name === "xyz:TSLA");
|
|
85
|
+
expect(tslaIdx).toBeGreaterThanOrEqual(0);
|
|
86
|
+
const tslaCtx = ctxs[tslaIdx];
|
|
87
|
+
expect(Number(tslaCtx.markPx)).toBeGreaterThan(0);
|
|
88
|
+
expect(tslaCtx).toHaveProperty("funding");
|
|
89
|
+
expect(tslaCtx).toHaveProperty("openInterest");
|
|
90
|
+
console.log(`xyz:TSLA — mark: $${tslaCtx.markPx}, funding: ${tslaCtx.funding}, OI: ${tslaCtx.openInterest}`);
|
|
91
|
+
}, 15000);
|
|
92
|
+
it("fetches dex-specific meta (used for asset map)", async () => {
|
|
93
|
+
const data = await infoPost({ type: "meta", dex: "xyz" });
|
|
94
|
+
expect(data).toHaveProperty("universe");
|
|
95
|
+
expect(data.universe.length).toBeGreaterThan(0);
|
|
96
|
+
expect(data.universe[0].name).toContain("xyz:");
|
|
97
|
+
// Verify assets can be mapped to indices
|
|
98
|
+
const assetMap = new Map();
|
|
99
|
+
data.universe.forEach((a, i) => {
|
|
100
|
+
assetMap.set(a.name, i);
|
|
101
|
+
});
|
|
102
|
+
expect(assetMap.has("xyz:TSLA")).toBe(true);
|
|
103
|
+
expect(assetMap.has("xyz:NVDA")).toBe(true);
|
|
104
|
+
console.log(`xyz dex has ${assetMap.size} assets`);
|
|
105
|
+
}, 15000);
|
|
106
|
+
});
|
|
107
|
+
describe("HIP-3 Integration — dex-specific clearinghouseState", () => {
|
|
108
|
+
it("returns valid structure for a random address", async () => {
|
|
109
|
+
// Use a zero address — no positions, but structure should be valid
|
|
110
|
+
const address = "0x0000000000000000000000000000000000000001";
|
|
111
|
+
const data = await infoPost({
|
|
112
|
+
type: "clearinghouseState",
|
|
113
|
+
user: address,
|
|
114
|
+
dex: "xyz",
|
|
115
|
+
});
|
|
116
|
+
// Should return clearinghouse state structure
|
|
117
|
+
expect(data).toBeTruthy();
|
|
118
|
+
expect(data).toHaveProperty("assetPositions");
|
|
119
|
+
expect(Array.isArray(data.assetPositions)).toBe(true);
|
|
120
|
+
// Zero address should have no positions
|
|
121
|
+
expect(data.assetPositions.length).toBe(0);
|
|
122
|
+
// Should have margin info
|
|
123
|
+
const hasMoney = data.marginSummary || data.crossMarginSummary;
|
|
124
|
+
expect(hasMoney).toBeTruthy();
|
|
125
|
+
}, 15000);
|
|
126
|
+
});
|
|
127
|
+
describe("HIP-3 Integration — native vs dex asset separation", () => {
|
|
128
|
+
it("native perps do not have prefixed names", async () => {
|
|
129
|
+
const data = await infoPost({ type: "metaAndAssetCtxs" });
|
|
130
|
+
const universe = data[0]?.universe ?? [];
|
|
131
|
+
// Native assets should NOT have colon prefix
|
|
132
|
+
const btc = universe.find((a) => a.name === "BTC");
|
|
133
|
+
expect(btc).toBeTruthy();
|
|
134
|
+
expect(btc.name).not.toContain(":");
|
|
135
|
+
// None of the native assets should be prefixed
|
|
136
|
+
const prefixed = universe.filter((a) => a.name.includes(":"));
|
|
137
|
+
expect(prefixed.length).toBe(0);
|
|
138
|
+
}, 15000);
|
|
139
|
+
it("dex perps all have matching prefix", async () => {
|
|
140
|
+
const data = await infoPost({ type: "metaAndAssetCtxs", dex: "xyz" });
|
|
141
|
+
const universe = data[0]?.universe ?? [];
|
|
142
|
+
// ALL xyz dex assets should have "xyz:" prefix
|
|
143
|
+
for (const asset of universe) {
|
|
144
|
+
expect(asset.name.startsWith("xyz:")).toBe(true);
|
|
145
|
+
}
|
|
146
|
+
}, 15000);
|
|
147
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll } from "vitest";
|
|
2
|
+
import { HyperliquidAdapter } from "../../exchanges/hyperliquid.js";
|
|
3
|
+
/**
|
|
4
|
+
* Integration tests for Hyperliquid exchange adapter (Testnet).
|
|
5
|
+
*
|
|
6
|
+
* Prerequisites:
|
|
7
|
+
* 1. Set HYPERLIQUID_PRIVATE_KEY env var (0x-prefixed EVM private key, 66 chars)
|
|
8
|
+
* 2. Have testnet ETH on Arbitrum Sepolia (for gas)
|
|
9
|
+
* 3. Have testnet USDC deposited into Hyperliquid testnet
|
|
10
|
+
*
|
|
11
|
+
* Run: HYPERLIQUID_PRIVATE_KEY=<key> pnpm --filter perp-cli test -- --testPathPattern integration/hyperliquid
|
|
12
|
+
*/
|
|
13
|
+
const SKIP = !process.env.HYPERLIQUID_PRIVATE_KEY;
|
|
14
|
+
describe.skipIf(SKIP)("Hyperliquid Integration (Testnet)", () => {
|
|
15
|
+
let adapter;
|
|
16
|
+
beforeAll(async () => {
|
|
17
|
+
adapter = new HyperliquidAdapter(process.env.HYPERLIQUID_PRIVATE_KEY, true);
|
|
18
|
+
await adapter.init();
|
|
19
|
+
}, 30000);
|
|
20
|
+
describe("Read-only operations", () => {
|
|
21
|
+
it("initializes and has address", () => {
|
|
22
|
+
expect(adapter.address).toBeTruthy();
|
|
23
|
+
expect(adapter.address.startsWith("0x")).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
it("fetches markets", async () => {
|
|
26
|
+
const markets = await adapter.getMarkets();
|
|
27
|
+
expect(markets.length).toBeGreaterThan(0);
|
|
28
|
+
const btc = markets.find((m) => m.symbol === "BTC");
|
|
29
|
+
expect(btc).toBeTruthy();
|
|
30
|
+
expect(Number(btc.markPrice)).toBeGreaterThan(0);
|
|
31
|
+
});
|
|
32
|
+
it("resolves asset index for BTC", () => {
|
|
33
|
+
const idx = adapter.getAssetIndex("BTC");
|
|
34
|
+
expect(typeof idx).toBe("number");
|
|
35
|
+
expect(idx).toBeGreaterThanOrEqual(0);
|
|
36
|
+
});
|
|
37
|
+
it("fetches orderbook for BTC", async () => {
|
|
38
|
+
const book = await adapter.getOrderbook("BTC");
|
|
39
|
+
expect(book.bids.length).toBeGreaterThan(0);
|
|
40
|
+
expect(book.asks.length).toBeGreaterThan(0);
|
|
41
|
+
});
|
|
42
|
+
it("fetches balance", async () => {
|
|
43
|
+
const balance = await adapter.getBalance();
|
|
44
|
+
expect(balance).toHaveProperty("equity");
|
|
45
|
+
expect(Number(balance.equity)).toBeGreaterThanOrEqual(0);
|
|
46
|
+
});
|
|
47
|
+
it("fetches positions", async () => {
|
|
48
|
+
const positions = await adapter.getPositions();
|
|
49
|
+
expect(Array.isArray(positions)).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
it("fetches open orders", async () => {
|
|
52
|
+
const orders = await adapter.getOpenOrders();
|
|
53
|
+
expect(Array.isArray(orders)).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe("Trading operations", () => {
|
|
57
|
+
const TEST_SYMBOL = "BTC";
|
|
58
|
+
let limitOrderId;
|
|
59
|
+
it("places a limit buy order (far from market)", async () => {
|
|
60
|
+
const result = await adapter.limitOrder(TEST_SYMBOL, "buy", "10000", "0.001");
|
|
61
|
+
expect(result).toBeTruthy();
|
|
62
|
+
const orders = await adapter.getOpenOrders();
|
|
63
|
+
const myOrder = orders.find((o) => o.symbol === TEST_SYMBOL && o.side === "buy");
|
|
64
|
+
if (myOrder) {
|
|
65
|
+
limitOrderId = myOrder.orderId;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
it("cancels the limit order", async () => {
|
|
69
|
+
if (!limitOrderId)
|
|
70
|
+
return;
|
|
71
|
+
const result = await adapter.cancelOrder(TEST_SYMBOL, limitOrderId);
|
|
72
|
+
expect(result).toBeTruthy();
|
|
73
|
+
});
|
|
74
|
+
it("cancel all orders succeeds", async () => {
|
|
75
|
+
const result = await adapter.cancelAllOrders();
|
|
76
|
+
expect(result).toBeTruthy();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll } from "vitest";
|
|
2
|
+
import { LighterAdapter } from "../../exchanges/lighter.js";
|
|
3
|
+
/**
|
|
4
|
+
* Integration tests for Lighter exchange adapter (Mainnet ONLY — no testnet available).
|
|
5
|
+
*
|
|
6
|
+
* ⚠️ WARNING: Lighter has NO testnet. These tests run on MAINNET with REAL funds.
|
|
7
|
+
* Only run if you understand the risks and have funded an account.
|
|
8
|
+
*
|
|
9
|
+
* Prerequisites:
|
|
10
|
+
* 1. Set LIGHTER_PRIVATE_KEY env var (0x-prefixed EVM private key, 66 chars)
|
|
11
|
+
* 2. Have ETH on Ethereum L1 for gas (~$3-10 per tx)
|
|
12
|
+
* 3. Have USDC deposited into Lighter (mainnet)
|
|
13
|
+
*
|
|
14
|
+
* Run: LIGHTER_PRIVATE_KEY=<key> LIGHTER_INTEGRATION=1 pnpm --filter perp-cli test -- --testPathPattern integration/lighter
|
|
15
|
+
*/
|
|
16
|
+
// Require BOTH the key AND explicit opt-in flag (since this is mainnet)
|
|
17
|
+
const SKIP = !process.env.LIGHTER_PRIVATE_KEY || !process.env.LIGHTER_INTEGRATION;
|
|
18
|
+
describe.skipIf(SKIP)("Lighter Integration (Mainnet)", () => {
|
|
19
|
+
let adapter;
|
|
20
|
+
beforeAll(async () => {
|
|
21
|
+
adapter = new LighterAdapter(process.env.LIGHTER_PRIVATE_KEY);
|
|
22
|
+
await adapter.init();
|
|
23
|
+
}, 30000);
|
|
24
|
+
describe("Read-only operations (safe)", () => {
|
|
25
|
+
it("initializes with account index", () => {
|
|
26
|
+
expect(adapter.accountIndex).toBeGreaterThanOrEqual(0);
|
|
27
|
+
});
|
|
28
|
+
it("fetches markets", async () => {
|
|
29
|
+
const markets = await adapter.getMarkets();
|
|
30
|
+
expect(Array.isArray(markets)).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
it("fetches orderbook", async () => {
|
|
33
|
+
const book = await adapter.getOrderbook("BTC");
|
|
34
|
+
expect(book).toHaveProperty("bids");
|
|
35
|
+
expect(book).toHaveProperty("asks");
|
|
36
|
+
});
|
|
37
|
+
it("fetches balance", async () => {
|
|
38
|
+
const balance = await adapter.getBalance();
|
|
39
|
+
expect(balance).toHaveProperty("equity");
|
|
40
|
+
expect(Number(balance.equity)).toBeGreaterThanOrEqual(0);
|
|
41
|
+
});
|
|
42
|
+
it("fetches positions", async () => {
|
|
43
|
+
const positions = await adapter.getPositions();
|
|
44
|
+
expect(Array.isArray(positions)).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
it("fetches open orders", async () => {
|
|
47
|
+
const orders = await adapter.getOpenOrders();
|
|
48
|
+
expect(Array.isArray(orders)).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
// Trading tests intentionally omitted for Lighter (mainnet = real money)
|
|
52
|
+
// Use the CLI manually for trade testing: perp trade limit BTC buy 10000 0.001 --exchange lighter
|
|
53
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E integration tests for new atomic commands and api-spec.
|
|
3
|
+
* These tests spawn the actual CLI process and verify JSON output.
|
|
4
|
+
*
|
|
5
|
+
* - api-spec: no adapter needed, always works
|
|
6
|
+
* - market mid: needs HL mainnet (read-only, no key needed for public data)
|
|
7
|
+
* - error envelopes: verify structured errors across commands
|
|
8
|
+
*/
|
|
9
|
+
import "dotenv/config";
|