@guiie/buda-mcp 1.4.2 → 1.5.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 +39 -0
- package/dist/cache.d.ts +1 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +1 -0
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +16 -0
- package/dist/http.js +55 -0
- package/dist/index.js +30 -0
- package/dist/tools/account.d.ts +19 -0
- package/dist/tools/account.d.ts.map +1 -0
- package/dist/tools/account.js +49 -0
- package/dist/tools/balance.d.ts +29 -0
- package/dist/tools/balance.d.ts.map +1 -0
- package/dist/tools/balance.js +72 -0
- package/dist/tools/banks.d.ts +28 -0
- package/dist/tools/banks.d.ts.map +1 -0
- package/dist/tools/banks.js +68 -0
- package/dist/tools/batch_orders.d.ts +77 -0
- package/dist/tools/batch_orders.d.ts.map +1 -0
- package/dist/tools/batch_orders.js +154 -0
- package/dist/tools/cancel_all_orders.d.ts +34 -0
- package/dist/tools/cancel_all_orders.d.ts.map +1 -0
- package/dist/tools/cancel_all_orders.js +89 -0
- package/dist/tools/cancel_order_by_client_id.d.ts +34 -0
- package/dist/tools/cancel_order_by_client_id.d.ts.map +1 -0
- package/dist/tools/cancel_order_by_client_id.js +102 -0
- package/dist/tools/deposits.d.ts +83 -0
- package/dist/tools/deposits.d.ts.map +1 -0
- package/dist/tools/deposits.js +174 -0
- package/dist/tools/fees.d.ts +34 -0
- package/dist/tools/fees.d.ts.map +1 -0
- package/dist/tools/fees.js +72 -0
- package/dist/tools/lightning.d.ts +68 -0
- package/dist/tools/lightning.d.ts.map +1 -0
- package/dist/tools/lightning.js +171 -0
- package/dist/tools/order_lookup.d.ts +50 -0
- package/dist/tools/order_lookup.d.ts.map +1 -0
- package/dist/tools/order_lookup.js +112 -0
- package/dist/tools/place_order.d.ts +30 -0
- package/dist/tools/place_order.d.ts.map +1 -1
- package/dist/tools/place_order.js +100 -2
- package/dist/tools/quotation.d.ts +44 -0
- package/dist/tools/quotation.d.ts.map +1 -0
- package/dist/tools/quotation.js +99 -0
- package/dist/tools/receive_addresses.d.ts +78 -0
- package/dist/tools/receive_addresses.d.ts.map +1 -0
- package/dist/tools/receive_addresses.js +161 -0
- package/dist/tools/remittance_recipients.d.ts +54 -0
- package/dist/tools/remittance_recipients.d.ts.map +1 -0
- package/dist/tools/remittance_recipients.js +106 -0
- package/dist/tools/remittances.d.ts +115 -0
- package/dist/tools/remittances.d.ts.map +1 -0
- package/dist/tools/remittances.js +237 -0
- package/dist/tools/simulate_order.d.ts.map +1 -1
- package/dist/tools/simulate_order.js +2 -1
- package/dist/tools/withdrawals.d.ts +93 -0
- package/dist/tools/withdrawals.d.ts.map +1 -0
- package/dist/tools/withdrawals.js +215 -0
- package/dist/types.d.ts +155 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts +5 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +12 -0
- package/marketplace/README.md +1 -1
- package/marketplace/claude-listing.md +30 -2
- package/marketplace/gemini-tools.json +155 -1
- package/marketplace/openapi.yaml +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/cache.ts +1 -0
- package/src/client.ts +20 -0
- package/src/http.ts +55 -0
- package/src/index.ts +30 -0
- package/src/tools/account.ts +66 -0
- package/src/tools/balance.ts +94 -0
- package/src/tools/banks.ts +94 -0
- package/src/tools/batch_orders.ts +199 -0
- package/src/tools/cancel_all_orders.ts +117 -0
- package/src/tools/cancel_order_by_client_id.ts +132 -0
- package/src/tools/deposits.ts +230 -0
- package/src/tools/fees.ts +91 -0
- package/src/tools/lightning.ts +231 -0
- package/src/tools/order_lookup.ts +139 -0
- package/src/tools/place_order.ts +119 -2
- package/src/tools/quotation.ts +124 -0
- package/src/tools/receive_addresses.ts +216 -0
- package/src/tools/remittance_recipients.ts +139 -0
- package/src/tools/remittances.ts +299 -0
- package/src/tools/simulate_order.ts +1 -0
- package/src/tools/withdrawals.ts +276 -0
- package/src/types.ts +210 -0
- package/src/validation.ts +16 -0
- package/test/run-all.ts +16 -0
- package/test/unit.ts +1905 -0
package/test/unit.ts
CHANGED
|
@@ -17,6 +17,24 @@ import { handleCalculatePositionSize } from "../src/tools/calculate_position_siz
|
|
|
17
17
|
import { handleMarketSentiment } from "../src/tools/market_sentiment.js";
|
|
18
18
|
import { handleTechnicalIndicators } from "../src/tools/technical_indicators.js";
|
|
19
19
|
import { handleScheduleCancelAll, handleRenewCancelTimer, handleDisarmCancelTimer } from "../src/tools/dead_mans_switch.js";
|
|
20
|
+
import { validateCurrency } from "../src/validation.js";
|
|
21
|
+
import { handleGetAccountInfo } from "../src/tools/account.js";
|
|
22
|
+
import { handleGetBalance } from "../src/tools/balance.js";
|
|
23
|
+
import { handleGetOrder, handleGetOrderByClientId } from "../src/tools/order_lookup.js";
|
|
24
|
+
import { handleGetNetworkFees } from "../src/tools/fees.js";
|
|
25
|
+
import { handleGetDepositHistory } from "../src/tools/deposits.js";
|
|
26
|
+
import { handleGetWithdrawalHistory } from "../src/tools/withdrawals.js";
|
|
27
|
+
import { handleListReceiveAddresses, handleGetReceiveAddress, handleCreateReceiveAddress } from "../src/tools/receive_addresses.js";
|
|
28
|
+
import { handleListRemittances, handleGetRemittance, handleQuoteRemittance, handleAcceptRemittanceQuote } from "../src/tools/remittances.js";
|
|
29
|
+
import { handleGetRealQuotation } from "../src/tools/quotation.js";
|
|
30
|
+
import { handleListRemittanceRecipients, handleGetRemittanceRecipient } from "../src/tools/remittance_recipients.js";
|
|
31
|
+
import { handleGetAvailableBanks } from "../src/tools/banks.js";
|
|
32
|
+
import { handleCancelAllOrders } from "../src/tools/cancel_all_orders.js";
|
|
33
|
+
import { handleCancelOrderByClientId } from "../src/tools/cancel_order_by_client_id.js";
|
|
34
|
+
import { handlePlaceBatchOrders } from "../src/tools/batch_orders.js";
|
|
35
|
+
import { handleCreateWithdrawal } from "../src/tools/withdrawals.js";
|
|
36
|
+
import { handleCreateFiatDeposit } from "../src/tools/deposits.js";
|
|
37
|
+
import { handleLightningWithdrawal, handleCreateLightningInvoice } from "../src/tools/lightning.js";
|
|
20
38
|
|
|
21
39
|
// ----------------------------------------------------------------
|
|
22
40
|
// Minimal test harness
|
|
@@ -1159,6 +1177,1893 @@ await test("disarm after arm: timer is cleared", async () => {
|
|
|
1159
1177
|
assert(renewResult.isError === true, "should have no timer left after disarm");
|
|
1160
1178
|
});
|
|
1161
1179
|
|
|
1180
|
+
// ----------------------------------------------------------------
|
|
1181
|
+
// n. validateCurrency
|
|
1182
|
+
// ----------------------------------------------------------------
|
|
1183
|
+
|
|
1184
|
+
section("n. validateCurrency");
|
|
1185
|
+
|
|
1186
|
+
await test("validateCurrency: accepts BTC", () => {
|
|
1187
|
+
assertEqual(validateCurrency("BTC"), null, "BTC should be valid");
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
await test("validateCurrency: accepts CLP (fiat)", () => {
|
|
1191
|
+
assertEqual(validateCurrency("CLP"), null, "CLP should be valid");
|
|
1192
|
+
});
|
|
1193
|
+
|
|
1194
|
+
await test("validateCurrency: accepts USDC (multi-char)", () => {
|
|
1195
|
+
assertEqual(validateCurrency("USDC"), null, "USDC should be valid");
|
|
1196
|
+
});
|
|
1197
|
+
|
|
1198
|
+
await test("validateCurrency: case-insensitive — accepts lowercase btc", () => {
|
|
1199
|
+
assertEqual(validateCurrency("btc"), null, "lowercase btc should be valid");
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
await test("validateCurrency: rejects empty string", () => {
|
|
1203
|
+
assert(validateCurrency("") !== null, "should reject empty string");
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
await test("validateCurrency: rejects special characters", () => {
|
|
1207
|
+
assert(validateCurrency("BT$") !== null, "should reject $");
|
|
1208
|
+
assert(validateCurrency("BTC!") !== null, "should reject !");
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1211
|
+
await test("validateCurrency: rejects string longer than 10 chars", () => {
|
|
1212
|
+
assert(validateCurrency("ABCDEFGHIJK") !== null, "should reject 11-char string");
|
|
1213
|
+
});
|
|
1214
|
+
|
|
1215
|
+
await test("validateCurrency: rejects single character", () => {
|
|
1216
|
+
assert(validateCurrency("B") !== null, "should reject 1-char string");
|
|
1217
|
+
});
|
|
1218
|
+
|
|
1219
|
+
// ----------------------------------------------------------------
|
|
1220
|
+
// o. P1 auth tools — INVALID_CURRENCY guard
|
|
1221
|
+
// ----------------------------------------------------------------
|
|
1222
|
+
|
|
1223
|
+
section("o. P1 auth tools — INVALID_CURRENCY guard");
|
|
1224
|
+
|
|
1225
|
+
await test("handleGetBalance: invalid currency '!!' returns INVALID_CURRENCY", async () => {
|
|
1226
|
+
const fakeClient = {} as BudaClient;
|
|
1227
|
+
const result = await handleGetBalance({ currency: "!!" }, fakeClient);
|
|
1228
|
+
assert(result.isError === true, "should be error");
|
|
1229
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
1230
|
+
assertEqual(parsed.code, "INVALID_CURRENCY", "code should be INVALID_CURRENCY");
|
|
1231
|
+
});
|
|
1232
|
+
|
|
1233
|
+
await test("handleGetBalance: invalid currency empty string returns INVALID_CURRENCY", async () => {
|
|
1234
|
+
const fakeClient = {} as BudaClient;
|
|
1235
|
+
const result = await handleGetBalance({ currency: "" }, fakeClient);
|
|
1236
|
+
assert(result.isError === true, "should be error");
|
|
1237
|
+
});
|
|
1238
|
+
|
|
1239
|
+
await test("handleGetNetworkFees: invalid currency returns INVALID_CURRENCY", async () => {
|
|
1240
|
+
const fakeClient = {} as BudaClient;
|
|
1241
|
+
const result = await handleGetNetworkFees({ currency: "$$", type: "withdrawal" }, fakeClient);
|
|
1242
|
+
assert(result.isError === true, "should be error");
|
|
1243
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
1244
|
+
assertEqual(parsed.code, "INVALID_CURRENCY", "code should be INVALID_CURRENCY");
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
await test("handleGetDepositHistory: invalid currency returns INVALID_CURRENCY", async () => {
|
|
1248
|
+
const fakeClient = {} as BudaClient;
|
|
1249
|
+
const result = await handleGetDepositHistory({ currency: "BAD!!" }, fakeClient);
|
|
1250
|
+
assert(result.isError === true, "should be error");
|
|
1251
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
1252
|
+
assertEqual(parsed.code, "INVALID_CURRENCY", "code should be INVALID_CURRENCY");
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1255
|
+
await test("handleGetWithdrawalHistory: invalid currency returns INVALID_CURRENCY", async () => {
|
|
1256
|
+
const fakeClient = {} as BudaClient;
|
|
1257
|
+
const result = await handleGetWithdrawalHistory({ currency: "B" }, fakeClient);
|
|
1258
|
+
assert(result.isError === true, "should be error");
|
|
1259
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
1260
|
+
assertEqual(parsed.code, "INVALID_CURRENCY", "code should be INVALID_CURRENCY");
|
|
1261
|
+
});
|
|
1262
|
+
|
|
1263
|
+
await test("handleListReceiveAddresses: invalid currency returns INVALID_CURRENCY", async () => {
|
|
1264
|
+
const fakeClient = {} as BudaClient;
|
|
1265
|
+
const result = await handleListReceiveAddresses({ currency: "!!" }, fakeClient);
|
|
1266
|
+
assert(result.isError === true, "should be error");
|
|
1267
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
1268
|
+
assertEqual(parsed.code, "INVALID_CURRENCY", "code should be INVALID_CURRENCY");
|
|
1269
|
+
});
|
|
1270
|
+
|
|
1271
|
+
await test("handleGetReceiveAddress: invalid currency returns INVALID_CURRENCY", async () => {
|
|
1272
|
+
const fakeClient = {} as BudaClient;
|
|
1273
|
+
const result = await handleGetReceiveAddress({ currency: "!!", id: 1 }, fakeClient);
|
|
1274
|
+
assert(result.isError === true, "should be error");
|
|
1275
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
1276
|
+
assertEqual(parsed.code, "INVALID_CURRENCY", "code should be INVALID_CURRENCY");
|
|
1277
|
+
});
|
|
1278
|
+
|
|
1279
|
+
await test("handleGetAvailableBanks: invalid currency returns INVALID_CURRENCY", async () => {
|
|
1280
|
+
const fakeClient = {} as BudaClient;
|
|
1281
|
+
const fakeCache = new MemoryCache();
|
|
1282
|
+
const result = await handleGetAvailableBanks({ currency: "!!" }, fakeClient, fakeCache);
|
|
1283
|
+
assert(result.isError === true, "should be error");
|
|
1284
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
1285
|
+
assertEqual(parsed.code, "INVALID_CURRENCY", "code should be INVALID_CURRENCY");
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
// ----------------------------------------------------------------
|
|
1289
|
+
// p. P1 tools — happy path (mocked API)
|
|
1290
|
+
// ----------------------------------------------------------------
|
|
1291
|
+
|
|
1292
|
+
section("p. P1 tools — happy path (mocked API)");
|
|
1293
|
+
|
|
1294
|
+
await test("handleGetAccountInfo: returns flattened profile", async () => {
|
|
1295
|
+
const savedFetch = globalThis.fetch;
|
|
1296
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1297
|
+
new Response(
|
|
1298
|
+
JSON.stringify({
|
|
1299
|
+
me: {
|
|
1300
|
+
id: 42,
|
|
1301
|
+
email: "user@example.com",
|
|
1302
|
+
name: "Test User",
|
|
1303
|
+
monthly_transacted: ["5000000", "CLP"],
|
|
1304
|
+
pubsub_key: "pk_test",
|
|
1305
|
+
},
|
|
1306
|
+
}),
|
|
1307
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
1308
|
+
);
|
|
1309
|
+
try {
|
|
1310
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1311
|
+
const result = await handleGetAccountInfo(client);
|
|
1312
|
+
assert(!result.isError, "should not be error");
|
|
1313
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
1314
|
+
id: number;
|
|
1315
|
+
email: string;
|
|
1316
|
+
monthly_transacted: number;
|
|
1317
|
+
monthly_transacted_currency: string;
|
|
1318
|
+
};
|
|
1319
|
+
assertEqual(parsed.id, 42, "id should be 42");
|
|
1320
|
+
assertEqual(parsed.email, "user@example.com", "email should match");
|
|
1321
|
+
assertEqual(parsed.monthly_transacted, 5000000, "monthly_transacted should be a float");
|
|
1322
|
+
assertEqual(parsed.monthly_transacted_currency, "CLP", "currency should be CLP");
|
|
1323
|
+
} finally {
|
|
1324
|
+
globalThis.fetch = savedFetch;
|
|
1325
|
+
}
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
await test("handleGetBalance: returns flattened balance", async () => {
|
|
1329
|
+
const savedFetch = globalThis.fetch;
|
|
1330
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1331
|
+
new Response(
|
|
1332
|
+
JSON.stringify({
|
|
1333
|
+
balance: {
|
|
1334
|
+
id: "BTC",
|
|
1335
|
+
amount: ["0.5", "BTC"],
|
|
1336
|
+
available_amount: ["0.4", "BTC"],
|
|
1337
|
+
frozen_amount: ["0.1", "BTC"],
|
|
1338
|
+
pending_withdraw_amount: ["0.0", "BTC"],
|
|
1339
|
+
},
|
|
1340
|
+
}),
|
|
1341
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
1342
|
+
);
|
|
1343
|
+
try {
|
|
1344
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1345
|
+
const result = await handleGetBalance({ currency: "BTC" }, client);
|
|
1346
|
+
assert(!result.isError, "should not be error");
|
|
1347
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
1348
|
+
id: string;
|
|
1349
|
+
amount: number;
|
|
1350
|
+
available_amount: number;
|
|
1351
|
+
frozen_amount: number;
|
|
1352
|
+
};
|
|
1353
|
+
assertEqual(parsed.id, "BTC", "id should be BTC");
|
|
1354
|
+
assertEqual(parsed.amount, 0.5, "amount should be 0.5");
|
|
1355
|
+
assertEqual(parsed.available_amount, 0.4, "available_amount should be 0.4");
|
|
1356
|
+
assertEqual(parsed.frozen_amount, 0.1, "frozen_amount should be 0.1");
|
|
1357
|
+
} finally {
|
|
1358
|
+
globalThis.fetch = savedFetch;
|
|
1359
|
+
}
|
|
1360
|
+
});
|
|
1361
|
+
|
|
1362
|
+
await test("handleGetOrder: returns normalized order", async () => {
|
|
1363
|
+
const savedFetch = globalThis.fetch;
|
|
1364
|
+
const mockOrder = {
|
|
1365
|
+
order: {
|
|
1366
|
+
id: 123456,
|
|
1367
|
+
type: "Bid",
|
|
1368
|
+
state: "active",
|
|
1369
|
+
created_at: "2024-01-01T00:00:00Z",
|
|
1370
|
+
market_id: "BTC-CLP",
|
|
1371
|
+
fee_currency: "CLP",
|
|
1372
|
+
price_type: "limit",
|
|
1373
|
+
order_type: "limit_order",
|
|
1374
|
+
client_id: null,
|
|
1375
|
+
limit: ["65000000", "CLP"],
|
|
1376
|
+
amount: ["0.001", "BTC"],
|
|
1377
|
+
original_amount: ["0.001", "BTC"],
|
|
1378
|
+
traded_amount: ["0", "BTC"],
|
|
1379
|
+
total_exchanged: ["0", "CLP"],
|
|
1380
|
+
paid_fee: ["0", "CLP"],
|
|
1381
|
+
},
|
|
1382
|
+
};
|
|
1383
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1384
|
+
new Response(JSON.stringify(mockOrder), { status: 200, headers: { "Content-Type": "application/json" } });
|
|
1385
|
+
try {
|
|
1386
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1387
|
+
const result = await handleGetOrder({ order_id: 123456 }, client);
|
|
1388
|
+
assert(!result.isError, "should not be error");
|
|
1389
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
1390
|
+
id: number;
|
|
1391
|
+
type: string;
|
|
1392
|
+
state: string;
|
|
1393
|
+
limit_price: number;
|
|
1394
|
+
amount: number;
|
|
1395
|
+
};
|
|
1396
|
+
assertEqual(parsed.id, 123456, "id should match");
|
|
1397
|
+
assertEqual(parsed.type, "Bid", "type should be Bid");
|
|
1398
|
+
assertEqual(parsed.state, "active", "state should be active");
|
|
1399
|
+
assertEqual(parsed.limit_price, 65000000, "limit_price should be a float");
|
|
1400
|
+
assertEqual(parsed.amount, 0.001, "amount should be 0.001");
|
|
1401
|
+
} finally {
|
|
1402
|
+
globalThis.fetch = savedFetch;
|
|
1403
|
+
}
|
|
1404
|
+
});
|
|
1405
|
+
|
|
1406
|
+
await test("handleGetOrder: 404 returns isError with code 404", async () => {
|
|
1407
|
+
const savedFetch = globalThis.fetch;
|
|
1408
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1409
|
+
new Response(JSON.stringify({ message: "Not found" }), { status: 404 });
|
|
1410
|
+
try {
|
|
1411
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1412
|
+
const result = await handleGetOrder({ order_id: 999999 }, client);
|
|
1413
|
+
assert(result.isError === true, "should be error");
|
|
1414
|
+
const parsed = JSON.parse(result.content[0].text) as { code: number };
|
|
1415
|
+
assertEqual(parsed.code, 404, "code should be 404");
|
|
1416
|
+
} finally {
|
|
1417
|
+
globalThis.fetch = savedFetch;
|
|
1418
|
+
}
|
|
1419
|
+
});
|
|
1420
|
+
|
|
1421
|
+
await test("handleGetOrderByClientId: returns normalized order", async () => {
|
|
1422
|
+
const savedFetch = globalThis.fetch;
|
|
1423
|
+
const mockOrder = {
|
|
1424
|
+
order: {
|
|
1425
|
+
id: 77777,
|
|
1426
|
+
type: "Ask",
|
|
1427
|
+
state: "traded",
|
|
1428
|
+
created_at: "2024-06-01T00:00:00Z",
|
|
1429
|
+
market_id: "ETH-BTC",
|
|
1430
|
+
fee_currency: "BTC",
|
|
1431
|
+
price_type: "limit",
|
|
1432
|
+
order_type: "limit_order",
|
|
1433
|
+
client_id: "my-bot-42",
|
|
1434
|
+
limit: ["0.05", "BTC"],
|
|
1435
|
+
amount: ["1", "ETH"],
|
|
1436
|
+
original_amount: ["1", "ETH"],
|
|
1437
|
+
traded_amount: ["1", "ETH"],
|
|
1438
|
+
total_exchanged: ["0.05", "BTC"],
|
|
1439
|
+
paid_fee: ["0.0004", "BTC"],
|
|
1440
|
+
},
|
|
1441
|
+
};
|
|
1442
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1443
|
+
new Response(JSON.stringify(mockOrder), { status: 200, headers: { "Content-Type": "application/json" } });
|
|
1444
|
+
try {
|
|
1445
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1446
|
+
const result = await handleGetOrderByClientId({ client_id: "my-bot-42" }, client);
|
|
1447
|
+
assert(!result.isError, "should not be error");
|
|
1448
|
+
const parsed = JSON.parse(result.content[0].text) as { id: number; client_id: string };
|
|
1449
|
+
assertEqual(parsed.id, 77777, "id should match");
|
|
1450
|
+
assertEqual(parsed.client_id, "my-bot-42", "client_id should match");
|
|
1451
|
+
} finally {
|
|
1452
|
+
globalThis.fetch = savedFetch;
|
|
1453
|
+
}
|
|
1454
|
+
});
|
|
1455
|
+
|
|
1456
|
+
await test("handleGetDepositHistory: returns flattened deposits with meta", async () => {
|
|
1457
|
+
const savedFetch = globalThis.fetch;
|
|
1458
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1459
|
+
new Response(
|
|
1460
|
+
JSON.stringify({
|
|
1461
|
+
deposits: [
|
|
1462
|
+
{
|
|
1463
|
+
id: 1,
|
|
1464
|
+
state: "confirmed",
|
|
1465
|
+
currency: "BTC",
|
|
1466
|
+
amount: ["0.1", "BTC"],
|
|
1467
|
+
fee: ["0.0001", "BTC"],
|
|
1468
|
+
created_at: "2024-01-01T00:00:00Z",
|
|
1469
|
+
updated_at: "2024-01-01T01:00:00Z",
|
|
1470
|
+
transfer_account_id: null,
|
|
1471
|
+
transaction_hash: "abc123",
|
|
1472
|
+
},
|
|
1473
|
+
],
|
|
1474
|
+
meta: { current_page: 1, total_count: 1, total_pages: 1 },
|
|
1475
|
+
}),
|
|
1476
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
1477
|
+
);
|
|
1478
|
+
try {
|
|
1479
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1480
|
+
const result = await handleGetDepositHistory({ currency: "BTC" }, client);
|
|
1481
|
+
assert(!result.isError, "should not be error");
|
|
1482
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
1483
|
+
deposits: Array<{ id: number; amount: number; fee: number; state: string }>;
|
|
1484
|
+
meta: { total_count: number };
|
|
1485
|
+
};
|
|
1486
|
+
assertEqual(parsed.deposits.length, 1, "should have 1 deposit");
|
|
1487
|
+
assertEqual(parsed.deposits[0].id, 1, "id should be 1");
|
|
1488
|
+
assertEqual(parsed.deposits[0].amount, 0.1, "amount should be 0.1");
|
|
1489
|
+
assertEqual(parsed.deposits[0].fee, 0.0001, "fee should be a float");
|
|
1490
|
+
assertEqual(parsed.meta.total_count, 1, "total_count should be 1");
|
|
1491
|
+
} finally {
|
|
1492
|
+
globalThis.fetch = savedFetch;
|
|
1493
|
+
}
|
|
1494
|
+
});
|
|
1495
|
+
|
|
1496
|
+
await test("handleGetDepositHistory: empty list is not an error", async () => {
|
|
1497
|
+
const savedFetch = globalThis.fetch;
|
|
1498
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1499
|
+
new Response(
|
|
1500
|
+
JSON.stringify({ deposits: [], meta: { current_page: 1, total_count: 0, total_pages: 0 } }),
|
|
1501
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
1502
|
+
);
|
|
1503
|
+
try {
|
|
1504
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1505
|
+
const result = await handleGetDepositHistory({ currency: "BTC" }, client);
|
|
1506
|
+
assert(!result.isError, "empty list should not be error");
|
|
1507
|
+
const parsed = JSON.parse(result.content[0].text) as { deposits: unknown[] };
|
|
1508
|
+
assertEqual(parsed.deposits.length, 0, "deposits should be empty array");
|
|
1509
|
+
} finally {
|
|
1510
|
+
globalThis.fetch = savedFetch;
|
|
1511
|
+
}
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
await test("handleGetWithdrawalHistory: returns flattened withdrawals", async () => {
|
|
1515
|
+
const savedFetch = globalThis.fetch;
|
|
1516
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1517
|
+
new Response(
|
|
1518
|
+
JSON.stringify({
|
|
1519
|
+
withdrawals: [
|
|
1520
|
+
{
|
|
1521
|
+
id: 5,
|
|
1522
|
+
state: "confirmed",
|
|
1523
|
+
currency: "CLP",
|
|
1524
|
+
amount: ["100000", "CLP"],
|
|
1525
|
+
fee: ["500", "CLP"],
|
|
1526
|
+
address: null,
|
|
1527
|
+
tx_hash: null,
|
|
1528
|
+
bank_account_id: 99,
|
|
1529
|
+
created_at: "2024-02-01T00:00:00Z",
|
|
1530
|
+
updated_at: "2024-02-01T01:00:00Z",
|
|
1531
|
+
},
|
|
1532
|
+
],
|
|
1533
|
+
meta: { current_page: 1, total_count: 1, total_pages: 1 },
|
|
1534
|
+
}),
|
|
1535
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
1536
|
+
);
|
|
1537
|
+
try {
|
|
1538
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1539
|
+
const result = await handleGetWithdrawalHistory({ currency: "CLP" }, client);
|
|
1540
|
+
assert(!result.isError, "should not be error");
|
|
1541
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
1542
|
+
withdrawals: Array<{ id: number; amount: number; bank_account_id: number }>;
|
|
1543
|
+
};
|
|
1544
|
+
assertEqual(parsed.withdrawals[0].id, 5, "id should be 5");
|
|
1545
|
+
assertEqual(parsed.withdrawals[0].amount, 100000, "amount should be 100000");
|
|
1546
|
+
assertEqual(parsed.withdrawals[0].bank_account_id, 99, "bank_account_id should be 99");
|
|
1547
|
+
} finally {
|
|
1548
|
+
globalThis.fetch = savedFetch;
|
|
1549
|
+
}
|
|
1550
|
+
});
|
|
1551
|
+
|
|
1552
|
+
await test("handleListReceiveAddresses: returns address list", async () => {
|
|
1553
|
+
const savedFetch = globalThis.fetch;
|
|
1554
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1555
|
+
new Response(
|
|
1556
|
+
JSON.stringify({
|
|
1557
|
+
receive_addresses: [
|
|
1558
|
+
{ id: 10, address: "bc1qtest", currency: "BTC", created_at: "2024-01-01T00:00:00Z", label: null },
|
|
1559
|
+
],
|
|
1560
|
+
}),
|
|
1561
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
1562
|
+
);
|
|
1563
|
+
try {
|
|
1564
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1565
|
+
const result = await handleListReceiveAddresses({ currency: "BTC" }, client);
|
|
1566
|
+
assert(!result.isError, "should not be error");
|
|
1567
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
1568
|
+
receive_addresses: Array<{ id: number; address: string }>;
|
|
1569
|
+
};
|
|
1570
|
+
assertEqual(parsed.receive_addresses.length, 1, "should have 1 address");
|
|
1571
|
+
assertEqual(parsed.receive_addresses[0].id, 10, "id should be 10");
|
|
1572
|
+
assertEqual(parsed.receive_addresses[0].address, "bc1qtest", "address should match");
|
|
1573
|
+
} finally {
|
|
1574
|
+
globalThis.fetch = savedFetch;
|
|
1575
|
+
}
|
|
1576
|
+
});
|
|
1577
|
+
|
|
1578
|
+
await test("handleGetReceiveAddress: returns single address", async () => {
|
|
1579
|
+
const savedFetch = globalThis.fetch;
|
|
1580
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1581
|
+
new Response(
|
|
1582
|
+
JSON.stringify({
|
|
1583
|
+
receive_address: { id: 10, address: "bc1qtest", currency: "BTC", created_at: "2024-01-01T00:00:00Z", label: "cold storage" },
|
|
1584
|
+
}),
|
|
1585
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
1586
|
+
);
|
|
1587
|
+
try {
|
|
1588
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1589
|
+
const result = await handleGetReceiveAddress({ currency: "BTC", id: 10 }, client);
|
|
1590
|
+
assert(!result.isError, "should not be error");
|
|
1591
|
+
const parsed = JSON.parse(result.content[0].text) as { id: number; label: string };
|
|
1592
|
+
assertEqual(parsed.id, 10, "id should be 10");
|
|
1593
|
+
assertEqual(parsed.label, "cold storage", "label should match");
|
|
1594
|
+
} finally {
|
|
1595
|
+
globalThis.fetch = savedFetch;
|
|
1596
|
+
}
|
|
1597
|
+
});
|
|
1598
|
+
|
|
1599
|
+
await test("handleListRemittances: returns remittance list with flattened amounts", async () => {
|
|
1600
|
+
const savedFetch = globalThis.fetch;
|
|
1601
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1602
|
+
new Response(
|
|
1603
|
+
JSON.stringify({
|
|
1604
|
+
remittances: [
|
|
1605
|
+
{
|
|
1606
|
+
id: 77,
|
|
1607
|
+
state: "quoted",
|
|
1608
|
+
currency: "CLP",
|
|
1609
|
+
amount: ["100000", "CLP"],
|
|
1610
|
+
recipient_id: 5,
|
|
1611
|
+
created_at: "2024-03-01T00:00:00Z",
|
|
1612
|
+
expires_at: "2024-03-01T01:00:00Z",
|
|
1613
|
+
},
|
|
1614
|
+
],
|
|
1615
|
+
meta: { current_page: 1, total_count: 1, total_pages: 1 },
|
|
1616
|
+
}),
|
|
1617
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
1618
|
+
);
|
|
1619
|
+
try {
|
|
1620
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1621
|
+
const result = await handleListRemittances({}, client);
|
|
1622
|
+
assert(!result.isError, "should not be error");
|
|
1623
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
1624
|
+
remittances: Array<{ id: number; amount: number; recipient_id: number }>;
|
|
1625
|
+
};
|
|
1626
|
+
assertEqual(parsed.remittances[0].id, 77, "id should be 77");
|
|
1627
|
+
assertEqual(parsed.remittances[0].amount, 100000, "amount should be a float");
|
|
1628
|
+
assertEqual(parsed.remittances[0].recipient_id, 5, "recipient_id should be 5");
|
|
1629
|
+
} finally {
|
|
1630
|
+
globalThis.fetch = savedFetch;
|
|
1631
|
+
}
|
|
1632
|
+
});
|
|
1633
|
+
|
|
1634
|
+
await test("handleGetRemittance: returns single remittance", async () => {
|
|
1635
|
+
const savedFetch = globalThis.fetch;
|
|
1636
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1637
|
+
new Response(
|
|
1638
|
+
JSON.stringify({
|
|
1639
|
+
remittance: {
|
|
1640
|
+
id: 88,
|
|
1641
|
+
state: "confirmed",
|
|
1642
|
+
currency: "COP",
|
|
1643
|
+
amount: ["500000", "COP"],
|
|
1644
|
+
recipient_id: 3,
|
|
1645
|
+
created_at: "2024-04-01T00:00:00Z",
|
|
1646
|
+
expires_at: null,
|
|
1647
|
+
},
|
|
1648
|
+
}),
|
|
1649
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
1650
|
+
);
|
|
1651
|
+
try {
|
|
1652
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1653
|
+
const result = await handleGetRemittance({ id: 88 }, client);
|
|
1654
|
+
assert(!result.isError, "should not be error");
|
|
1655
|
+
const parsed = JSON.parse(result.content[0].text) as { id: number; state: string; expires_at: null };
|
|
1656
|
+
assertEqual(parsed.id, 88, "id should be 88");
|
|
1657
|
+
assertEqual(parsed.state, "confirmed", "state should be confirmed");
|
|
1658
|
+
assertEqual(parsed.expires_at, null, "expires_at should be null");
|
|
1659
|
+
} finally {
|
|
1660
|
+
globalThis.fetch = savedFetch;
|
|
1661
|
+
}
|
|
1662
|
+
});
|
|
1663
|
+
|
|
1664
|
+
await test("handleListRemittanceRecipients: returns recipient list", async () => {
|
|
1665
|
+
const savedFetch = globalThis.fetch;
|
|
1666
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1667
|
+
new Response(
|
|
1668
|
+
JSON.stringify({
|
|
1669
|
+
remittance_recipients: [
|
|
1670
|
+
{ id: 1, name: "Alice", bank: "banco_estado", account_number: "123456", currency: "CLP", country: "CL" },
|
|
1671
|
+
],
|
|
1672
|
+
meta: { current_page: 1, total_count: 1, total_pages: 1 },
|
|
1673
|
+
}),
|
|
1674
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
1675
|
+
);
|
|
1676
|
+
try {
|
|
1677
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1678
|
+
const result = await handleListRemittanceRecipients({}, client);
|
|
1679
|
+
assert(!result.isError, "should not be error");
|
|
1680
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
1681
|
+
remittance_recipients: Array<{ id: number; name: string; bank: string }>;
|
|
1682
|
+
};
|
|
1683
|
+
assertEqual(parsed.remittance_recipients[0].id, 1, "id should be 1");
|
|
1684
|
+
assertEqual(parsed.remittance_recipients[0].name, "Alice", "name should match");
|
|
1685
|
+
assertEqual(parsed.remittance_recipients[0].bank, "banco_estado", "bank should match");
|
|
1686
|
+
} finally {
|
|
1687
|
+
globalThis.fetch = savedFetch;
|
|
1688
|
+
}
|
|
1689
|
+
});
|
|
1690
|
+
|
|
1691
|
+
await test("handleGetRemittanceRecipient: returns single recipient", async () => {
|
|
1692
|
+
const savedFetch = globalThis.fetch;
|
|
1693
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1694
|
+
new Response(
|
|
1695
|
+
JSON.stringify({
|
|
1696
|
+
remittance_recipient: {
|
|
1697
|
+
id: 7, name: "Bob", bank: "bancolombia", account_number: "987654", currency: "COP", country: null,
|
|
1698
|
+
},
|
|
1699
|
+
}),
|
|
1700
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
1701
|
+
);
|
|
1702
|
+
try {
|
|
1703
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1704
|
+
const result = await handleGetRemittanceRecipient({ id: 7 }, client);
|
|
1705
|
+
assert(!result.isError, "should not be error");
|
|
1706
|
+
const parsed = JSON.parse(result.content[0].text) as { id: number; country: null };
|
|
1707
|
+
assertEqual(parsed.id, 7, "id should be 7");
|
|
1708
|
+
assertEqual(parsed.country, null, "country should be null");
|
|
1709
|
+
} finally {
|
|
1710
|
+
globalThis.fetch = savedFetch;
|
|
1711
|
+
}
|
|
1712
|
+
});
|
|
1713
|
+
|
|
1714
|
+
await test("handleGetAvailableBanks: returns bank list for fiat currency", async () => {
|
|
1715
|
+
const savedFetch = globalThis.fetch;
|
|
1716
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1717
|
+
new Response(
|
|
1718
|
+
JSON.stringify({
|
|
1719
|
+
banks: [
|
|
1720
|
+
{ id: "banco_estado", name: "Banco Estado", country: "CL" },
|
|
1721
|
+
{ id: "bci", name: "BCI", country: "CL" },
|
|
1722
|
+
],
|
|
1723
|
+
}),
|
|
1724
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
1725
|
+
);
|
|
1726
|
+
try {
|
|
1727
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1728
|
+
const cache = new MemoryCache();
|
|
1729
|
+
const result = await handleGetAvailableBanks({ currency: "CLP" }, client, cache);
|
|
1730
|
+
assert(!result.isError, "should not be error");
|
|
1731
|
+
const parsed = JSON.parse(result.content[0].text) as { currency: string; banks: Array<{ id: string }> };
|
|
1732
|
+
assertEqual(parsed.currency, "CLP", "currency should be CLP");
|
|
1733
|
+
assertEqual(parsed.banks.length, 2, "should have 2 banks");
|
|
1734
|
+
assertEqual(parsed.banks[0].id, "banco_estado", "first bank id should match");
|
|
1735
|
+
} finally {
|
|
1736
|
+
globalThis.fetch = savedFetch;
|
|
1737
|
+
}
|
|
1738
|
+
});
|
|
1739
|
+
|
|
1740
|
+
await test("handleGetAvailableBanks: 404 returns empty list (not an error)", async () => {
|
|
1741
|
+
const savedFetch = globalThis.fetch;
|
|
1742
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1743
|
+
new Response(JSON.stringify({ message: "Not found" }), { status: 404 });
|
|
1744
|
+
try {
|
|
1745
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1746
|
+
const cache = new MemoryCache();
|
|
1747
|
+
const result = await handleGetAvailableBanks({ currency: "USD" }, client, cache);
|
|
1748
|
+
assert(!result.isError, "404 should NOT be an error — empty list");
|
|
1749
|
+
const parsed = JSON.parse(result.content[0].text) as { banks: unknown[] };
|
|
1750
|
+
assertEqual(parsed.banks.length, 0, "banks should be empty array");
|
|
1751
|
+
} finally {
|
|
1752
|
+
globalThis.fetch = savedFetch;
|
|
1753
|
+
}
|
|
1754
|
+
});
|
|
1755
|
+
|
|
1756
|
+
await test("handleGetAvailableBanks: 500 returns isError", async () => {
|
|
1757
|
+
const savedFetch = globalThis.fetch;
|
|
1758
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1759
|
+
new Response(JSON.stringify({ message: "Internal server error" }), { status: 500 });
|
|
1760
|
+
try {
|
|
1761
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1762
|
+
const cache = new MemoryCache();
|
|
1763
|
+
const result = await handleGetAvailableBanks({ currency: "CLP" }, client, cache);
|
|
1764
|
+
assert(result.isError === true, "500 should be isError");
|
|
1765
|
+
const parsed = JSON.parse(result.content[0].text) as { code: number };
|
|
1766
|
+
assertEqual(parsed.code, 500, "code should be 500");
|
|
1767
|
+
} finally {
|
|
1768
|
+
globalThis.fetch = savedFetch;
|
|
1769
|
+
}
|
|
1770
|
+
});
|
|
1771
|
+
|
|
1772
|
+
// ----------------------------------------------------------------
|
|
1773
|
+
// Priority 2 — get_real_quotation
|
|
1774
|
+
// ----------------------------------------------------------------
|
|
1775
|
+
|
|
1776
|
+
section("get_real_quotation");
|
|
1777
|
+
|
|
1778
|
+
await test("handleGetRealQuotation: INVALID_MARKET_ID without fetch", async () => {
|
|
1779
|
+
const fetchCalled = { value: false };
|
|
1780
|
+
const savedFetch = globalThis.fetch;
|
|
1781
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
1782
|
+
fetchCalled.value = true;
|
|
1783
|
+
return new Response("{}", { status: 200 });
|
|
1784
|
+
};
|
|
1785
|
+
try {
|
|
1786
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1787
|
+
const result = await handleGetRealQuotation({ market_id: "INVALID", type: "Bid", amount: 1 }, client);
|
|
1788
|
+
assert(result.isError === true, "should be error");
|
|
1789
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
1790
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
1791
|
+
assertEqual(parsed.code, "INVALID_MARKET_ID", "code should be INVALID_MARKET_ID");
|
|
1792
|
+
} finally {
|
|
1793
|
+
globalThis.fetch = savedFetch;
|
|
1794
|
+
}
|
|
1795
|
+
});
|
|
1796
|
+
|
|
1797
|
+
await test("handleGetRealQuotation: happy path Bid", async () => {
|
|
1798
|
+
const savedFetch = globalThis.fetch;
|
|
1799
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1800
|
+
new Response(
|
|
1801
|
+
JSON.stringify({
|
|
1802
|
+
quotation: {
|
|
1803
|
+
id: 42,
|
|
1804
|
+
type: "Bid",
|
|
1805
|
+
market_id: "BTC-CLP",
|
|
1806
|
+
amount: ["0.05", "BTC"],
|
|
1807
|
+
limit: null,
|
|
1808
|
+
base_balance_change: ["-0.05", "BTC"],
|
|
1809
|
+
quote_balance_change: ["4500000", "CLP"],
|
|
1810
|
+
fee_amount: ["22500", "CLP"],
|
|
1811
|
+
order_amount: ["0.05", "BTC"],
|
|
1812
|
+
},
|
|
1813
|
+
}),
|
|
1814
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
1815
|
+
);
|
|
1816
|
+
try {
|
|
1817
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1818
|
+
const result = await handleGetRealQuotation({ market_id: "BTC-CLP", type: "Bid", amount: 0.05 }, client);
|
|
1819
|
+
assert(!result.isError, "should not be error");
|
|
1820
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
1821
|
+
id: number;
|
|
1822
|
+
type: string;
|
|
1823
|
+
market_id: string;
|
|
1824
|
+
amount: number;
|
|
1825
|
+
amount_currency: string;
|
|
1826
|
+
limit: null;
|
|
1827
|
+
limit_currency: null;
|
|
1828
|
+
fee_amount: number;
|
|
1829
|
+
fee_currency: string;
|
|
1830
|
+
};
|
|
1831
|
+
assertEqual(parsed.id, 42, "id should be 42");
|
|
1832
|
+
assertEqual(parsed.type, "Bid", "type should be Bid");
|
|
1833
|
+
assertEqual(parsed.market_id, "BTC-CLP", "market_id should match");
|
|
1834
|
+
assertEqual(parsed.amount, 0.05, "amount should be 0.05");
|
|
1835
|
+
assertEqual(parsed.amount_currency, "BTC", "amount_currency should be BTC");
|
|
1836
|
+
assertEqual(parsed.limit, null, "limit should be null");
|
|
1837
|
+
assertEqual(parsed.limit_currency, null, "limit_currency should be null");
|
|
1838
|
+
assertEqual(parsed.fee_amount, 22500, "fee_amount should be 22500");
|
|
1839
|
+
assertEqual(parsed.fee_currency, "CLP", "fee_currency should be CLP");
|
|
1840
|
+
} finally {
|
|
1841
|
+
globalThis.fetch = savedFetch;
|
|
1842
|
+
}
|
|
1843
|
+
});
|
|
1844
|
+
|
|
1845
|
+
await test("handleGetRealQuotation: happy path Ask with limit", async () => {
|
|
1846
|
+
const savedFetch = globalThis.fetch;
|
|
1847
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1848
|
+
new Response(
|
|
1849
|
+
JSON.stringify({
|
|
1850
|
+
quotation: {
|
|
1851
|
+
id: null,
|
|
1852
|
+
type: "Ask",
|
|
1853
|
+
market_id: "ETH-CLP",
|
|
1854
|
+
amount: ["1", "ETH"],
|
|
1855
|
+
limit: ["3000000", "CLP"],
|
|
1856
|
+
base_balance_change: ["1", "ETH"],
|
|
1857
|
+
quote_balance_change: ["-3000000", "CLP"],
|
|
1858
|
+
fee_amount: ["15000", "CLP"],
|
|
1859
|
+
order_amount: ["1", "ETH"],
|
|
1860
|
+
},
|
|
1861
|
+
}),
|
|
1862
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
1863
|
+
);
|
|
1864
|
+
try {
|
|
1865
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1866
|
+
const result = await handleGetRealQuotation(
|
|
1867
|
+
{ market_id: "ETH-CLP", type: "Ask", amount: 1, limit: 3000000 },
|
|
1868
|
+
client,
|
|
1869
|
+
);
|
|
1870
|
+
assert(!result.isError, "should not be error");
|
|
1871
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
1872
|
+
id: null;
|
|
1873
|
+
limit: number;
|
|
1874
|
+
limit_currency: string;
|
|
1875
|
+
};
|
|
1876
|
+
assertEqual(parsed.id, null, "id should be null");
|
|
1877
|
+
assertEqual(parsed.limit, 3000000, "limit should be 3000000");
|
|
1878
|
+
assertEqual(parsed.limit_currency, "CLP", "limit_currency should be CLP");
|
|
1879
|
+
} finally {
|
|
1880
|
+
globalThis.fetch = savedFetch;
|
|
1881
|
+
}
|
|
1882
|
+
});
|
|
1883
|
+
|
|
1884
|
+
await test("handleGetRealQuotation: API 422 passthrough", async () => {
|
|
1885
|
+
const savedFetch = globalThis.fetch;
|
|
1886
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1887
|
+
new Response(JSON.stringify({ message: "Unprocessable Entity" }), { status: 422 });
|
|
1888
|
+
try {
|
|
1889
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1890
|
+
const result = await handleGetRealQuotation({ market_id: "BTC-CLP", type: "Bid", amount: 0.000001 }, client);
|
|
1891
|
+
assert(result.isError === true, "should be error");
|
|
1892
|
+
const parsed = JSON.parse(result.content[0].text) as { code: number };
|
|
1893
|
+
assertEqual(parsed.code, 422, "code should be 422");
|
|
1894
|
+
} finally {
|
|
1895
|
+
globalThis.fetch = savedFetch;
|
|
1896
|
+
}
|
|
1897
|
+
});
|
|
1898
|
+
|
|
1899
|
+
// ----------------------------------------------------------------
|
|
1900
|
+
// Priority 2 — create_receive_address
|
|
1901
|
+
// ----------------------------------------------------------------
|
|
1902
|
+
|
|
1903
|
+
section("create_receive_address");
|
|
1904
|
+
|
|
1905
|
+
await test("handleCreateReceiveAddress: INVALID_CURRENCY without fetch", async () => {
|
|
1906
|
+
const fetchCalled = { value: false };
|
|
1907
|
+
const savedFetch = globalThis.fetch;
|
|
1908
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
1909
|
+
fetchCalled.value = true;
|
|
1910
|
+
return new Response("{}", { status: 200 });
|
|
1911
|
+
};
|
|
1912
|
+
try {
|
|
1913
|
+
const client = {} as BudaClient;
|
|
1914
|
+
const result = await handleCreateReceiveAddress({ currency: "!!!!" }, client);
|
|
1915
|
+
assert(result.isError === true, "should be error");
|
|
1916
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
1917
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
1918
|
+
assertEqual(parsed.code, "INVALID_CURRENCY", "code should be INVALID_CURRENCY");
|
|
1919
|
+
} finally {
|
|
1920
|
+
globalThis.fetch = savedFetch;
|
|
1921
|
+
}
|
|
1922
|
+
});
|
|
1923
|
+
|
|
1924
|
+
await test("handleCreateReceiveAddress: happy path", async () => {
|
|
1925
|
+
const savedFetch = globalThis.fetch;
|
|
1926
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1927
|
+
new Response(
|
|
1928
|
+
JSON.stringify({
|
|
1929
|
+
receive_address: {
|
|
1930
|
+
id: 99,
|
|
1931
|
+
address: "bc1qnewaddress123",
|
|
1932
|
+
currency: "BTC",
|
|
1933
|
+
created_at: "2024-04-01T00:00:00Z",
|
|
1934
|
+
label: null,
|
|
1935
|
+
},
|
|
1936
|
+
}),
|
|
1937
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
1938
|
+
);
|
|
1939
|
+
try {
|
|
1940
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1941
|
+
const result = await handleCreateReceiveAddress({ currency: "BTC" }, client);
|
|
1942
|
+
assert(!result.isError, "should not be error");
|
|
1943
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
1944
|
+
id: number;
|
|
1945
|
+
address: string;
|
|
1946
|
+
currency: string;
|
|
1947
|
+
label: null;
|
|
1948
|
+
};
|
|
1949
|
+
assertEqual(parsed.id, 99, "id should be 99");
|
|
1950
|
+
assertEqual(parsed.address, "bc1qnewaddress123", "address should match");
|
|
1951
|
+
assertEqual(parsed.currency, "BTC", "currency should be BTC");
|
|
1952
|
+
assertEqual(parsed.label, null, "label should be null");
|
|
1953
|
+
} finally {
|
|
1954
|
+
globalThis.fetch = savedFetch;
|
|
1955
|
+
}
|
|
1956
|
+
});
|
|
1957
|
+
|
|
1958
|
+
await test("handleCreateReceiveAddress: fiat currency API error passthrough", async () => {
|
|
1959
|
+
const savedFetch = globalThis.fetch;
|
|
1960
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
1961
|
+
new Response(JSON.stringify({ message: "Not found" }), { status: 404 });
|
|
1962
|
+
try {
|
|
1963
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
1964
|
+
const result = await handleCreateReceiveAddress({ currency: "CLP" }, client);
|
|
1965
|
+
assert(result.isError === true, "should be error for fiat");
|
|
1966
|
+
const parsed = JSON.parse(result.content[0].text) as { code: number };
|
|
1967
|
+
assertEqual(parsed.code, 404, "code should be 404");
|
|
1968
|
+
} finally {
|
|
1969
|
+
globalThis.fetch = savedFetch;
|
|
1970
|
+
}
|
|
1971
|
+
});
|
|
1972
|
+
|
|
1973
|
+
// ----------------------------------------------------------------
|
|
1974
|
+
// Priority 2 — quote_remittance
|
|
1975
|
+
// ----------------------------------------------------------------
|
|
1976
|
+
|
|
1977
|
+
section("quote_remittance");
|
|
1978
|
+
|
|
1979
|
+
await test("handleQuoteRemittance: INVALID_CURRENCY without fetch", async () => {
|
|
1980
|
+
const fetchCalled = { value: false };
|
|
1981
|
+
const savedFetch = globalThis.fetch;
|
|
1982
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
1983
|
+
fetchCalled.value = true;
|
|
1984
|
+
return new Response("{}", { status: 200 });
|
|
1985
|
+
};
|
|
1986
|
+
try {
|
|
1987
|
+
const client = {} as BudaClient;
|
|
1988
|
+
const result = await handleQuoteRemittance({ currency: "!!!", amount: 100, recipient_id: 1 }, client);
|
|
1989
|
+
assert(result.isError === true, "should be error");
|
|
1990
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
1991
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
1992
|
+
assertEqual(parsed.code, "INVALID_CURRENCY", "code should be INVALID_CURRENCY");
|
|
1993
|
+
} finally {
|
|
1994
|
+
globalThis.fetch = savedFetch;
|
|
1995
|
+
}
|
|
1996
|
+
});
|
|
1997
|
+
|
|
1998
|
+
await test("handleQuoteRemittance: happy path", async () => {
|
|
1999
|
+
const savedFetch = globalThis.fetch;
|
|
2000
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2001
|
+
new Response(
|
|
2002
|
+
JSON.stringify({
|
|
2003
|
+
remittance: {
|
|
2004
|
+
id: 55,
|
|
2005
|
+
state: "quoted",
|
|
2006
|
+
currency: "CLP",
|
|
2007
|
+
amount: ["100000", "CLP"],
|
|
2008
|
+
recipient_id: 5,
|
|
2009
|
+
created_at: "2024-04-01T00:00:00Z",
|
|
2010
|
+
expires_at: "2024-04-01T00:30:00Z",
|
|
2011
|
+
},
|
|
2012
|
+
}),
|
|
2013
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
2014
|
+
);
|
|
2015
|
+
try {
|
|
2016
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2017
|
+
const result = await handleQuoteRemittance({ currency: "CLP", amount: 100000, recipient_id: 5 }, client);
|
|
2018
|
+
assert(!result.isError, "should not be error");
|
|
2019
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
2020
|
+
id: number;
|
|
2021
|
+
state: string;
|
|
2022
|
+
expires_at: string;
|
|
2023
|
+
};
|
|
2024
|
+
assertEqual(parsed.id, 55, "id should be 55");
|
|
2025
|
+
assertEqual(parsed.state, "quoted", "state should be quoted");
|
|
2026
|
+
assert(parsed.expires_at !== null, "expires_at should not be null");
|
|
2027
|
+
} finally {
|
|
2028
|
+
globalThis.fetch = savedFetch;
|
|
2029
|
+
}
|
|
2030
|
+
});
|
|
2031
|
+
|
|
2032
|
+
await test("handleQuoteRemittance: 404 unknown recipient passthrough", async () => {
|
|
2033
|
+
const savedFetch = globalThis.fetch;
|
|
2034
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2035
|
+
new Response(JSON.stringify({ message: "Not found" }), { status: 404 });
|
|
2036
|
+
try {
|
|
2037
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2038
|
+
const result = await handleQuoteRemittance({ currency: "CLP", amount: 100000, recipient_id: 9999 }, client);
|
|
2039
|
+
assert(result.isError === true, "should be error");
|
|
2040
|
+
const parsed = JSON.parse(result.content[0].text) as { code: number };
|
|
2041
|
+
assertEqual(parsed.code, 404, "code should be 404");
|
|
2042
|
+
} finally {
|
|
2043
|
+
globalThis.fetch = savedFetch;
|
|
2044
|
+
}
|
|
2045
|
+
});
|
|
2046
|
+
|
|
2047
|
+
// ----------------------------------------------------------------
|
|
2048
|
+
// Priority 2 — accept_remittance_quote
|
|
2049
|
+
// ----------------------------------------------------------------
|
|
2050
|
+
|
|
2051
|
+
section("accept_remittance_quote");
|
|
2052
|
+
|
|
2053
|
+
await test("handleAcceptRemittanceQuote: missing/wrong token returns CONFIRMATION_REQUIRED without fetch", async () => {
|
|
2054
|
+
const fetchCalled = { value: false };
|
|
2055
|
+
const savedFetch = globalThis.fetch;
|
|
2056
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2057
|
+
fetchCalled.value = true;
|
|
2058
|
+
return new Response("{}", { status: 200 });
|
|
2059
|
+
};
|
|
2060
|
+
try {
|
|
2061
|
+
const client = {} as BudaClient;
|
|
2062
|
+
const result = await handleAcceptRemittanceQuote({ id: 77, confirmation_token: "yes" }, client);
|
|
2063
|
+
assert(result.isError === true, "should be error");
|
|
2064
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2065
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string; remittance_id: number };
|
|
2066
|
+
assertEqual(parsed.code, "CONFIRMATION_REQUIRED", "code should be CONFIRMATION_REQUIRED");
|
|
2067
|
+
assertEqual(parsed.remittance_id, 77, "remittance_id should be 77");
|
|
2068
|
+
} finally {
|
|
2069
|
+
globalThis.fetch = savedFetch;
|
|
2070
|
+
}
|
|
2071
|
+
});
|
|
2072
|
+
|
|
2073
|
+
await test("handleAcceptRemittanceQuote: empty string token returns CONFIRMATION_REQUIRED without fetch", async () => {
|
|
2074
|
+
const fetchCalled = { value: false };
|
|
2075
|
+
const savedFetch = globalThis.fetch;
|
|
2076
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2077
|
+
fetchCalled.value = true;
|
|
2078
|
+
return new Response("{}", { status: 200 });
|
|
2079
|
+
};
|
|
2080
|
+
try {
|
|
2081
|
+
const client = {} as BudaClient;
|
|
2082
|
+
const result = await handleAcceptRemittanceQuote({ id: 10, confirmation_token: "" }, client);
|
|
2083
|
+
assert(result.isError === true, "should be error");
|
|
2084
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2085
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
2086
|
+
assertEqual(parsed.code, "CONFIRMATION_REQUIRED", "code should be CONFIRMATION_REQUIRED");
|
|
2087
|
+
} finally {
|
|
2088
|
+
globalThis.fetch = savedFetch;
|
|
2089
|
+
}
|
|
2090
|
+
});
|
|
2091
|
+
|
|
2092
|
+
await test("handleAcceptRemittanceQuote: happy path with CONFIRM token", async () => {
|
|
2093
|
+
const savedFetch = globalThis.fetch;
|
|
2094
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2095
|
+
new Response(
|
|
2096
|
+
JSON.stringify({
|
|
2097
|
+
remittance: {
|
|
2098
|
+
id: 77,
|
|
2099
|
+
state: "accepted",
|
|
2100
|
+
currency: "CLP",
|
|
2101
|
+
amount: ["100000", "CLP"],
|
|
2102
|
+
recipient_id: 5,
|
|
2103
|
+
created_at: "2024-04-01T00:00:00Z",
|
|
2104
|
+
expires_at: null,
|
|
2105
|
+
},
|
|
2106
|
+
}),
|
|
2107
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
2108
|
+
);
|
|
2109
|
+
try {
|
|
2110
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2111
|
+
const result = await handleAcceptRemittanceQuote({ id: 77, confirmation_token: "CONFIRM" }, client);
|
|
2112
|
+
assert(!result.isError, "should not be error");
|
|
2113
|
+
const parsed = JSON.parse(result.content[0].text) as { id: number; state: string };
|
|
2114
|
+
assertEqual(parsed.id, 77, "id should be 77");
|
|
2115
|
+
assertEqual(parsed.state, "accepted", "state should be accepted");
|
|
2116
|
+
} finally {
|
|
2117
|
+
globalThis.fetch = savedFetch;
|
|
2118
|
+
}
|
|
2119
|
+
});
|
|
2120
|
+
|
|
2121
|
+
await test("handleAcceptRemittanceQuote: 422 expired quote passthrough", async () => {
|
|
2122
|
+
const savedFetch = globalThis.fetch;
|
|
2123
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2124
|
+
new Response(JSON.stringify({ message: "Unprocessable Entity" }), { status: 422 });
|
|
2125
|
+
try {
|
|
2126
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2127
|
+
const result = await handleAcceptRemittanceQuote({ id: 77, confirmation_token: "CONFIRM" }, client);
|
|
2128
|
+
assert(result.isError === true, "should be error");
|
|
2129
|
+
const parsed = JSON.parse(result.content[0].text) as { code: number };
|
|
2130
|
+
assertEqual(parsed.code, 422, "code should be 422");
|
|
2131
|
+
} finally {
|
|
2132
|
+
globalThis.fetch = savedFetch;
|
|
2133
|
+
}
|
|
2134
|
+
});
|
|
2135
|
+
|
|
2136
|
+
// ----------------------------------------------------------------
|
|
2137
|
+
// Priority 3 — cancel_all_orders
|
|
2138
|
+
// ----------------------------------------------------------------
|
|
2139
|
+
|
|
2140
|
+
section("cancel_all_orders");
|
|
2141
|
+
|
|
2142
|
+
await test("handleCancelAllOrders: missing/wrong token returns CONFIRMATION_REQUIRED without fetch", async () => {
|
|
2143
|
+
const fetchCalled = { value: false };
|
|
2144
|
+
const savedFetch = globalThis.fetch;
|
|
2145
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2146
|
+
fetchCalled.value = true;
|
|
2147
|
+
return new Response("{}", { status: 200 });
|
|
2148
|
+
};
|
|
2149
|
+
try {
|
|
2150
|
+
const client = {} as BudaClient;
|
|
2151
|
+
const result = await handleCancelAllOrders({ market_id: "BTC-CLP", confirmation_token: "NOPE" }, client);
|
|
2152
|
+
assert(result.isError === true, "should be error");
|
|
2153
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2154
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
2155
|
+
assertEqual(parsed.code, "CONFIRMATION_REQUIRED", "code should be CONFIRMATION_REQUIRED");
|
|
2156
|
+
} finally {
|
|
2157
|
+
globalThis.fetch = savedFetch;
|
|
2158
|
+
}
|
|
2159
|
+
});
|
|
2160
|
+
|
|
2161
|
+
await test("handleCancelAllOrders: '*' + CONFIRM cancels all markets", async () => {
|
|
2162
|
+
const savedFetch = globalThis.fetch;
|
|
2163
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2164
|
+
new Response(
|
|
2165
|
+
JSON.stringify({ canceled_count: 5 }),
|
|
2166
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
2167
|
+
);
|
|
2168
|
+
try {
|
|
2169
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2170
|
+
const result = await handleCancelAllOrders({ market_id: "*", confirmation_token: "CONFIRM" }, client);
|
|
2171
|
+
assert(!result.isError, "should not be error");
|
|
2172
|
+
const parsed = JSON.parse(result.content[0].text) as { canceled_count: number; market_id: string };
|
|
2173
|
+
assertEqual(parsed.canceled_count, 5, "canceled_count should be 5");
|
|
2174
|
+
assertEqual(parsed.market_id, "*", "market_id should be *");
|
|
2175
|
+
} finally {
|
|
2176
|
+
globalThis.fetch = savedFetch;
|
|
2177
|
+
}
|
|
2178
|
+
});
|
|
2179
|
+
|
|
2180
|
+
await test("handleCancelAllOrders: specific market + CONFIRM", async () => {
|
|
2181
|
+
const savedFetch = globalThis.fetch;
|
|
2182
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2183
|
+
new Response(
|
|
2184
|
+
JSON.stringify({ canceled_count: 2 }),
|
|
2185
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
2186
|
+
);
|
|
2187
|
+
try {
|
|
2188
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2189
|
+
const result = await handleCancelAllOrders({ market_id: "BTC-CLP", confirmation_token: "CONFIRM" }, client);
|
|
2190
|
+
assert(!result.isError, "should not be error");
|
|
2191
|
+
const parsed = JSON.parse(result.content[0].text) as { canceled_count: number; market_id: string };
|
|
2192
|
+
assertEqual(parsed.canceled_count, 2, "canceled_count should be 2");
|
|
2193
|
+
assertEqual(parsed.market_id, "BTC-CLP", "market_id should be BTC-CLP");
|
|
2194
|
+
} finally {
|
|
2195
|
+
globalThis.fetch = savedFetch;
|
|
2196
|
+
}
|
|
2197
|
+
});
|
|
2198
|
+
|
|
2199
|
+
await test("handleCancelAllOrders: invalid market format returns INVALID_MARKET_ID even with CONFIRM", async () => {
|
|
2200
|
+
const fetchCalled = { value: false };
|
|
2201
|
+
const savedFetch = globalThis.fetch;
|
|
2202
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2203
|
+
fetchCalled.value = true;
|
|
2204
|
+
return new Response("{}", { status: 200 });
|
|
2205
|
+
};
|
|
2206
|
+
try {
|
|
2207
|
+
const client = {} as BudaClient;
|
|
2208
|
+
const result = await handleCancelAllOrders({ market_id: "INVALID!!!", confirmation_token: "CONFIRM" }, client);
|
|
2209
|
+
assert(result.isError === true, "should be error");
|
|
2210
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2211
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
2212
|
+
assertEqual(parsed.code, "INVALID_MARKET_ID", "code should be INVALID_MARKET_ID");
|
|
2213
|
+
} finally {
|
|
2214
|
+
globalThis.fetch = savedFetch;
|
|
2215
|
+
}
|
|
2216
|
+
});
|
|
2217
|
+
|
|
2218
|
+
await test("handleCancelAllOrders: API 404 passthrough", async () => {
|
|
2219
|
+
const savedFetch = globalThis.fetch;
|
|
2220
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2221
|
+
new Response(JSON.stringify({ message: "Not found" }), { status: 404 });
|
|
2222
|
+
try {
|
|
2223
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2224
|
+
const result = await handleCancelAllOrders({ market_id: "BTC-CLP", confirmation_token: "CONFIRM" }, client);
|
|
2225
|
+
assert(result.isError === true, "should be error");
|
|
2226
|
+
const parsed = JSON.parse(result.content[0].text) as { code: number };
|
|
2227
|
+
assertEqual(parsed.code, 404, "code should be 404");
|
|
2228
|
+
} finally {
|
|
2229
|
+
globalThis.fetch = savedFetch;
|
|
2230
|
+
}
|
|
2231
|
+
});
|
|
2232
|
+
|
|
2233
|
+
// ----------------------------------------------------------------
|
|
2234
|
+
// Priority 3 — cancel_order_by_client_id
|
|
2235
|
+
// ----------------------------------------------------------------
|
|
2236
|
+
|
|
2237
|
+
section("cancel_order_by_client_id");
|
|
2238
|
+
|
|
2239
|
+
await test("handleCancelOrderByClientId: missing/wrong token returns CONFIRMATION_REQUIRED without fetch", async () => {
|
|
2240
|
+
const fetchCalled = { value: false };
|
|
2241
|
+
const savedFetch = globalThis.fetch;
|
|
2242
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2243
|
+
fetchCalled.value = true;
|
|
2244
|
+
return new Response("{}", { status: 200 });
|
|
2245
|
+
};
|
|
2246
|
+
try {
|
|
2247
|
+
const client = {} as BudaClient;
|
|
2248
|
+
const result = await handleCancelOrderByClientId({ client_id: "my-order-1", confirmation_token: "NOPE" }, client);
|
|
2249
|
+
assert(result.isError === true, "should be error");
|
|
2250
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2251
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string; client_id: string };
|
|
2252
|
+
assertEqual(parsed.code, "CONFIRMATION_REQUIRED", "code should be CONFIRMATION_REQUIRED");
|
|
2253
|
+
assertEqual(parsed.client_id, "my-order-1", "client_id should be echoed back");
|
|
2254
|
+
} finally {
|
|
2255
|
+
globalThis.fetch = savedFetch;
|
|
2256
|
+
}
|
|
2257
|
+
});
|
|
2258
|
+
|
|
2259
|
+
await test("handleCancelOrderByClientId: happy path returns flat order", async () => {
|
|
2260
|
+
const savedFetch = globalThis.fetch;
|
|
2261
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2262
|
+
new Response(
|
|
2263
|
+
JSON.stringify({
|
|
2264
|
+
order: {
|
|
2265
|
+
id: 999,
|
|
2266
|
+
type: "Bid",
|
|
2267
|
+
state: "canceling",
|
|
2268
|
+
created_at: "2024-01-01T00:00:00Z",
|
|
2269
|
+
market_id: "BTC-CLP",
|
|
2270
|
+
fee_currency: "CLP",
|
|
2271
|
+
price_type: "limit",
|
|
2272
|
+
order_type: "limit",
|
|
2273
|
+
client_id: "my-order-1",
|
|
2274
|
+
limit: ["50000000", "CLP"],
|
|
2275
|
+
amount: ["0.001", "BTC"],
|
|
2276
|
+
original_amount: ["0.001", "BTC"],
|
|
2277
|
+
traded_amount: ["0", "BTC"],
|
|
2278
|
+
total_exchanged: ["0", "CLP"],
|
|
2279
|
+
paid_fee: ["0", "CLP"],
|
|
2280
|
+
},
|
|
2281
|
+
}),
|
|
2282
|
+
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
2283
|
+
);
|
|
2284
|
+
try {
|
|
2285
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2286
|
+
const result = await handleCancelOrderByClientId({ client_id: "my-order-1", confirmation_token: "CONFIRM" }, client);
|
|
2287
|
+
assert(!result.isError, "should not be error");
|
|
2288
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
2289
|
+
id: number;
|
|
2290
|
+
state: string;
|
|
2291
|
+
client_id: string;
|
|
2292
|
+
limit_price: number;
|
|
2293
|
+
};
|
|
2294
|
+
assertEqual(parsed.id, 999, "id should be 999");
|
|
2295
|
+
assertEqual(parsed.state, "canceling", "state should be canceling");
|
|
2296
|
+
assertEqual(parsed.client_id, "my-order-1", "client_id should be my-order-1");
|
|
2297
|
+
assertEqual(parsed.limit_price, 50000000, "limit_price should be flattened");
|
|
2298
|
+
} finally {
|
|
2299
|
+
globalThis.fetch = savedFetch;
|
|
2300
|
+
}
|
|
2301
|
+
});
|
|
2302
|
+
|
|
2303
|
+
await test("handleCancelOrderByClientId: 404 passthrough", async () => {
|
|
2304
|
+
const savedFetch = globalThis.fetch;
|
|
2305
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2306
|
+
new Response(JSON.stringify({ message: "Not found" }), { status: 404 });
|
|
2307
|
+
try {
|
|
2308
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2309
|
+
const result = await handleCancelOrderByClientId({ client_id: "ghost-order", confirmation_token: "CONFIRM" }, client);
|
|
2310
|
+
assert(result.isError === true, "should be error");
|
|
2311
|
+
const parsed = JSON.parse(result.content[0].text) as { code: number };
|
|
2312
|
+
assertEqual(parsed.code, 404, "code should be 404");
|
|
2313
|
+
} finally {
|
|
2314
|
+
globalThis.fetch = savedFetch;
|
|
2315
|
+
}
|
|
2316
|
+
});
|
|
2317
|
+
|
|
2318
|
+
// ----------------------------------------------------------------
|
|
2319
|
+
// Priority 3 — place_batch_orders
|
|
2320
|
+
// ----------------------------------------------------------------
|
|
2321
|
+
|
|
2322
|
+
section("place_batch_orders");
|
|
2323
|
+
|
|
2324
|
+
await test("handlePlaceBatchOrders: missing/wrong token returns CONFIRMATION_REQUIRED without fetch", async () => {
|
|
2325
|
+
const fetchCalled = { value: false };
|
|
2326
|
+
const savedFetch = globalThis.fetch;
|
|
2327
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2328
|
+
fetchCalled.value = true;
|
|
2329
|
+
return new Response("{}", { status: 200 });
|
|
2330
|
+
};
|
|
2331
|
+
try {
|
|
2332
|
+
const client = {} as BudaClient;
|
|
2333
|
+
const result = await handlePlaceBatchOrders(
|
|
2334
|
+
{
|
|
2335
|
+
orders: [{ market_id: "BTC-CLP", type: "Bid", price_type: "limit", amount: 0.001, limit_price: 50000000 }],
|
|
2336
|
+
confirmation_token: "NOPE",
|
|
2337
|
+
},
|
|
2338
|
+
client,
|
|
2339
|
+
);
|
|
2340
|
+
assert(result.isError === true, "should be error");
|
|
2341
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2342
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
2343
|
+
assertEqual(parsed.code, "CONFIRMATION_REQUIRED", "code should be CONFIRMATION_REQUIRED");
|
|
2344
|
+
} finally {
|
|
2345
|
+
globalThis.fetch = savedFetch;
|
|
2346
|
+
}
|
|
2347
|
+
});
|
|
2348
|
+
|
|
2349
|
+
await test("handlePlaceBatchOrders: all valid + CONFIRM places all orders", async () => {
|
|
2350
|
+
let callCount = 0;
|
|
2351
|
+
const savedFetch = globalThis.fetch;
|
|
2352
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2353
|
+
callCount++;
|
|
2354
|
+
return new Response(
|
|
2355
|
+
JSON.stringify({
|
|
2356
|
+
order: {
|
|
2357
|
+
id: callCount * 100,
|
|
2358
|
+
type: "Bid",
|
|
2359
|
+
state: "pending",
|
|
2360
|
+
created_at: "2024-01-01T00:00:00Z",
|
|
2361
|
+
market_id: "BTC-CLP",
|
|
2362
|
+
fee_currency: "CLP",
|
|
2363
|
+
price_type: "limit",
|
|
2364
|
+
order_type: "limit",
|
|
2365
|
+
client_id: null,
|
|
2366
|
+
limit: ["50000000", "CLP"],
|
|
2367
|
+
amount: ["0.001", "BTC"],
|
|
2368
|
+
original_amount: ["0.001", "BTC"],
|
|
2369
|
+
traded_amount: ["0", "BTC"],
|
|
2370
|
+
total_exchanged: ["0", "CLP"],
|
|
2371
|
+
paid_fee: ["0", "CLP"],
|
|
2372
|
+
},
|
|
2373
|
+
}),
|
|
2374
|
+
{ status: 201, headers: { "Content-Type": "application/json" } },
|
|
2375
|
+
);
|
|
2376
|
+
};
|
|
2377
|
+
try {
|
|
2378
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2379
|
+
const result = await handlePlaceBatchOrders(
|
|
2380
|
+
{
|
|
2381
|
+
orders: [
|
|
2382
|
+
{ market_id: "BTC-CLP", type: "Bid", price_type: "limit", amount: 0.001, limit_price: 50000000 },
|
|
2383
|
+
{ market_id: "ETH-CLP", type: "Ask", price_type: "limit", amount: 0.1, limit_price: 3000000 },
|
|
2384
|
+
],
|
|
2385
|
+
confirmation_token: "CONFIRM",
|
|
2386
|
+
},
|
|
2387
|
+
client,
|
|
2388
|
+
);
|
|
2389
|
+
assert(!result.isError, "should not be error");
|
|
2390
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
2391
|
+
total: number;
|
|
2392
|
+
succeeded: number;
|
|
2393
|
+
failed: number;
|
|
2394
|
+
results: Array<{ success: boolean }>;
|
|
2395
|
+
};
|
|
2396
|
+
assertEqual(parsed.total, 2, "total should be 2");
|
|
2397
|
+
assertEqual(parsed.succeeded, 2, "succeeded should be 2");
|
|
2398
|
+
assertEqual(parsed.failed, 0, "failed should be 0");
|
|
2399
|
+
assertEqual(callCount, 2, "should have made 2 API calls");
|
|
2400
|
+
} finally {
|
|
2401
|
+
globalThis.fetch = savedFetch;
|
|
2402
|
+
}
|
|
2403
|
+
});
|
|
2404
|
+
|
|
2405
|
+
await test("handlePlaceBatchOrders: one invalid market → pre-validation error, zero API calls", async () => {
|
|
2406
|
+
const fetchCalled = { value: false };
|
|
2407
|
+
const savedFetch = globalThis.fetch;
|
|
2408
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2409
|
+
fetchCalled.value = true;
|
|
2410
|
+
return new Response("{}", { status: 200 });
|
|
2411
|
+
};
|
|
2412
|
+
try {
|
|
2413
|
+
const client = {} as BudaClient;
|
|
2414
|
+
const result = await handlePlaceBatchOrders(
|
|
2415
|
+
{
|
|
2416
|
+
orders: [
|
|
2417
|
+
{ market_id: "BTC-CLP", type: "Bid", price_type: "limit", amount: 0.001, limit_price: 50000000 },
|
|
2418
|
+
{ market_id: "INVALID!!!", type: "Ask", price_type: "limit", amount: 0.1, limit_price: 3000000 },
|
|
2419
|
+
],
|
|
2420
|
+
confirmation_token: "CONFIRM",
|
|
2421
|
+
},
|
|
2422
|
+
client,
|
|
2423
|
+
);
|
|
2424
|
+
assert(result.isError === true, "should be error");
|
|
2425
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2426
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string; index: number };
|
|
2427
|
+
assertEqual(parsed.code, "INVALID_MARKET_ID", "code should be INVALID_MARKET_ID");
|
|
2428
|
+
assertEqual(parsed.index, 1, "index should be 1");
|
|
2429
|
+
} finally {
|
|
2430
|
+
globalThis.fetch = savedFetch;
|
|
2431
|
+
}
|
|
2432
|
+
});
|
|
2433
|
+
|
|
2434
|
+
await test("handlePlaceBatchOrders: missing limit_price on limit order → pre-validation error", async () => {
|
|
2435
|
+
const fetchCalled = { value: false };
|
|
2436
|
+
const savedFetch = globalThis.fetch;
|
|
2437
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2438
|
+
fetchCalled.value = true;
|
|
2439
|
+
return new Response("{}", { status: 200 });
|
|
2440
|
+
};
|
|
2441
|
+
try {
|
|
2442
|
+
const client = {} as BudaClient;
|
|
2443
|
+
const result = await handlePlaceBatchOrders(
|
|
2444
|
+
{
|
|
2445
|
+
orders: [{ market_id: "BTC-CLP", type: "Bid", price_type: "limit", amount: 0.001 }],
|
|
2446
|
+
confirmation_token: "CONFIRM",
|
|
2447
|
+
},
|
|
2448
|
+
client,
|
|
2449
|
+
);
|
|
2450
|
+
assert(result.isError === true, "should be error");
|
|
2451
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2452
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
2453
|
+
assertEqual(parsed.code, "VALIDATION_ERROR", "code should be VALIDATION_ERROR");
|
|
2454
|
+
} finally {
|
|
2455
|
+
globalThis.fetch = savedFetch;
|
|
2456
|
+
}
|
|
2457
|
+
});
|
|
2458
|
+
|
|
2459
|
+
await test("handlePlaceBatchOrders: one mid-batch API error → partial success report", async () => {
|
|
2460
|
+
let callCount = 0;
|
|
2461
|
+
const savedFetch = globalThis.fetch;
|
|
2462
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2463
|
+
callCount++;
|
|
2464
|
+
if (callCount === 2) {
|
|
2465
|
+
return new Response(JSON.stringify({ message: "Insufficient balance" }), { status: 422 });
|
|
2466
|
+
}
|
|
2467
|
+
return new Response(
|
|
2468
|
+
JSON.stringify({
|
|
2469
|
+
order: {
|
|
2470
|
+
id: 100,
|
|
2471
|
+
type: "Bid",
|
|
2472
|
+
state: "pending",
|
|
2473
|
+
created_at: "2024-01-01T00:00:00Z",
|
|
2474
|
+
market_id: "BTC-CLP",
|
|
2475
|
+
fee_currency: "CLP",
|
|
2476
|
+
price_type: "limit",
|
|
2477
|
+
order_type: "limit",
|
|
2478
|
+
client_id: null,
|
|
2479
|
+
limit: ["50000000", "CLP"],
|
|
2480
|
+
amount: ["0.001", "BTC"],
|
|
2481
|
+
original_amount: ["0.001", "BTC"],
|
|
2482
|
+
traded_amount: ["0", "BTC"],
|
|
2483
|
+
total_exchanged: ["0", "CLP"],
|
|
2484
|
+
paid_fee: ["0", "CLP"],
|
|
2485
|
+
},
|
|
2486
|
+
}),
|
|
2487
|
+
{ status: 201, headers: { "Content-Type": "application/json" } },
|
|
2488
|
+
);
|
|
2489
|
+
};
|
|
2490
|
+
try {
|
|
2491
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2492
|
+
const result = await handlePlaceBatchOrders(
|
|
2493
|
+
{
|
|
2494
|
+
orders: [
|
|
2495
|
+
{ market_id: "BTC-CLP", type: "Bid", price_type: "limit", amount: 0.001, limit_price: 50000000 },
|
|
2496
|
+
{ market_id: "ETH-CLP", type: "Ask", price_type: "limit", amount: 0.1, limit_price: 3000000 },
|
|
2497
|
+
],
|
|
2498
|
+
confirmation_token: "CONFIRM",
|
|
2499
|
+
},
|
|
2500
|
+
client,
|
|
2501
|
+
);
|
|
2502
|
+
assert(!result.isError, "partial success should not set isError");
|
|
2503
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
2504
|
+
total: number;
|
|
2505
|
+
succeeded: number;
|
|
2506
|
+
failed: number;
|
|
2507
|
+
warning?: string;
|
|
2508
|
+
};
|
|
2509
|
+
assertEqual(parsed.total, 2, "total should be 2");
|
|
2510
|
+
assertEqual(parsed.succeeded, 1, "succeeded should be 1");
|
|
2511
|
+
assertEqual(parsed.failed, 1, "failed should be 1");
|
|
2512
|
+
assert(parsed.warning !== undefined, "warning should be present for partial failure");
|
|
2513
|
+
} finally {
|
|
2514
|
+
globalThis.fetch = savedFetch;
|
|
2515
|
+
}
|
|
2516
|
+
});
|
|
2517
|
+
|
|
2518
|
+
// ----------------------------------------------------------------
|
|
2519
|
+
// Priority 3 — place_order extended (3.22)
|
|
2520
|
+
// ----------------------------------------------------------------
|
|
2521
|
+
|
|
2522
|
+
section("place_order extended (TIF + stop)");
|
|
2523
|
+
|
|
2524
|
+
await test("handlePlaceOrder: existing limit order unchanged (no regression)", async () => {
|
|
2525
|
+
const savedFetch = globalThis.fetch;
|
|
2526
|
+
let capturedBody: Record<string, unknown> = {};
|
|
2527
|
+
globalThis.fetch = async (_url: string | URL | Request, init?: RequestInit): Promise<Response> => {
|
|
2528
|
+
capturedBody = JSON.parse(init?.body as string) as Record<string, unknown>;
|
|
2529
|
+
return new Response(
|
|
2530
|
+
JSON.stringify({
|
|
2531
|
+
order: {
|
|
2532
|
+
id: 1,
|
|
2533
|
+
type: "Bid",
|
|
2534
|
+
state: "pending",
|
|
2535
|
+
created_at: "2024-01-01T00:00:00Z",
|
|
2536
|
+
market_id: "BTC-CLP",
|
|
2537
|
+
fee_currency: "CLP",
|
|
2538
|
+
price_type: "limit",
|
|
2539
|
+
order_type: "limit",
|
|
2540
|
+
client_id: null,
|
|
2541
|
+
limit: ["50000000", "CLP"],
|
|
2542
|
+
amount: ["0.001", "BTC"],
|
|
2543
|
+
original_amount: ["0.001", "BTC"],
|
|
2544
|
+
traded_amount: ["0", "BTC"],
|
|
2545
|
+
total_exchanged: ["0", "CLP"],
|
|
2546
|
+
paid_fee: ["0", "CLP"],
|
|
2547
|
+
},
|
|
2548
|
+
}),
|
|
2549
|
+
{ status: 201, headers: { "Content-Type": "application/json" } },
|
|
2550
|
+
);
|
|
2551
|
+
};
|
|
2552
|
+
try {
|
|
2553
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2554
|
+
const result = await handlePlaceOrder(
|
|
2555
|
+
{ market_id: "BTC-CLP", type: "Bid", price_type: "limit", amount: 0.001, limit_price: 50000000, confirmation_token: "CONFIRM" },
|
|
2556
|
+
client,
|
|
2557
|
+
);
|
|
2558
|
+
assert(!result.isError, "should not be error");
|
|
2559
|
+
const limit = capturedBody.limit as Record<string, unknown>;
|
|
2560
|
+
assertEqual(limit.type as string, "gtc", "default limit type should be gtc");
|
|
2561
|
+
} finally {
|
|
2562
|
+
globalThis.fetch = savedFetch;
|
|
2563
|
+
}
|
|
2564
|
+
});
|
|
2565
|
+
|
|
2566
|
+
await test("handlePlaceOrder: IOC flag → limit.type='ioc'", async () => {
|
|
2567
|
+
const savedFetch = globalThis.fetch;
|
|
2568
|
+
let capturedBody: Record<string, unknown> = {};
|
|
2569
|
+
globalThis.fetch = async (_url: string | URL | Request, init?: RequestInit): Promise<Response> => {
|
|
2570
|
+
capturedBody = JSON.parse(init?.body as string) as Record<string, unknown>;
|
|
2571
|
+
return new Response(
|
|
2572
|
+
JSON.stringify({
|
|
2573
|
+
order: {
|
|
2574
|
+
id: 2,
|
|
2575
|
+
type: "Ask",
|
|
2576
|
+
state: "traded",
|
|
2577
|
+
created_at: "2024-01-01T00:00:00Z",
|
|
2578
|
+
market_id: "BTC-CLP",
|
|
2579
|
+
fee_currency: "CLP",
|
|
2580
|
+
price_type: "limit",
|
|
2581
|
+
order_type: "limit",
|
|
2582
|
+
client_id: null,
|
|
2583
|
+
limit: ["50000000", "CLP"],
|
|
2584
|
+
amount: ["0.001", "BTC"],
|
|
2585
|
+
original_amount: ["0.001", "BTC"],
|
|
2586
|
+
traded_amount: ["0.001", "BTC"],
|
|
2587
|
+
total_exchanged: ["50000", "CLP"],
|
|
2588
|
+
paid_fee: ["250", "CLP"],
|
|
2589
|
+
},
|
|
2590
|
+
}),
|
|
2591
|
+
{ status: 201, headers: { "Content-Type": "application/json" } },
|
|
2592
|
+
);
|
|
2593
|
+
};
|
|
2594
|
+
try {
|
|
2595
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2596
|
+
const result = await handlePlaceOrder(
|
|
2597
|
+
{ market_id: "BTC-CLP", type: "Ask", price_type: "limit", amount: 0.001, limit_price: 50000000, ioc: true, confirmation_token: "CONFIRM" },
|
|
2598
|
+
client,
|
|
2599
|
+
);
|
|
2600
|
+
assert(!result.isError, "should not be error");
|
|
2601
|
+
const limit = capturedBody.limit as Record<string, unknown>;
|
|
2602
|
+
assertEqual(limit.type as string, "ioc", "limit type should be ioc");
|
|
2603
|
+
} finally {
|
|
2604
|
+
globalThis.fetch = savedFetch;
|
|
2605
|
+
}
|
|
2606
|
+
});
|
|
2607
|
+
|
|
2608
|
+
await test("handlePlaceOrder: stop_price without stop_type → VALIDATION_ERROR", async () => {
|
|
2609
|
+
const fetchCalled = { value: false };
|
|
2610
|
+
const savedFetch = globalThis.fetch;
|
|
2611
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2612
|
+
fetchCalled.value = true;
|
|
2613
|
+
return new Response("{}", { status: 200 });
|
|
2614
|
+
};
|
|
2615
|
+
try {
|
|
2616
|
+
const client = {} as BudaClient;
|
|
2617
|
+
const result = await handlePlaceOrder(
|
|
2618
|
+
{ market_id: "BTC-CLP", type: "Bid", price_type: "limit", amount: 0.001, limit_price: 50000000, stop_price: 45000000, confirmation_token: "CONFIRM" },
|
|
2619
|
+
client,
|
|
2620
|
+
);
|
|
2621
|
+
assert(result.isError === true, "should be error");
|
|
2622
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2623
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
2624
|
+
assertEqual(parsed.code, "VALIDATION_ERROR", "code should be VALIDATION_ERROR");
|
|
2625
|
+
} finally {
|
|
2626
|
+
globalThis.fetch = savedFetch;
|
|
2627
|
+
}
|
|
2628
|
+
});
|
|
2629
|
+
|
|
2630
|
+
await test("handlePlaceOrder: mutually exclusive TIF flags → VALIDATION_ERROR", async () => {
|
|
2631
|
+
const fetchCalled = { value: false };
|
|
2632
|
+
const savedFetch = globalThis.fetch;
|
|
2633
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2634
|
+
fetchCalled.value = true;
|
|
2635
|
+
return new Response("{}", { status: 200 });
|
|
2636
|
+
};
|
|
2637
|
+
try {
|
|
2638
|
+
const client = {} as BudaClient;
|
|
2639
|
+
const result = await handlePlaceOrder(
|
|
2640
|
+
{ market_id: "BTC-CLP", type: "Bid", price_type: "limit", amount: 0.001, limit_price: 50000000, ioc: true, fok: true, confirmation_token: "CONFIRM" },
|
|
2641
|
+
client,
|
|
2642
|
+
);
|
|
2643
|
+
assert(result.isError === true, "should be error");
|
|
2644
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2645
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
2646
|
+
assertEqual(parsed.code, "VALIDATION_ERROR", "code should be VALIDATION_ERROR");
|
|
2647
|
+
} finally {
|
|
2648
|
+
globalThis.fetch = savedFetch;
|
|
2649
|
+
}
|
|
2650
|
+
});
|
|
2651
|
+
|
|
2652
|
+
// ----------------------------------------------------------------
|
|
2653
|
+
// Priority 3 — create_withdrawal
|
|
2654
|
+
// ----------------------------------------------------------------
|
|
2655
|
+
|
|
2656
|
+
section("create_withdrawal");
|
|
2657
|
+
|
|
2658
|
+
await test("handleCreateWithdrawal: missing/wrong token returns CONFIRMATION_REQUIRED without fetch", async () => {
|
|
2659
|
+
const fetchCalled = { value: false };
|
|
2660
|
+
const savedFetch = globalThis.fetch;
|
|
2661
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2662
|
+
fetchCalled.value = true;
|
|
2663
|
+
return new Response("{}", { status: 200 });
|
|
2664
|
+
};
|
|
2665
|
+
try {
|
|
2666
|
+
const client = {} as BudaClient;
|
|
2667
|
+
const result = await handleCreateWithdrawal(
|
|
2668
|
+
{ currency: "BTC", amount: 0.01, address: "bc1q...", confirmation_token: "NOPE" },
|
|
2669
|
+
client,
|
|
2670
|
+
);
|
|
2671
|
+
assert(result.isError === true, "should be error");
|
|
2672
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2673
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
2674
|
+
assertEqual(parsed.code, "CONFIRMATION_REQUIRED", "code should be CONFIRMATION_REQUIRED");
|
|
2675
|
+
} finally {
|
|
2676
|
+
globalThis.fetch = savedFetch;
|
|
2677
|
+
}
|
|
2678
|
+
});
|
|
2679
|
+
|
|
2680
|
+
await test("handleCreateWithdrawal: both address+bank_account_id → VALIDATION_ERROR", async () => {
|
|
2681
|
+
const fetchCalled = { value: false };
|
|
2682
|
+
const savedFetch = globalThis.fetch;
|
|
2683
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2684
|
+
fetchCalled.value = true;
|
|
2685
|
+
return new Response("{}", { status: 200 });
|
|
2686
|
+
};
|
|
2687
|
+
try {
|
|
2688
|
+
const client = {} as BudaClient;
|
|
2689
|
+
const result = await handleCreateWithdrawal(
|
|
2690
|
+
{ currency: "BTC", amount: 0.01, address: "bc1q...", bank_account_id: 123, confirmation_token: "CONFIRM" },
|
|
2691
|
+
client,
|
|
2692
|
+
);
|
|
2693
|
+
assert(result.isError === true, "should be error");
|
|
2694
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2695
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
2696
|
+
assertEqual(parsed.code, "VALIDATION_ERROR", "code should be VALIDATION_ERROR");
|
|
2697
|
+
} finally {
|
|
2698
|
+
globalThis.fetch = savedFetch;
|
|
2699
|
+
}
|
|
2700
|
+
});
|
|
2701
|
+
|
|
2702
|
+
await test("handleCreateWithdrawal: neither address nor bank_account_id → VALIDATION_ERROR", async () => {
|
|
2703
|
+
const fetchCalled = { value: false };
|
|
2704
|
+
const savedFetch = globalThis.fetch;
|
|
2705
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2706
|
+
fetchCalled.value = true;
|
|
2707
|
+
return new Response("{}", { status: 200 });
|
|
2708
|
+
};
|
|
2709
|
+
try {
|
|
2710
|
+
const client = {} as BudaClient;
|
|
2711
|
+
const result = await handleCreateWithdrawal(
|
|
2712
|
+
{ currency: "BTC", amount: 0.01, confirmation_token: "CONFIRM" },
|
|
2713
|
+
client,
|
|
2714
|
+
);
|
|
2715
|
+
assert(result.isError === true, "should be error");
|
|
2716
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2717
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
2718
|
+
assertEqual(parsed.code, "VALIDATION_ERROR", "code should be VALIDATION_ERROR");
|
|
2719
|
+
} finally {
|
|
2720
|
+
globalThis.fetch = savedFetch;
|
|
2721
|
+
}
|
|
2722
|
+
});
|
|
2723
|
+
|
|
2724
|
+
await test("handleCreateWithdrawal: crypto path + CONFIRM", async () => {
|
|
2725
|
+
const savedFetch = globalThis.fetch;
|
|
2726
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2727
|
+
new Response(
|
|
2728
|
+
JSON.stringify({
|
|
2729
|
+
withdrawal: {
|
|
2730
|
+
id: 77,
|
|
2731
|
+
state: "pending",
|
|
2732
|
+
currency: "BTC",
|
|
2733
|
+
amount: ["0.01", "BTC"],
|
|
2734
|
+
fee: ["0.0001", "BTC"],
|
|
2735
|
+
address: "bc1q...",
|
|
2736
|
+
tx_hash: null,
|
|
2737
|
+
bank_account_id: null,
|
|
2738
|
+
created_at: "2024-01-01T00:00:00Z",
|
|
2739
|
+
updated_at: "2024-01-01T00:00:00Z",
|
|
2740
|
+
},
|
|
2741
|
+
}),
|
|
2742
|
+
{ status: 201, headers: { "Content-Type": "application/json" } },
|
|
2743
|
+
);
|
|
2744
|
+
try {
|
|
2745
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2746
|
+
const result = await handleCreateWithdrawal(
|
|
2747
|
+
{ currency: "BTC", amount: 0.01, address: "bc1q...", confirmation_token: "CONFIRM" },
|
|
2748
|
+
client,
|
|
2749
|
+
);
|
|
2750
|
+
assert(!result.isError, "should not be error");
|
|
2751
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
2752
|
+
id: number;
|
|
2753
|
+
state: string;
|
|
2754
|
+
amount: number;
|
|
2755
|
+
address: string | null;
|
|
2756
|
+
};
|
|
2757
|
+
assertEqual(parsed.id, 77, "id should be 77");
|
|
2758
|
+
assertEqual(parsed.state, "pending", "state should be pending");
|
|
2759
|
+
assertEqual(parsed.amount, 0.01, "amount should be flattened");
|
|
2760
|
+
} finally {
|
|
2761
|
+
globalThis.fetch = savedFetch;
|
|
2762
|
+
}
|
|
2763
|
+
});
|
|
2764
|
+
|
|
2765
|
+
await test("handleCreateWithdrawal: fiat path + CONFIRM", async () => {
|
|
2766
|
+
const savedFetch = globalThis.fetch;
|
|
2767
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2768
|
+
new Response(
|
|
2769
|
+
JSON.stringify({
|
|
2770
|
+
withdrawal: {
|
|
2771
|
+
id: 88,
|
|
2772
|
+
state: "pending",
|
|
2773
|
+
currency: "CLP",
|
|
2774
|
+
amount: ["100000", "CLP"],
|
|
2775
|
+
fee: ["0", "CLP"],
|
|
2776
|
+
address: null,
|
|
2777
|
+
tx_hash: null,
|
|
2778
|
+
bank_account_id: 123,
|
|
2779
|
+
created_at: "2024-01-01T00:00:00Z",
|
|
2780
|
+
updated_at: "2024-01-01T00:00:00Z",
|
|
2781
|
+
},
|
|
2782
|
+
}),
|
|
2783
|
+
{ status: 201, headers: { "Content-Type": "application/json" } },
|
|
2784
|
+
);
|
|
2785
|
+
try {
|
|
2786
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2787
|
+
const result = await handleCreateWithdrawal(
|
|
2788
|
+
{ currency: "CLP", amount: 100000, bank_account_id: 123, confirmation_token: "CONFIRM" },
|
|
2789
|
+
client,
|
|
2790
|
+
);
|
|
2791
|
+
assert(!result.isError, "should not be error");
|
|
2792
|
+
const parsed = JSON.parse(result.content[0].text) as { id: number; bank_account_id: number | null };
|
|
2793
|
+
assertEqual(parsed.id, 88, "id should be 88");
|
|
2794
|
+
assertEqual(parsed.bank_account_id, 123, "bank_account_id should be 123");
|
|
2795
|
+
} finally {
|
|
2796
|
+
globalThis.fetch = savedFetch;
|
|
2797
|
+
}
|
|
2798
|
+
});
|
|
2799
|
+
|
|
2800
|
+
await test("handleCreateWithdrawal: 422 insufficient balance passthrough", async () => {
|
|
2801
|
+
const savedFetch = globalThis.fetch;
|
|
2802
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2803
|
+
new Response(JSON.stringify({ message: "Insufficient balance" }), { status: 422 });
|
|
2804
|
+
try {
|
|
2805
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2806
|
+
const result = await handleCreateWithdrawal(
|
|
2807
|
+
{ currency: "BTC", amount: 999, address: "bc1q...", confirmation_token: "CONFIRM" },
|
|
2808
|
+
client,
|
|
2809
|
+
);
|
|
2810
|
+
assert(result.isError === true, "should be error");
|
|
2811
|
+
const parsed = JSON.parse(result.content[0].text) as { code: number };
|
|
2812
|
+
assertEqual(parsed.code, 422, "code should be 422");
|
|
2813
|
+
} finally {
|
|
2814
|
+
globalThis.fetch = savedFetch;
|
|
2815
|
+
}
|
|
2816
|
+
});
|
|
2817
|
+
|
|
2818
|
+
// ----------------------------------------------------------------
|
|
2819
|
+
// Priority 3 — create_fiat_deposit
|
|
2820
|
+
// ----------------------------------------------------------------
|
|
2821
|
+
|
|
2822
|
+
section("create_fiat_deposit");
|
|
2823
|
+
|
|
2824
|
+
await test("handleCreateFiatDeposit: missing/wrong token returns CONFIRMATION_REQUIRED without fetch", async () => {
|
|
2825
|
+
const fetchCalled = { value: false };
|
|
2826
|
+
const savedFetch = globalThis.fetch;
|
|
2827
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2828
|
+
fetchCalled.value = true;
|
|
2829
|
+
return new Response("{}", { status: 200 });
|
|
2830
|
+
};
|
|
2831
|
+
try {
|
|
2832
|
+
const client = {} as BudaClient;
|
|
2833
|
+
const result = await handleCreateFiatDeposit(
|
|
2834
|
+
{ currency: "CLP", amount: 100000, confirmation_token: "NOPE" },
|
|
2835
|
+
client,
|
|
2836
|
+
);
|
|
2837
|
+
assert(result.isError === true, "should be error");
|
|
2838
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2839
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
2840
|
+
assertEqual(parsed.code, "CONFIRMATION_REQUIRED", "code should be CONFIRMATION_REQUIRED");
|
|
2841
|
+
} finally {
|
|
2842
|
+
globalThis.fetch = savedFetch;
|
|
2843
|
+
}
|
|
2844
|
+
});
|
|
2845
|
+
|
|
2846
|
+
await test("handleCreateFiatDeposit: invalid currency → INVALID_CURRENCY before API", async () => {
|
|
2847
|
+
const fetchCalled = { value: false };
|
|
2848
|
+
const savedFetch = globalThis.fetch;
|
|
2849
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2850
|
+
fetchCalled.value = true;
|
|
2851
|
+
return new Response("{}", { status: 200 });
|
|
2852
|
+
};
|
|
2853
|
+
try {
|
|
2854
|
+
const client = {} as BudaClient;
|
|
2855
|
+
const result = await handleCreateFiatDeposit(
|
|
2856
|
+
{ currency: "!!!", amount: 100000, confirmation_token: "CONFIRM" },
|
|
2857
|
+
client,
|
|
2858
|
+
);
|
|
2859
|
+
assert(result.isError === true, "should be error");
|
|
2860
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2861
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string };
|
|
2862
|
+
assertEqual(parsed.code, "INVALID_CURRENCY", "code should be INVALID_CURRENCY");
|
|
2863
|
+
} finally {
|
|
2864
|
+
globalThis.fetch = savedFetch;
|
|
2865
|
+
}
|
|
2866
|
+
});
|
|
2867
|
+
|
|
2868
|
+
await test("handleCreateFiatDeposit: happy path + CONFIRM", async () => {
|
|
2869
|
+
const savedFetch = globalThis.fetch;
|
|
2870
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2871
|
+
new Response(
|
|
2872
|
+
JSON.stringify({
|
|
2873
|
+
deposit: {
|
|
2874
|
+
id: 55,
|
|
2875
|
+
state: "pending_info",
|
|
2876
|
+
currency: "CLP",
|
|
2877
|
+
amount: ["100000", "CLP"],
|
|
2878
|
+
fee: ["0", "CLP"],
|
|
2879
|
+
created_at: "2024-01-01T00:00:00Z",
|
|
2880
|
+
updated_at: "2024-01-01T00:00:00Z",
|
|
2881
|
+
transfer_account_id: null,
|
|
2882
|
+
transaction_hash: null,
|
|
2883
|
+
},
|
|
2884
|
+
}),
|
|
2885
|
+
{ status: 201, headers: { "Content-Type": "application/json" } },
|
|
2886
|
+
);
|
|
2887
|
+
try {
|
|
2888
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2889
|
+
const result = await handleCreateFiatDeposit(
|
|
2890
|
+
{ currency: "CLP", amount: 100000, bank: "BancoEstado", confirmation_token: "CONFIRM" },
|
|
2891
|
+
client,
|
|
2892
|
+
);
|
|
2893
|
+
assert(!result.isError, "should not be error");
|
|
2894
|
+
const parsed = JSON.parse(result.content[0].text) as { id: number; state: string; amount: number };
|
|
2895
|
+
assertEqual(parsed.id, 55, "id should be 55");
|
|
2896
|
+
assertEqual(parsed.state, "pending_info", "state should be pending_info");
|
|
2897
|
+
assertEqual(parsed.amount, 100000, "amount should be flattened");
|
|
2898
|
+
} finally {
|
|
2899
|
+
globalThis.fetch = savedFetch;
|
|
2900
|
+
}
|
|
2901
|
+
});
|
|
2902
|
+
|
|
2903
|
+
await test("handleCreateFiatDeposit: 422 passthrough", async () => {
|
|
2904
|
+
const savedFetch = globalThis.fetch;
|
|
2905
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2906
|
+
new Response(JSON.stringify({ message: "Unprocessable Entity" }), { status: 422 });
|
|
2907
|
+
try {
|
|
2908
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2909
|
+
const result = await handleCreateFiatDeposit(
|
|
2910
|
+
{ currency: "CLP", amount: 100000, confirmation_token: "CONFIRM" },
|
|
2911
|
+
client,
|
|
2912
|
+
);
|
|
2913
|
+
assert(result.isError === true, "should be error");
|
|
2914
|
+
const parsed = JSON.parse(result.content[0].text) as { code: number };
|
|
2915
|
+
assertEqual(parsed.code, 422, "code should be 422");
|
|
2916
|
+
} finally {
|
|
2917
|
+
globalThis.fetch = savedFetch;
|
|
2918
|
+
}
|
|
2919
|
+
});
|
|
2920
|
+
|
|
2921
|
+
// ----------------------------------------------------------------
|
|
2922
|
+
// Priority 3 — lightning_withdrawal
|
|
2923
|
+
// ----------------------------------------------------------------
|
|
2924
|
+
|
|
2925
|
+
section("lightning_withdrawal");
|
|
2926
|
+
|
|
2927
|
+
await test("handleLightningWithdrawal: missing/wrong token returns CONFIRMATION_REQUIRED without fetch", async () => {
|
|
2928
|
+
const fetchCalled = { value: false };
|
|
2929
|
+
const savedFetch = globalThis.fetch;
|
|
2930
|
+
globalThis.fetch = async (): Promise<Response> => {
|
|
2931
|
+
fetchCalled.value = true;
|
|
2932
|
+
return new Response("{}", { status: 200 });
|
|
2933
|
+
};
|
|
2934
|
+
try {
|
|
2935
|
+
const client = {} as BudaClient;
|
|
2936
|
+
const result = await handleLightningWithdrawal(
|
|
2937
|
+
{ invoice: "lnbc1000u1ptest...", confirmation_token: "NOPE" },
|
|
2938
|
+
client,
|
|
2939
|
+
);
|
|
2940
|
+
assert(result.isError === true, "should be error");
|
|
2941
|
+
assert(!fetchCalled.value, "fetch should not have been called");
|
|
2942
|
+
const parsed = JSON.parse(result.content[0].text) as { code: string; preview?: { invoice_preview: string } };
|
|
2943
|
+
assertEqual(parsed.code, "CONFIRMATION_REQUIRED", "code should be CONFIRMATION_REQUIRED");
|
|
2944
|
+
assert(parsed.preview?.invoice_preview !== undefined, "should include invoice_preview");
|
|
2945
|
+
} finally {
|
|
2946
|
+
globalThis.fetch = savedFetch;
|
|
2947
|
+
}
|
|
2948
|
+
});
|
|
2949
|
+
|
|
2950
|
+
await test("handleLightningWithdrawal: happy path returns flat withdrawal", async () => {
|
|
2951
|
+
const savedFetch = globalThis.fetch;
|
|
2952
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2953
|
+
new Response(
|
|
2954
|
+
JSON.stringify({
|
|
2955
|
+
lightning_withdrawal: {
|
|
2956
|
+
id: 42,
|
|
2957
|
+
state: "completed",
|
|
2958
|
+
amount: ["100000", "SAT"],
|
|
2959
|
+
fee: ["1000", "SAT"],
|
|
2960
|
+
payment_hash: "abc123",
|
|
2961
|
+
created_at: "2024-01-01T00:00:00Z",
|
|
2962
|
+
},
|
|
2963
|
+
}),
|
|
2964
|
+
{ status: 201, headers: { "Content-Type": "application/json" } },
|
|
2965
|
+
);
|
|
2966
|
+
try {
|
|
2967
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2968
|
+
const result = await handleLightningWithdrawal(
|
|
2969
|
+
{ invoice: "lnbc1000u1ptest...", confirmation_token: "CONFIRM" },
|
|
2970
|
+
client,
|
|
2971
|
+
);
|
|
2972
|
+
assert(!result.isError, "should not be error");
|
|
2973
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
2974
|
+
id: number;
|
|
2975
|
+
state: string;
|
|
2976
|
+
amount: number;
|
|
2977
|
+
payment_hash: string;
|
|
2978
|
+
};
|
|
2979
|
+
assertEqual(parsed.id, 42, "id should be 42");
|
|
2980
|
+
assertEqual(parsed.state, "completed", "state should be completed");
|
|
2981
|
+
assertEqual(parsed.amount, 100000, "amount should be flattened");
|
|
2982
|
+
assertEqual(parsed.payment_hash, "abc123", "payment_hash should be abc123");
|
|
2983
|
+
} finally {
|
|
2984
|
+
globalThis.fetch = savedFetch;
|
|
2985
|
+
}
|
|
2986
|
+
});
|
|
2987
|
+
|
|
2988
|
+
await test("handleLightningWithdrawal: API error passthrough", async () => {
|
|
2989
|
+
const savedFetch = globalThis.fetch;
|
|
2990
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
2991
|
+
new Response(JSON.stringify({ message: "Invoice already paid" }), { status: 422 });
|
|
2992
|
+
try {
|
|
2993
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
2994
|
+
const result = await handleLightningWithdrawal(
|
|
2995
|
+
{ invoice: "lnbc1000u1ptest...", confirmation_token: "CONFIRM" },
|
|
2996
|
+
client,
|
|
2997
|
+
);
|
|
2998
|
+
assert(result.isError === true, "should be error");
|
|
2999
|
+
const parsed = JSON.parse(result.content[0].text) as { code: number };
|
|
3000
|
+
assertEqual(parsed.code, 422, "code should be 422");
|
|
3001
|
+
} finally {
|
|
3002
|
+
globalThis.fetch = savedFetch;
|
|
3003
|
+
}
|
|
3004
|
+
});
|
|
3005
|
+
|
|
3006
|
+
// ----------------------------------------------------------------
|
|
3007
|
+
// Priority 3 — create_lightning_invoice
|
|
3008
|
+
// ----------------------------------------------------------------
|
|
3009
|
+
|
|
3010
|
+
section("create_lightning_invoice");
|
|
3011
|
+
|
|
3012
|
+
await test("handleCreateLightningInvoice: happy path returns invoice fields", async () => {
|
|
3013
|
+
const savedFetch = globalThis.fetch;
|
|
3014
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
3015
|
+
new Response(
|
|
3016
|
+
JSON.stringify({
|
|
3017
|
+
lightning_network_invoice: {
|
|
3018
|
+
id: 99,
|
|
3019
|
+
payment_request: "lnbc1000u1pfinal...",
|
|
3020
|
+
amount: ["100000", "SAT"],
|
|
3021
|
+
description: "Test payment",
|
|
3022
|
+
expires_at: "2024-01-01T01:00:00Z",
|
|
3023
|
+
state: "pending",
|
|
3024
|
+
created_at: "2024-01-01T00:00:00Z",
|
|
3025
|
+
},
|
|
3026
|
+
}),
|
|
3027
|
+
{ status: 201, headers: { "Content-Type": "application/json" } },
|
|
3028
|
+
);
|
|
3029
|
+
try {
|
|
3030
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
3031
|
+
const result = await handleCreateLightningInvoice(
|
|
3032
|
+
{ amount_satoshis: 100000, description: "Test payment", expiry_seconds: 3600 },
|
|
3033
|
+
client,
|
|
3034
|
+
);
|
|
3035
|
+
assert(!result.isError, "should not be error");
|
|
3036
|
+
const parsed = JSON.parse(result.content[0].text) as {
|
|
3037
|
+
id: number;
|
|
3038
|
+
payment_request: string;
|
|
3039
|
+
amount_satoshis: number;
|
|
3040
|
+
description: string | null;
|
|
3041
|
+
expires_at: string;
|
|
3042
|
+
};
|
|
3043
|
+
assertEqual(parsed.id, 99, "id should be 99");
|
|
3044
|
+
assertEqual(parsed.payment_request, "lnbc1000u1pfinal...", "payment_request should match");
|
|
3045
|
+
assertEqual(parsed.amount_satoshis, 100000, "amount_satoshis should be 100000");
|
|
3046
|
+
assertEqual(parsed.description, "Test payment", "description should match");
|
|
3047
|
+
} finally {
|
|
3048
|
+
globalThis.fetch = savedFetch;
|
|
3049
|
+
}
|
|
3050
|
+
});
|
|
3051
|
+
|
|
3052
|
+
await test("handleCreateLightningInvoice: API error passthrough", async () => {
|
|
3053
|
+
const savedFetch = globalThis.fetch;
|
|
3054
|
+
globalThis.fetch = async (): Promise<Response> =>
|
|
3055
|
+
new Response(JSON.stringify({ message: "Unprocessable Entity" }), { status: 422 });
|
|
3056
|
+
try {
|
|
3057
|
+
const client = new BudaClient("https://www.buda.com/api/v2");
|
|
3058
|
+
const result = await handleCreateLightningInvoice({ amount_satoshis: 100000 }, client);
|
|
3059
|
+
assert(result.isError === true, "should be error");
|
|
3060
|
+
const parsed = JSON.parse(result.content[0].text) as { code: number };
|
|
3061
|
+
assertEqual(parsed.code, 422, "code should be 422");
|
|
3062
|
+
} finally {
|
|
3063
|
+
globalThis.fetch = savedFetch;
|
|
3064
|
+
}
|
|
3065
|
+
});
|
|
3066
|
+
|
|
1162
3067
|
// ----------------------------------------------------------------
|
|
1163
3068
|
// Summary
|
|
1164
3069
|
// ----------------------------------------------------------------
|