|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
| 3 | +import importlib.metadata |
3 | 4 | import json |
4 | 5 | import re |
5 | 6 | import shutil |
@@ -729,6 +730,100 @@ def builder() -> TransactionBuilder: |
729 | 730 | assert _rpc(server.port(), 'getLatestLedger', {})['result']['sequence'] == 4 |
730 | 731 |
|
731 | 732 |
|
| 733 | +def test_get_version_info(server: StellarRpcServer) -> None: |
| 734 | + """getVersionInfo returns exactly the five spec fields with the right JSON types. |
| 735 | +
|
| 736 | + Real stellar-rpc (protocol 22+) emits camelCase keys only; the deprecated snake_case |
| 737 | + aliases (``commit_hash``, ...) were removed, so an exact key-set check covers both the |
| 738 | + required fields and the absence of the legacy ones. ``protocolVersion`` is a Go uint32, |
| 739 | + i.e. a JSON number, not a string. |
| 740 | + """ |
| 741 | + resp = _rpc(server.port(), 'getVersionInfo', {}) |
| 742 | + assert 'error' not in resp, resp |
| 743 | + result = resp['result'] |
| 744 | + |
| 745 | + assert set(result) == {'version', 'commitHash', 'buildTimestamp', 'captiveCoreVersion', 'protocolVersion'} |
| 746 | + # komet-node reports its own package version as the RPC server version. |
| 747 | + assert result['version'] == importlib.metadata.version('komet-node') |
| 748 | + assert type(result['commitHash']) is str |
| 749 | + assert type(result['buildTimestamp']) is str |
| 750 | + assert type(result['captiveCoreVersion']) is str |
| 751 | + assert type(result['protocolVersion']) is int # `is int` also rejects booleans |
| 752 | + assert result['protocolVersion'] == 22 |
| 753 | + |
| 754 | + |
| 755 | +def test_get_version_info_accepts_omitted_params(server: StellarRpcServer) -> None: |
| 756 | + """getVersionInfo takes no parameters; a request without a params member must succeed.""" |
| 757 | + resp = _post(server.port(), b'{"jsonrpc": "2.0", "id": 1, "method": "getVersionInfo"}') |
| 758 | + assert 'error' not in resp, resp |
| 759 | + assert resp['result']['protocolVersion'] == 22 |
| 760 | + |
| 761 | + |
| 762 | +# Every FeeDistribution field except ledgerCount is an unsigned integer serialised with Go's |
| 763 | +# `,string` option, i.e. a JSON string holding a decimal number (see the getFeeStats spec |
| 764 | +# example: `"transactionCount": "10"` but `"ledgerCount": 50`). |
| 765 | +_FEE_DISTRIBUTION_STRING_FIELDS = ( |
| 766 | + 'max', |
| 767 | + 'min', |
| 768 | + 'mode', |
| 769 | + 'p10', |
| 770 | + 'p20', |
| 771 | + 'p30', |
| 772 | + 'p40', |
| 773 | + 'p50', |
| 774 | + 'p60', |
| 775 | + 'p70', |
| 776 | + 'p80', |
| 777 | + 'p90', |
| 778 | + 'p95', |
| 779 | + 'p99', |
| 780 | + 'transactionCount', |
| 781 | +) |
| 782 | + |
| 783 | + |
| 784 | +def _assert_fee_distribution(dist: dict[str, Any]) -> None: |
| 785 | + """Check one FeeDistribution object against the stellar-rpc wire format.""" |
| 786 | + assert set(dist) == {*_FEE_DISTRIBUTION_STRING_FIELDS, 'ledgerCount'} |
| 787 | + for field in _FEE_DISTRIBUTION_STRING_FIELDS: |
| 788 | + value = dist[field] |
| 789 | + assert type(value) is str, f'{field} must be a JSON string, got {type(value).__name__}' |
| 790 | + assert value.isdigit(), f'{field} must hold a decimal number, got {value!r}' |
| 791 | + assert type(dist['ledgerCount']) is int, 'ledgerCount must be a JSON number' |
| 792 | + # The distribution must at least be internally consistent. |
| 793 | + assert int(dist['min']) <= int(dist['p50']) <= int(dist['max']) |
| 794 | + |
| 795 | + |
| 796 | +def test_get_fee_stats(server: StellarRpcServer) -> None: |
| 797 | + """getFeeStats returns both fee distributions and latestLedger with the right JSON types.""" |
| 798 | + resp = _rpc(server.port(), 'getFeeStats', {}) |
| 799 | + assert 'error' not in resp, resp |
| 800 | + result = resp['result'] |
| 801 | + |
| 802 | + assert set(result) == {'sorobanInclusionFee', 'inclusionFee', 'latestLedger'} |
| 803 | + _assert_fee_distribution(result['sorobanInclusionFee']) |
| 804 | + _assert_fee_distribution(result['inclusionFee']) |
| 805 | + assert type(result['latestLedger']) is int # a JSON number, and 0 on a fresh chain |
| 806 | + assert result['latestLedger'] == 0 |
| 807 | + |
| 808 | + |
| 809 | +def test_get_fee_stats_latest_ledger_tracks_chain(server: StellarRpcServer) -> None: |
| 810 | + """getFeeStats reports the live ledger sequence, not a constant.""" |
| 811 | + keypair = Keypair.random() |
| 812 | + account = Account(keypair.public_key, sequence=0) |
| 813 | + envelope = ( |
| 814 | + TransactionBuilder(account, Network.TESTNET_NETWORK_PASSPHRASE) |
| 815 | + .append_create_account_op(destination=keypair.public_key, starting_balance='1000') |
| 816 | + .set_timeout(30) |
| 817 | + .build() |
| 818 | + ) |
| 819 | + envelope.sign(keypair) |
| 820 | + assert _rpc(server.port(), 'sendTransaction', {'transaction': envelope.to_xdr()})['result']['status'] == 'PENDING' |
| 821 | + |
| 822 | + resp = _rpc(server.port(), 'getFeeStats', {}) |
| 823 | + assert 'error' not in resp, resp |
| 824 | + assert resp['result']['latestLedger'] == 1 |
| 825 | + |
| 826 | + |
732 | 827 | # ---------------------------------------------------------------------- |
733 | 828 | # xdrFormat parameter (getTransaction / sendTransaction) |
734 | 829 | # |
|
0 commit comments