Skip to content

Commit 1b4e96c

Browse files
committed
test: cover sqlite persistence node lifecycle
1 parent 54f3fc9 commit 1b4e96c

1 file changed

Lines changed: 120 additions & 0 deletions

File tree

tests/test_persistence_runtime.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import tempfile
2+
import shutil
3+
import unittest
4+
from unittest.mock import patch
5+
6+
from nacl.encoding import HexEncoder
7+
from nacl.signing import SigningKey
8+
9+
import main as main_module
10+
from minichain import Blockchain, Block, Transaction, mine_block
11+
from minichain.persistence import load, save
12+
13+
14+
class FakeNetwork:
15+
def __init__(self):
16+
self.handler = None
17+
self.peer_count = 0
18+
self._on_peer_connected = None
19+
20+
def register_handler(self, handler):
21+
self.handler = handler
22+
23+
def register_on_peer_connected(self, callback):
24+
self._on_peer_connected = callback
25+
26+
async def start(self, port=9000, host="127.0.0.1"):
27+
self.port = port
28+
self.host = host
29+
30+
async def stop(self):
31+
return None
32+
33+
async def connect_to_peer(self, host, port):
34+
self.peer_count += 1
35+
return True
36+
37+
async def broadcast_transaction(self, tx):
38+
return None
39+
40+
async def broadcast_block(self, block, miner=None):
41+
return None
42+
43+
44+
def _make_keypair():
45+
sk = SigningKey.generate()
46+
pk = sk.verify_key.encode(encoder=HexEncoder).decode()
47+
return sk, pk
48+
49+
50+
class TestPersistenceRuntime(unittest.IsolatedAsyncioTestCase):
51+
def setUp(self):
52+
self.tmpdir = tempfile.mkdtemp()
53+
54+
def tearDown(self):
55+
shutil.rmtree(self.tmpdir, ignore_errors=True)
56+
57+
def _chain_with_tx(self):
58+
bc = Blockchain()
59+
alice_sk, alice_pk = _make_keypair()
60+
_, bob_pk = _make_keypair()
61+
62+
bc.state.credit_mining_reward(alice_pk, 100)
63+
tx = Transaction(alice_pk, bob_pk, 30, 0)
64+
tx.sign(alice_sk)
65+
66+
block = Block(
67+
index=1,
68+
previous_hash=bc.last_block.hash,
69+
transactions=[tx],
70+
difficulty=1,
71+
)
72+
mine_block(block, difficulty=1)
73+
bc.add_block(block)
74+
return bc
75+
76+
async def test_run_node_loads_existing_sqlite_snapshot(self):
77+
chain = self._chain_with_tx()
78+
save(chain, self.tmpdir)
79+
80+
async def fake_cli_loop(sk, pk, loaded_chain, mempool, network):
81+
self.assertEqual(len(loaded_chain.chain), len(chain.chain))
82+
self.assertEqual(loaded_chain.last_block.hash, chain.last_block.hash)
83+
self.assertEqual(loaded_chain.state.accounts, chain.state.accounts)
84+
85+
with patch.object(main_module, "P2PNetwork", FakeNetwork), patch.object(
86+
main_module, "cli_loop", fake_cli_loop
87+
):
88+
await main_module.run_node(
89+
port=9400,
90+
host="127.0.0.1",
91+
connect_to=None,
92+
fund=0,
93+
datadir=self.tmpdir,
94+
)
95+
96+
async def test_run_node_saves_sqlite_snapshot_on_shutdown(self):
97+
fixed_sk, fixed_pk = _make_keypair()
98+
99+
async def fake_cli_loop(sk, pk, chain, mempool, network):
100+
self.assertEqual(pk, fixed_pk)
101+
self.assertEqual(chain.state.get_account(pk)["balance"], 25)
102+
103+
with patch.object(main_module, "P2PNetwork", FakeNetwork), patch.object(
104+
main_module, "cli_loop", fake_cli_loop
105+
), patch.object(main_module, "create_wallet", return_value=(fixed_sk, fixed_pk)):
106+
await main_module.run_node(
107+
port=9401,
108+
host="127.0.0.1",
109+
connect_to=None,
110+
fund=25,
111+
datadir=self.tmpdir,
112+
)
113+
114+
restored = load(self.tmpdir)
115+
self.assertEqual(restored.state.get_account(fixed_pk)["balance"], 25)
116+
self.assertEqual(len(restored.chain), 1)
117+
118+
119+
if __name__ == "__main__":
120+
unittest.main()

0 commit comments

Comments
 (0)