Files
pump-game-ops/tools/autoclose-check.cjs
T
subochev 6ee30c39fb docs(security): SEC-2/SEC-3/SEC-8 закрыты, SEC-4 усилен (updown-relayer:6)
- SEC-3 закрыт: ручка POST /close удалена, ставки закрывает сам шлюз (404)
- SEC-8 закрыт: авто-закрытие + одна открытая ставка на адрес (409)
- SEC-2 закрыт по факту: планировщик settle.ts работает, проверено живьём
- SEC-4 усилен: пара UP+DOWN больше не набирается (вторя ставка -> 409)
- SEC-5 частично: счётчик больше не накрутить массово, но /bet без авторизации
- tools/autoclose-check.cjs: сквозной живой тест (9/9 пройдено)
2026-09-13 18:49:37 +03:00

119 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* ЖИВАЯ проверка итерации: авто-закрытие + одна ставка на адрес + 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); });