Skip to content

Commit 9b54e39

Browse files
enaplesdaywalker90
authored andcommitted
pytest: test new features and improvements of currencyrate plugin
1 parent 203a71f commit 9b54e39

1 file changed

Lines changed: 130 additions & 81 deletions

File tree

tests/test_currencyrate.py

Lines changed: 130 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@
1111

1212
LOGGER = logging.getLogger(__name__)
1313

14+
ALL_RESOURCES = [
15+
"bitstamp",
16+
"coinbase",
17+
"coingecko",
18+
"kraken",
19+
"blockchain.info",
20+
"coindesk",
21+
"binance",
22+
]
23+
1424

1525
def median(rateslist):
1626
rates = [entry["amount"] for entry in rateslist]
@@ -127,15 +137,7 @@ def test_apis_batch2(node_factory):
127137

128138
def test_custom_source(node_factory):
129139
opts = {
130-
"currencyrate-disable-source": [
131-
"bitstamp",
132-
"coinbase",
133-
"coingecko",
134-
"kraken",
135-
"blockchain.info",
136-
"coindesk",
137-
"binance",
138-
],
140+
"currencyrate-disable-source": ALL_RESOURCES,
139141
"currencyrate-add-source": [
140142
r"my-coingecko,https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies={currency_lc},bitcoin,{currency_lc}",
141143
r"my-kraken,https://api.kraken.com/0/public/Ticker?pair=XXBTZ{currency},result,XXBTZ{currency},c,0",
@@ -179,15 +181,7 @@ def test_custom_source(node_factory):
179181

180182
def test_no_sources(node_factory):
181183
opts = {
182-
"currencyrate-disable-source": [
183-
"bitstamp",
184-
"coinbase",
185-
"coingecko",
186-
"kraken",
187-
"blockchain.info",
188-
"coindesk",
189-
"binance",
190-
],
184+
"currencyrate-disable-source": ALL_RESOURCES,
191185
}
192186
l1 = node_factory.get_node(options=opts)
193187

@@ -239,6 +233,9 @@ def fake_rateserver():
239233
"fast": 100_000_000,
240234
"slow": 50_000_000,
241235
"slow_delay": 1,
236+
"too_high": 50_000.0116,
237+
"too_low": 50_000.0114,
238+
"midpoint": 50_000.0115,
242239
}
243240

244241
@app.get("/fast")
@@ -250,6 +247,18 @@ def slow():
250247
time.sleep(state["slow_delay"])
251248
return jsonify({"price": state["slow"]})
252249

250+
@app.get("/too_high")
251+
def too_high():
252+
return jsonify({"price": state["too_high"]})
253+
254+
@app.get("/too_low")
255+
def too_low():
256+
return jsonify({"price": state["too_low"]})
257+
258+
@app.get("/midpoint")
259+
def midpoint():
260+
return jsonify({"price": state["midpoint"]})
261+
253262
srv = _ServerThread(app)
254263
srv.start()
255264
try:
@@ -265,15 +274,7 @@ def slow():
265274
def test_cached_median(node_factory, fake_rateserver):
266275
"""This should use the median of available sources"""
267276
opts = {
268-
"currencyrate-disable-source": [
269-
"bitstamp",
270-
"coinbase",
271-
"coingecko",
272-
"kraken",
273-
"blockchain.info",
274-
"coindesk",
275-
"binance",
276-
],
277+
"currencyrate-disable-source": ALL_RESOURCES,
277278
"currencyrate-add-source": [
278279
f"fast,{fake_rateserver['url']}/fast,price",
279280
f"slow,{fake_rateserver['url']}/slow,price",
@@ -302,15 +303,7 @@ def test_cached_median(node_factory, fake_rateserver):
302303

303304
def test_bkpr_listaccountevents_currencyrate(node_factory, fake_rateserver):
304305
opts = {
305-
"currencyrate-disable-source": [
306-
"bitstamp",
307-
"coinbase",
308-
"coingecko",
309-
"kraken",
310-
"blockchain.info",
311-
"coindesk",
312-
"binance",
313-
],
306+
"currencyrate-disable-source": ALL_RESOURCES,
314307
"currencyrate-add-source": [
315308
f"fast,{fake_rateserver['url']}/fast,price",
316309
f"slow,{fake_rateserver['url']}/slow,price",
@@ -333,15 +326,7 @@ def test_bkpr_listaccountevents_currencyrate(node_factory, fake_rateserver):
333326
def test_bkpr_listaccountevents_realtime(node_factory, fake_rateserver):
334327
"""Make sure we don't wait for bkpr command to look up rates!"""
335328
opts = {
336-
"currencyrate-disable-source": [
337-
"bitstamp",
338-
"coinbase",
339-
"coingecko",
340-
"kraken",
341-
"blockchain.info",
342-
"coindesk",
343-
"binance",
344-
],
329+
"currencyrate-disable-source": ALL_RESOURCES,
345330
"currencyrate-add-source": [
346331
f"fast,{fake_rateserver['url']}/fast,price",
347332
f"slow,{fake_rateserver['url']}/slow,price",
@@ -372,15 +357,7 @@ def test_bkpr_listaccountevents_realtime(node_factory, fake_rateserver):
372357

373358
def test_bkpr_currency_dynamic(node_factory, fake_rateserver):
374359
opts = {
375-
"currencyrate-disable-source": [
376-
"bitstamp",
377-
"coinbase",
378-
"coingecko",
379-
"kraken",
380-
"blockchain.info",
381-
"coindesk",
382-
"binance",
383-
],
360+
"currencyrate-disable-source": ALL_RESOURCES,
384361
"currencyrate-add-source": [
385362
f"fast,{fake_rateserver['url']}/fast,price",
386363
f"slow,{fake_rateserver['url']}/slow,price",
@@ -434,15 +411,7 @@ def test_bkpr_currency_dynamic(node_factory, fake_rateserver):
434411

435412
def test_bkpr_currencyrate_persisted(node_factory, fake_rateserver):
436413
opts = {
437-
"currencyrate-disable-source": [
438-
"bitstamp",
439-
"coinbase",
440-
"coingecko",
441-
"kraken",
442-
"blockchain.info",
443-
"coindesk",
444-
"binance",
445-
],
414+
"currencyrate-disable-source": ALL_RESOURCES,
446415
"currencyrate-add-source": [
447416
f"fast,{fake_rateserver['url']}/fast,price",
448417
f"slow,{fake_rateserver['url']}/slow,price",
@@ -504,15 +473,7 @@ def test_bkpr_currencyrate_persisted(node_factory, fake_rateserver):
504473

505474
def test_bkpr_currencyrate_warns_for_old_events(node_factory, fake_rateserver):
506475
opts = {
507-
"currencyrate-disable-source": [
508-
"bitstamp",
509-
"coinbase",
510-
"coingecko",
511-
"kraken",
512-
"blockchain.info",
513-
"coindesk",
514-
"binance",
515-
],
476+
"currencyrate-disable-source": ALL_RESOURCES,
516477
"currencyrate-add-source": [
517478
f"fast,{fake_rateserver['url']}/fast,price",
518479
f"slow,{fake_rateserver['url']}/slow,price",
@@ -568,15 +529,7 @@ def test_bkpr_currencyrate_warns_for_old_events(node_factory, fake_rateserver):
568529

569530
def test_bkpr_currencyrate_ranges(node_factory, fake_rateserver):
570531
opts = {
571-
"currencyrate-disable-source": [
572-
"bitstamp",
573-
"coinbase",
574-
"coingecko",
575-
"kraken",
576-
"blockchain.info",
577-
"coindesk",
578-
"binance",
579-
],
532+
"currencyrate-disable-source": ALL_RESOURCES,
580533
"currencyrate-add-source": [
581534
f"fast,{fake_rateserver['url']}/fast,price",
582535
f"slow,{fake_rateserver['url']}/slow,price",
@@ -628,3 +581,99 @@ def test_bkpr_currencyrate_ranges(node_factory, fake_rateserver):
628581
# We will load them fine on restart, too.
629582
l1.restart()
630583
assert l1.rpc.bkpr_listaccountevents() == events
584+
585+
586+
def test_currencyrate_rounding(node_factory, fake_rateserver):
587+
"""Test currencyrate returns at most 3 decimal places (ISO 4217)."""
588+
589+
cases = [
590+
("too_high", f"{fake_rateserver['url']}/too_high", 50_000.012),
591+
("too_low", f"{fake_rateserver['url']}/too_low", 50_000.011),
592+
("midpoint", f"{fake_rateserver['url']}/midpoint", 50_000.012),
593+
]
594+
595+
for source_name, url, expected in cases:
596+
opts = {
597+
"currencyrate-disable-source": ALL_RESOURCES,
598+
"currencyrate-add-source": [f"{source_name},{url},price"],
599+
}
600+
l1 = node_factory.get_node(options=opts)
601+
602+
result = l1.rpc.currencyrate("USD")
603+
LOGGER.info("currencyrate rounding [%s]: %s", source_name, result)
604+
rate = result["rate"]
605+
606+
decimal_places = len(f"{rate:.10f}".split(".")[1].rstrip("0"))
607+
assert decimal_places <= 3, (
608+
f"[{source_name}] Expected at most 3 decimal places, "
609+
f"got {rate!r} ({decimal_places} decimal places)"
610+
)
611+
assert abs(rate - expected) < 1e-9, (
612+
f"[{source_name}] Expected {expected} after rounding, got {rate!r}"
613+
)
614+
615+
l1.stop()
616+
617+
618+
def test_currencyrate_source(node_factory, fake_rateserver):
619+
"""Test currencyrate with a source argument returns that source's rate."""
620+
621+
opts = {
622+
"currencyrate-disable-source": ALL_RESOURCES,
623+
"currencyrate-add-source": [
624+
f"fast,{fake_rateserver['url']}/fast,price",
625+
f"slow,{fake_rateserver['url']}/slow,price",
626+
],
627+
}
628+
l1 = node_factory.get_node(options=opts)
629+
630+
result_fast = l1.rpc.call("currencyrate", ["USD", "fast"])
631+
LOGGER.info("currencyrate fast: %s", result_fast)
632+
assert result_fast["rate"] == float(fake_rateserver["state"]["fast"])
633+
634+
result_slow = l1.rpc.call("currencyrate", ["USD", "slow"])
635+
LOGGER.info("currencyrate slow: %s", result_slow)
636+
assert result_slow["rate"] == float(fake_rateserver["state"]["slow"])
637+
638+
expected_median = (
639+
fake_rateserver["state"]["fast"] + fake_rateserver["state"]["slow"]
640+
) / 2
641+
result_median = l1.rpc.call("currencyrate", ["USD"])
642+
LOGGER.info("currencyrate median: %s", result_median)
643+
assert result_median["rate"] == float(expected_median)
644+
645+
646+
def test_currencyrate_unknown_source(node_factory, fake_rateserver):
647+
"""Test currencyrate with a non-existent source name returns an error."""
648+
649+
opts = {
650+
"currencyrate-disable-source": ALL_RESOURCES,
651+
"currencyrate-add-source": [
652+
f"fast,{fake_rateserver['url']}/fast,price",
653+
],
654+
}
655+
l1 = node_factory.get_node(options=opts)
656+
657+
with pytest.raises(RpcError, match="Unknown source `nonexistent`"):
658+
l1.rpc.call("currencyrate", ["USD", "nonexistent"])
659+
660+
661+
def test_currencyrate_too_many_args(node_factory, fake_rateserver):
662+
"""Test that all three RPC calls reject extra positional arguments."""
663+
664+
opts = {
665+
"currencyrate-disable-source": ALL_RESOURCES,
666+
"currencyrate-add-source": [
667+
f"fast,{fake_rateserver['url']}/fast,price",
668+
],
669+
}
670+
l1 = node_factory.get_node(options=opts)
671+
672+
with pytest.raises(RpcError, match="Too many arguments"):
673+
l1.rpc.call("currencyrate", ["USD", "fast", "extra_arg"])
674+
675+
with pytest.raises(RpcError, match="Too many arguments"):
676+
l1.rpc.call("listcurrencyrates", ["USD", "extra_arg"])
677+
678+
with pytest.raises(RpcError, match="Too many arguments"):
679+
l1.rpc.call("currencyconvert", [100, "USD", "extra_arg"])

0 commit comments

Comments
 (0)