496 lines
22 KiB
Python
496 lines
22 KiB
Python
"""Uji verifikasi klaim reward BP (oracle §V32..V34).
|
|
|
|
`./venv/bin/python test_claim.py` harus exit 0. Memakai DB sementara + mock
|
|
jaringan/pyntelope — ⊥ klaim nyata. Verifikasi: jendela klaim (V32), parse
|
|
respons `get_table_rows` bentuk asli `{rows:[...]}` + recovery window lewat,
|
|
math fee (V33), siklus penuh klaim→fee, retry fee, resume, reward 0 →
|
|
skipped, klaim yang mendarat walau timeout (V34).
|
|
"""
|
|
|
|
import os
|
|
import sqlite3
|
|
import sys
|
|
import tempfile
|
|
from datetime import datetime, timedelta
|
|
from decimal import Decimal
|
|
|
|
import db
|
|
import claim
|
|
import telegram
|
|
import distribute as dist
|
|
|
|
|
|
def check(tag, cond):
|
|
if not cond:
|
|
raise AssertionError(f'GAGAL: {tag}')
|
|
print(f'ok: {tag}')
|
|
|
|
|
|
class FakeSigned:
|
|
"""Objek SignedTransaction tiruan: id deterministik + send terkontrol."""
|
|
|
|
def __init__(self, txid, exc=None):
|
|
self._txid = txid
|
|
self._exc = exc
|
|
|
|
def id(self):
|
|
return self._txid
|
|
|
|
def send(self):
|
|
if self._exc is not None:
|
|
raise self._exc
|
|
return {'processed': True}
|
|
|
|
|
|
def fresh_db(tag):
|
|
"""DB sqlite sementara baru per bagian uji."""
|
|
tmp = tempfile.mkdtemp()
|
|
db.DB_PATH = os.path.join(tmp, f'claim-{tag}.db')
|
|
return db.DB_PATH
|
|
|
|
|
|
def last_claim_row():
|
|
row = db.queryone(
|
|
'SELECT claim_id, run_date, claim_txid, reward, fee_amount, '
|
|
' fee_status, fee_txid FROM claim_runs ORDER BY claim_id DESC '
|
|
'LIMIT 1')
|
|
return row
|
|
|
|
|
|
def fake_post(data):
|
|
return type('R', (), {'raise_for_status': lambda self: None,
|
|
'json': lambda self: data})()
|
|
|
|
|
|
def main():
|
|
# Matikan sleep nyata (settle/poll) + Telegram selama uji.
|
|
claim.time.sleep = lambda s: None
|
|
_tg_token = telegram.TELEGRAM_BOT_TOKEN
|
|
_tg_comm = telegram.TELEGRAM_COMMUNITY_CHAT_IDS
|
|
_tg_int = telegram.TELEGRAM_INTERNAL_CHAT_IDS
|
|
_tg_post = telegram.requests.post
|
|
_req_get = dist.requests.get
|
|
telegram.TELEGRAM_BOT_TOKEN = ''
|
|
telegram.TELEGRAM_COMMUNITY_CHAT_IDS = []
|
|
telegram.TELEGRAM_INTERNAL_CHAT_IDS = []
|
|
# Default: pengukuran reward via selisih saldo (tx-based diuji terpisah).
|
|
_orig_reward_from_tx = claim.reward_from_tx
|
|
claim.reward_from_tx = lambda txid: None
|
|
try:
|
|
# ————— V32: parse_claim_time — format chain —————
|
|
check('V32 parse format %f',
|
|
claim.parse_claim_time('2026-08-05T07:01:44.500')
|
|
== datetime(2026, 8, 5, 7, 1, 44, 500000))
|
|
check('V32 parse format detik',
|
|
claim.parse_claim_time('2026-08-05T07:01:44')
|
|
== datetime(2026, 8, 5, 7, 1, 44))
|
|
check('V32 parse kosong → None', claim.parse_claim_time(None) is None)
|
|
check('V32 parse sampah → None', claim.parse_claim_time('x') is None)
|
|
|
|
# ————— V32: fetch_producer_last_claim — bentuk asli get_table_rows —————
|
|
claim._post_json = lambda url, payload: {
|
|
'rows': [{'last_claim_time': '2026-08-05T07:01:44.500'}],
|
|
'more': False, 'next_key': ''}
|
|
check('V32 parse respons asli {rows:[...]} → waktu',
|
|
claim.fetch_producer_last_claim() == '2026-08-05T07:01:44.500')
|
|
claim._post_json = lambda url, payload: {'rows': [], 'more': False,
|
|
'next_key': ''}
|
|
check('V32 rows kosong → None',
|
|
claim.fetch_producer_last_claim() is None)
|
|
claim._post_json = lambda url, payload: {}
|
|
check('V32 respons tak dikenal → None (⊥ crash)',
|
|
claim.fetch_producer_last_claim() is None)
|
|
|
|
# ————— V32: next_window = last + 24 jam; lewat → klaim segera —————
|
|
claim.fetch_producer_last_claim = lambda: '2026-08-05T07:01:44.500'
|
|
check('V32 jendela = last + 24 jam',
|
|
claim.next_window(datetime(2026, 8, 6, 5, 0, 0))
|
|
== datetime(2026, 8, 6, 7, 1, 44, 500000))
|
|
check('V32 window lewat (≥24 jam) → now (recovery, ⊥ deadlock)',
|
|
claim.next_window(datetime(2026, 8, 6, 12, 0, 0))
|
|
== datetime(2026, 8, 6, 12, 0, 0))
|
|
claim.fetch_producer_last_claim = lambda: None
|
|
try:
|
|
claim.next_window(datetime(2026, 8, 6, 12, 0, 0))
|
|
raise AssertionError('GAGAL: baca producer gagal harus raise')
|
|
except RuntimeError:
|
|
pass
|
|
check('V46 baca producer gagal → raise (⊥ clamp prematur)',
|
|
True)
|
|
# BP baru (row ada, last_claim_time epoch/dulu) → jendela lewat → now
|
|
claim.fetch_producer_last_claim = lambda: '2000-01-01T00:00:00.000'
|
|
check('V32 last epoch (BP baru) → now',
|
|
claim.next_window(datetime(2026, 8, 6, 12, 0, 0))
|
|
== datetime(2026, 8, 6, 12, 0, 0))
|
|
|
|
# ————— V49: window math UTC-naive (konsisten dgn last_claim_time chain) —————
|
|
now_utc = claim._utcnow()
|
|
check('V49 _utcnow UTC-naive (⊥ tzinfo lokal)',
|
|
now_utc.tzinfo is None)
|
|
# step dengan now jauh sebelum last+24h → sleep (⊥ poll prematur)
|
|
fresh_db('prewindow')
|
|
claim.fetch_producer_last_claim = lambda: (
|
|
claim._utcnow() - timedelta(hours=12)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
check('V49 before window → sleep (⊥ poll prematur)',
|
|
claim.step() == 'sleep')
|
|
|
|
# ————— V32/V33: siklus penuh — klaim → catat → fee terkirim —————
|
|
fresh_db('cycle')
|
|
claim.fetch_producer_last_claim = lambda: (
|
|
claim._utcnow() - timedelta(hours=25)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
balances = iter([Decimal('100.0000'), Decimal('110.0004')])
|
|
dist.fetch_balance = lambda: next(balances)
|
|
claim.build_claim_action = lambda: FakeSigned('txclaim')
|
|
dist.verify_txid = lambda txid: True
|
|
|
|
r1 = claim.step()
|
|
check('V32 step klaim sukses → claimed', r1 == 'claimed')
|
|
row = last_claim_row()
|
|
check('V32 klaim tercatat (reward 10.0004, fee 1.0000 floor)',
|
|
row is not None and row[3] == 10.0004 and row[4] == 1.0
|
|
and row[2] == 'txclaim' and row[5] == 'pending')
|
|
|
|
memos = []
|
|
|
|
def fake_fee_sign(to, amount, memo):
|
|
memos.append((to, amount, memo))
|
|
return FakeSigned('txfee')
|
|
|
|
dist.build_signed_transfer = fake_fee_sign
|
|
r2 = claim.step()
|
|
check('V34 fee terkirim → fee_sent', r2 == 'fee_sent')
|
|
check('V33 fee ke wallet bpdbsjasprod', memos[-1][0] == 'bpdbsjasprod')
|
|
check('V33 memo BP FEE + tanggal',
|
|
memos[-1][2] == f'BP FEE {claim._utcnow().strftime("%Y-%m-%d")}')
|
|
row = last_claim_row()
|
|
check('V34 fee ditandai sent + txid',
|
|
row[5] == 'sent' and row[6] == 'txfee')
|
|
|
|
# ————— V33/V34: fee gagal → failed → diulang sampai terkirim —————
|
|
fresh_db('retry')
|
|
db.record_claim('2026-08-05', 'txold', 10.0, 1.0, 'pending',
|
|
'2026-08-05T08:00:00')
|
|
dist.build_signed_transfer = lambda to, amount, memo: FakeSigned(
|
|
'txfail', exc=RuntimeError('net down'))
|
|
dist.verify_txid = lambda txid: False
|
|
check('V34 fee gagal → fee_failed', claim.step() == 'fee_failed')
|
|
check('V34 status failed',
|
|
last_claim_row()[5] == 'failed')
|
|
dist.build_signed_transfer = lambda to, amount, memo: FakeSigned('txok')
|
|
check('V34 fee diulang → fee_sent', claim.step() == 'fee_sent')
|
|
row = last_claim_row()
|
|
check('V34 fee akhirnya sent', row[5] == 'sent' and row[6] == 'txok')
|
|
|
|
# ————— V33: reward 0 → fee skipped; step lanjut ke jadwal —————
|
|
fresh_db('zero')
|
|
claim.fetch_producer_last_claim = lambda: (
|
|
claim._utcnow() - timedelta(hours=25)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
balances = iter([Decimal('100.0000'), Decimal('100.0000')])
|
|
dist.fetch_balance = lambda: next(balances)
|
|
claim.build_claim_action = lambda: FakeSigned('txzero')
|
|
dist.verify_txid = lambda txid: True
|
|
check('V33 klaim reward 0 → claimed', claim.step() == 'claimed')
|
|
row = last_claim_row()
|
|
check('V33 fee 0 → skipped', row[4] == 0.0 and row[5] == 'skipped')
|
|
# pending_claim_fee ⊥ memuat fee 0 → step lanjut ke jadwal (sleep)
|
|
claim.fetch_producer_last_claim = lambda: (
|
|
claim._utcnow() - timedelta(hours=20)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
check('V34 ⊥ fee pending → jadwal (sleep)', claim.step() == 'sleep')
|
|
|
|
# ————— V34: klaim timeout tapi mendarat (verify True) → diukur —————
|
|
fresh_db('landed')
|
|
claim.fetch_producer_last_claim = lambda: (
|
|
claim._utcnow() - timedelta(hours=25)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
balances = iter([Decimal('50.0000'), Decimal('60.0000')])
|
|
dist.fetch_balance = lambda: next(balances)
|
|
claim.build_claim_action = lambda: FakeSigned(
|
|
'txL', exc=RuntimeError('broadcast timeout'))
|
|
dist.verify_txid = lambda txid: True
|
|
check('V34 timeout tapi mendarat → claimed',
|
|
claim._try_claim_once() == 'claimed')
|
|
row = last_claim_row()
|
|
check('V34 reward terukur', row[3] == 10.0 and row[2] == 'txL')
|
|
|
|
# ————— V34/V45: verify None + last_claim_time benar2 MAJU → mendarat —————
|
|
fresh_db('reconcile')
|
|
last_state = {'n': 0}
|
|
|
|
def advancing_last():
|
|
last_state['n'] += 1
|
|
if last_state['n'] == 1: # baseline sebelum kirim
|
|
return (claim._utcnow() - timedelta(hours=2)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
return (claim._utcnow() - timedelta(hours=1)
|
|
).strftime('%Y-%m-%dT%H:%M:%S') # maju → mendarat
|
|
|
|
claim.fetch_producer_last_claim = advancing_last
|
|
balances = iter([Decimal('10.0000'), Decimal('12.0000')])
|
|
dist.fetch_balance = lambda: next(balances)
|
|
claim.build_claim_action = lambda: FakeSigned(
|
|
'txN', exc=RuntimeError('timeout'))
|
|
dist.verify_txid = lambda txid: None
|
|
check('V45 verify None + last MAJU → claimed',
|
|
claim._try_claim_once() == 'claimed')
|
|
check('V45 reward tetap diukur',
|
|
last_claim_row()[3] == 2.0)
|
|
|
|
# ————— V45: verify None + last TIDAK maju → retry (⊥ false success) —————
|
|
fresh_db('stale')
|
|
claim.fetch_producer_last_claim = lambda: (
|
|
claim._utcnow() - timedelta(hours=1)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
dist.fetch_balance = lambda: Decimal('100.0000')
|
|
claim.build_claim_action = lambda: FakeSigned(
|
|
'txS', exc=RuntimeError('timeout'))
|
|
dist.verify_txid = lambda txid: None
|
|
check('V45 last tak maju → retry (⊥ sukses-0)',
|
|
claim._try_claim_once() == 'retry')
|
|
db.ensure_claim_schema()
|
|
check('V45 ⊥ catat baris utk yg tak mendarat',
|
|
db.queryone('SELECT COUNT(*) FROM claim_runs')[0] == 0)
|
|
|
|
# ————— V46: send() "sukses" tapi chain MENOLAK (HTTP 500 ⊥ raise) —————
|
|
# pyntelope ⊥ raise saat chain reject — send kembali normal dgn body
|
|
# error; landing harus diverifikasi ulang → tak mendarat → retry.
|
|
fresh_db('rejectsilent')
|
|
claim.fetch_producer_last_claim = lambda: (
|
|
claim._utcnow() - timedelta(hours=25)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
dist.fetch_balance = lambda: Decimal('100.0000')
|
|
claim.build_claim_action = lambda: FakeSigned('txR2') # send ⊥ raise
|
|
dist.verify_txid = lambda txid: False # tak mendarat
|
|
notifs = []
|
|
_orig_nc2 = telegram.notify_claim
|
|
telegram.notify_claim = lambda r, f, w: notifs.append('claimed')
|
|
check('V46 send normal + verify False → retry (⊥ sukses-0)',
|
|
claim._try_claim_once() == 'retry')
|
|
db.ensure_claim_schema()
|
|
check('V46 ⊥ catat baris + ⊥ notif sukses',
|
|
db.queryone('SELECT COUNT(*) FROM claim_runs')[0] == 0
|
|
and notifs == [])
|
|
telegram.notify_claim = _orig_nc2
|
|
|
|
# ————— V46: send() "sukses" + last MAJU → benar-benar mendarat —————
|
|
fresh_db('sendsuccess')
|
|
last_s = {'n': 0}
|
|
|
|
def advancing_success():
|
|
last_s['n'] += 1
|
|
if last_s['n'] == 1:
|
|
return (claim._utcnow() - timedelta(hours=2)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
return (claim._utcnow() - timedelta(hours=1)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
|
|
claim.fetch_producer_last_claim = advancing_success
|
|
balances = iter([Decimal('10.0000'), Decimal('12.0000')])
|
|
dist.fetch_balance = lambda: next(balances)
|
|
claim.build_claim_action = lambda: FakeSigned('txR3') # send ⊥ raise
|
|
dist.verify_txid = lambda txid: None # Hyperion tak terjangkau
|
|
check('V46 send normal + last MAJU → claimed',
|
|
claim._try_claim_once() == 'claimed')
|
|
check('V46 reward terukur',
|
|
last_claim_row()[3] == 2.0)
|
|
|
|
# ————— V50: Hyperion `False` (lag indeks) tapi last_claim_time MAJU —————
|
|
# tx BARU SAJA mendarat belum terindeks Hyperion → verify False (⊥ None);
|
|
# landing tetap dideteksi via kemajuan `last_claim_time` (⊥ retry-loop).
|
|
fresh_db('lagindex')
|
|
last_l = {'n': 0}
|
|
|
|
def advancing_lag():
|
|
last_l['n'] += 1
|
|
if last_l['n'] == 1:
|
|
return (claim._utcnow() - timedelta(hours=2)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
return (claim._utcnow() - timedelta(hours=1)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
|
|
claim.fetch_producer_last_claim = advancing_lag
|
|
balances = iter([Decimal('20.0000'), Decimal('25.0000')])
|
|
dist.fetch_balance = lambda: next(balances)
|
|
claim.build_claim_action = lambda: FakeSigned('txR4') # send ⊥ raise
|
|
dist.verify_txid = lambda txid: False # Hyperion belum indeks (lag)
|
|
check('V50 verify False + last MAJU → claimed (⊥ retry-loop)',
|
|
claim._try_claim_once() == 'claimed')
|
|
check('V50 reward terukur + tercatat',
|
|
last_claim_row()[3] == 5.0 and last_claim_row()[2] == 'txR4')
|
|
|
|
# ————— V54/B16: baca last_after basi di attempt 1, maju di attempt 2 —————
|
|
# poll_claim menghitung baseline SEKALI; attempt 1 mendarat tapi node
|
|
# masih balikin last_claim_time lama → retry; attempt 2 (baseline sama)
|
|
# baca last_claim_time yg sudah maju → claimed (⊥ baseline ikut maju).
|
|
fresh_db('staleretry')
|
|
st = {'n': 0}
|
|
|
|
def stale_then_advance():
|
|
st['n'] += 1
|
|
if st['n'] == 1: # baseline (poll_claim awal window)
|
|
return (claim._utcnow() - timedelta(hours=2)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
if st['n'] == 2: # attempt 1: baca masih basi (belum ter-catch-up)
|
|
return (claim._utcnow() - timedelta(hours=2)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
return (claim._utcnow() - timedelta(hours=1)
|
|
).strftime('%Y-%m-%dT%H:%M:%S') # attempt 2: maju
|
|
|
|
claim.fetch_producer_last_claim = stale_then_advance
|
|
balances = iter([Decimal('30.0000'), Decimal('35.0000'),
|
|
Decimal('40.0000')])
|
|
dist.fetch_balance = lambda: next(balances)
|
|
sends = {'n': 0}
|
|
|
|
def send_once_then_reject():
|
|
sends['n'] += 1
|
|
if sends['n'] == 1:
|
|
return FakeSigned('txR5') # attempt 1: mendarat
|
|
return FakeSigned('txR5b', exc=RuntimeError('already claimed'))
|
|
|
|
claim.build_claim_action = send_once_then_reject
|
|
dist.verify_txid = lambda txid: False # Hyperion lag di kedua attempt
|
|
deadline = claim._utcnow() + timedelta(seconds=3600)
|
|
check('V54 stale lalu maju → poll_claim claimed',
|
|
claim.poll_claim(deadline) == 'claimed')
|
|
check('V54 reward terukur + tercatat',
|
|
last_claim_row()[3] == 5.0 and last_claim_row()[2] == 'txR5b')
|
|
|
|
# ————— V45: reward dari isi tx (bpay+vpay) → prioritas —————
|
|
fresh_db('txreward')
|
|
claim.fetch_producer_last_claim = lambda: (
|
|
claim._utcnow() - timedelta(hours=25)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
dist.fetch_balance = lambda: Decimal('100.0000') # ⊥ dipakai
|
|
claim.build_claim_action = lambda: FakeSigned('txR')
|
|
dist.verify_txid = lambda txid: True
|
|
|
|
def fake_hyperion(url, params=None, timeout=None):
|
|
return type('R', (), {
|
|
'ok': True,
|
|
'raise_for_status': lambda self: None,
|
|
'json': lambda self: {
|
|
'executed': True,
|
|
'actions': [
|
|
{'act': {'account': 'vexcore', 'name': 'claimrewards',
|
|
'data': {'owner': 'databisnisid'}}},
|
|
{'act': {'account': 'vex.token', 'name': 'issue',
|
|
'data': {'to': 'vexcore',
|
|
'quantity': '82744.9200 VEX'}}},
|
|
{'act': {'account': 'vex.token', 'name': 'transfer',
|
|
'data': {'from': 'vex.bpay', 'to': 'databisnisid',
|
|
'quantity': '638.9244 VEX'}}},
|
|
{'act': {'account': 'vex.token', 'name': 'transfer',
|
|
'data': {'from': 'vex.vpay', 'to': 'databisnisid',
|
|
'quantity': '1200.3217 VEX'}}},
|
|
{'act': {'account': 'vex.token', 'name': 'transfer',
|
|
'data': {'from': 'databisnisid', 'to': 'someone',
|
|
'quantity': '5.0000 VEX'}}},
|
|
],
|
|
},
|
|
})()
|
|
|
|
dist.requests.get = fake_hyperion
|
|
claim.reward_from_tx = _orig_reward_from_tx
|
|
check('V45 reward_from_tx jumlah bpay+vpay',
|
|
claim.reward_from_tx('txR') == Decimal('1839.2461'))
|
|
dist.requests.get = fake_hyperion
|
|
check('V45 siklus: reward dari tx → claimed',
|
|
claim._try_claim_once() == 'claimed')
|
|
row = last_claim_row()
|
|
check('V45 reward tx 1839.2461 + fee 183.9246 pending',
|
|
row[3] == 1839.2461 and row[4] == 183.9246
|
|
and row[5] == 'pending' and row[2] == 'txR')
|
|
dist.requests.get = _req_get
|
|
claim.reward_from_tx = lambda txid: None
|
|
|
|
# ————— V45: reward tak terukur (tx gagal + delta gagal) → fee pending —————
|
|
fresh_db('unmeasured')
|
|
claim.fetch_producer_last_claim = lambda: (
|
|
claim._utcnow() - timedelta(hours=25)
|
|
).strftime('%Y-%m-%dT%H:%M:%S')
|
|
claim.reward_from_tx = lambda txid: None
|
|
bal_seq = iter([Decimal('100.0000'), None, None, None]) # before ok, after ⊥
|
|
dist.fetch_balance = lambda: next(bal_seq)
|
|
claim.build_claim_action = lambda: FakeSigned('txU')
|
|
dist.verify_txid = lambda txid: True
|
|
notifs = []
|
|
_orig_nc = telegram.notify_claim
|
|
_orig_ncu = telegram.notify_claim_unmeasured
|
|
|
|
def capture_ni(label):
|
|
notifs.append('unmeasured')
|
|
|
|
telegram.notify_claim_unmeasured = capture_ni
|
|
telegram.notify_claim = lambda r, f, w: notifs.append('claimed')
|
|
check('V45 reward tak terukur → claimed (baris dicatat)',
|
|
claim._try_claim_once() == 'claimed')
|
|
row = last_claim_row()
|
|
check('V45 fee pending utk cek manual (⊥ skipped)',
|
|
row[4] == 0.0 and row[5] == 'pending')
|
|
check('V45 notif cek manual (⊥ sukses-0)',
|
|
'unmeasured' in notifs and 'claimed' not in notifs)
|
|
telegram.notify_claim = _orig_nc
|
|
telegram.notify_claim_unmeasured = _orig_ncu
|
|
|
|
# ————— V32: klaim ditolak → retry; poll deadline lewat → timeout —————
|
|
fresh_db('timeout')
|
|
claim.build_claim_action = lambda: FakeSigned(
|
|
'txT', exc=RuntimeError('rejected'))
|
|
dist.verify_txid = lambda txid: False
|
|
dist.fetch_balance = lambda: Decimal('100.0000')
|
|
check('V32 klaim ditolak → retry',
|
|
claim._try_claim_once() == 'retry')
|
|
check('V32 deadline lewat → timeout',
|
|
claim.poll_claim(claim._utcnow() - timedelta(seconds=1))
|
|
== 'timeout')
|
|
|
|
# ————— resume: fee failed dari DB → step mengirimnya —————
|
|
fresh_db('resume')
|
|
db.record_claim('2026-08-05', 'txr', 10.0, 1.0, 'failed',
|
|
'2026-08-05T08:00:00')
|
|
dist.build_signed_transfer = lambda to, amount, memo: FakeSigned('txr1')
|
|
dist.verify_txid = lambda txid: True
|
|
check('V34 resume fee dari DB → fee_sent', claim.step() == 'fee_sent')
|
|
check('V34 resume → sent',
|
|
last_claim_row()[5] == 'sent' and last_claim_row()[6] == 'txr1')
|
|
|
|
# ————— V36: klaim reward → kanal INTERNAL saja, ⊥ komunitas —————
|
|
tg = {'posts': []}
|
|
|
|
def tg_post(url, json=None, timeout=None):
|
|
tg['posts'].append((url, json['chat_id'], json['text']))
|
|
return fake_post({})
|
|
|
|
telegram.TELEGRAM_BOT_TOKEN = '123:TEST'
|
|
telegram.TELEGRAM_COMMUNITY_CHAT_IDS = ['-1001']
|
|
telegram.TELEGRAM_INTERNAL_CHAT_IDS = ['-1002']
|
|
telegram.requests.post = tg_post
|
|
telegram.notify_claim('10.0000', '1.0000', 'bpdbsjasprod')
|
|
telegram.notify_claim_failure(RuntimeError('net down'))
|
|
check('V36 klaim → internal saja',
|
|
len(tg['posts']) == 2
|
|
and all(p[1] == '-1002' for p in tg['posts']))
|
|
check('V36 ⊥ komunitas utk klaim',
|
|
not any(p[1] == '-1001' for p in tg['posts']))
|
|
check('V36 isi notif klaim',
|
|
'KLAIM REWARD SUKSES' in tg['posts'][0][2]
|
|
and 'KLAIM GAGAL' in tg['posts'][1][2])
|
|
finally:
|
|
claim.time.sleep = time.sleep
|
|
telegram.TELEGRAM_BOT_TOKEN = _tg_token
|
|
telegram.TELEGRAM_COMMUNITY_CHAT_IDS = _tg_comm
|
|
telegram.TELEGRAM_INTERNAL_CHAT_IDS = _tg_int
|
|
telegram.requests.post = _tg_post
|
|
dist.requests.get = _req_get
|
|
|
|
print('\nSEMUA UJI PASS')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import time
|
|
sys.exit(main())
|