/** * ЖИВАЯ проверка итерации: авто-закрытие + одна ставка на адрес + WS-события. * Запуск на хосте ассистента (Node 22, глобальный fetch + WebSocket). * * node autoclose-check.cjs * * Проверяет по шагам: * 1. POST /wallet — новый кошелёк * 2. GET /faucet/{addr} — пополнить * 3. WS /api/ws — подключиться и слушать события * 4. POST /bet — 1-я ставка: ждём 200 + событие bet_open * 5. POST /bet — 2-я ставка: ждём 409 (одна открытая на адрес) * 6. ждать экспирацию ~15 с — ждём событие bet_closed (авто-закрытие) * 7. POST /close — должен быть 404 (ручку убрали) */ const BASE = process.env.BASE || "https://testfront.binom.pw"; const WS_URL = process.env.WS_URL || "wss://testfront.binom.pw/api/ws"; const results = []; function check(name, ok, detail) { results.push({ name, ok, detail }); console.log(`${ok ? "OK " : "FAIL"} | ${name}${detail ? " — " + detail : ""}`); } const j = async (path, init) => { const r = await fetch(BASE + path, init); let body = null; try { body = await r.json(); } catch { /* не JSON */ } return { status: r.status, body }; }; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); (async () => { // --- 3. WS заранее: события редкие, слушаем всё время --- const events = []; let ws; try { ws = new WebSocket(WS_URL); ws.onmessage = (m) => { try { const d = JSON.parse(m.data); if (d.type !== "price") { events.push(d); console.log(" WS<-", JSON.stringify(d)); } } catch { /* ignore */ } }; await new Promise((res, rej) => { ws.onopen = res; ws.onerror = (e) => rej(new Error("ws error " + (e.message || ""))); setTimeout(() => rej(new Error("ws timeout")), 10000); }); check("WS подключён", true, WS_URL); } catch (e) { check("WS подключён", false, e.message); } // --- 1. кошелёк --- const w = await j("/wallet", { method: "POST" }); const addr = w.body && (w.body.address || w.body.pubkey || w.body.bettor); check("POST /wallet → 200 + адрес", w.status === 200 && !!addr, `status=${w.status} addr=${addr}`); if (!addr) { console.log("\nдальше невозможно: нет адреса"); process.exit(1); } // --- 2. faucet --- let f = await j(`/faucet/${addr}`); if (f.status !== 200) { f = await j(`/faucet/${addr}`, { method: "POST" }); } check("GET /faucet → пополнен", f.status === 200, `status=${f.status} ${JSON.stringify(f.body).slice(0,120)}`); await sleep(2500); const bal = await j(`/balance/${addr}`); check("GET /balance → 200", bal.status === 200, JSON.stringify(bal.body).slice(0, 160)); // --- 4. первая ставка --- const bet1 = await j("/bet", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ address: addr, side: "UP", amountUnits: 100000000 }), }); check("POST /bet (1-я) → 200", bet1.status === 200, `status=${bet1.status} ${JSON.stringify(bet1.body).slice(0,180)}`); const betId = bet1.body && (bet1.body.id ?? bet1.body.betId ?? bet1.body.bet_id); console.log(` id ставки = ${betId}`); // --- 5. вторая ставка — должна быть отклонена --- const bet2 = await j("/bet", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ address: addr, side: "DOWN", amountUnits: 100000000 }), }); const blocked = bet2.status === 409 || bet2.status === 400; check("POST /bet (2-я, пока 1-я открыта) → отклонена", blocked, `status=${bet2.status} ${JSON.stringify(bet2.body).slice(0,180)}`); // --- 6. авто-закрытие: ждём событие --- const t0 = Date.now(); let closed = null; while (Date.now() - t0 < 45000) { closed = events.find((e) => e.type === "bet_closed" && String(e.id) === String(betId)); if (closed) break; await sleep(1000); } const secs = ((Date.now() - t0) / 1000).toFixed(0); check("WS: событие bet_closed (авто-закрытие)", !!closed, closed ? `через ${secs} с: ${JSON.stringify(closed)}` : `не пришло за 45 с`); const opened = events.find((e) => e.type === "bet_open" && String(e.id) === String(betId)); check("WS: событие bet_open", !!opened, opened ? JSON.stringify(opened) : "не пришло"); // --- 7. ручка /close убрана --- const cl = await j("/close", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ id: betId }), }); check("POST /close → 404 (ручку убрали)", cl.status === 404, `status=${cl.status}`); // --- итог --- const bad = results.filter((r) => !r.ok); console.log(`\n=== ИТОГ: ${results.length - bad.length}/${results.length} пройдено ===`); if (bad.length) console.log("провалы: " + bad.map((b) => b.name).join("; ")); if (ws) try { ws.close(); } catch { /* ignore */ } process.exit(bad.length ? 1 : 0); })().catch((e) => { console.error("ERR", e); process.exit(2); });