diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..8a06361 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,24 @@ +name: Tests + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + name: pytest + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Run tests + run: docker compose -f tests/docker-compose.yml run tests + + - name: Cleanup Docker containers + if: always() + run: docker compose -f tests/docker-compose.yml down -v diff --git a/.gitignore b/.gitignore index 7e2ff25..6c3efd2 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ test_wallets .github/instructions/codacy.instructions.md .idea .codacy +.coverage.info diff --git a/README.md b/README.md index e74c2a9..4219329 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ For convenience, native libraries for Linux, macOS, and Windows are distributed cmake .. make ``` -6. Or build and install monero-python with pip: `pip3 install . --break-system-packages` +6. Or build and install monero-python with pip: `pip3 install . --break-system-packages` and copy libmonero-cpp.so to `/usr/lib/` ### Linux Docker Build 1. Install [Docker](https://docs.docker.com/engine/install/) diff --git a/tests/Dockerfile b/tests/Dockerfile new file mode 100644 index 0000000..d072052 --- /dev/null +++ b/tests/Dockerfile @@ -0,0 +1,57 @@ +FROM ubuntu:24.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y \ + build-essential cmake pkg-config libssl-dev libzmq3-dev libunbound-dev libsodium-dev \ + libunwind8-dev liblzma-dev libreadline6-dev libexpat1-dev libpgm-dev \ + qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev \ + libprotobuf-dev protobuf-compiler libudev-dev libzstd-dev \ + libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev \ + libboost-locale-dev libboost-program-options-dev libboost-regex-dev \ + libboost-serialization-dev libboost-system-dev libboost-thread-dev \ + python3 python3-pip python3-all python3-pybind11 python3-pytest \ + ccache doxygen graphviz git curl autoconf libtool gperf nettle-dev libevent-dev \ + debhelper bison flex wget \ + && rm -rf /var/lib/apt/lists/* + +RUN wget https://github.com/libexpat/libexpat/releases/download/R_2_4_8/expat-2.4.8.tar.bz2 \ + && tar -xf expat-2.4.8.tar.bz2 \ + && cd expat-2.4.8 \ + && ./configure --enable-static --disable-shared \ + && make -j$(nproc) && make install \ + && cd .. && rm -rf expat-2.4.8* + +RUN wget https://www.nlnetlabs.nl/downloads/unbound/unbound-1.22.0.tar.gz \ + && tar xzf unbound-1.22.0.tar.gz \ + && cd unbound-1.22.0 \ + && ./configure --with-libexpat=/usr --with-ssl=/usr --enable-static-exe \ + && make -j$(nproc) && make install \ + && cd .. && rm -rf unbound-1.22.0* + +RUN git config --global --add safe.directory '*' + +WORKDIR /monero-python + +COPY . . + +RUN cd ./external/monero-cpp/external/monero-project/ \ + && git submodule update --init --force \ + && mkdir -p build/release \ + && cd build/release \ + && cmake -DSTATIC=ON -DBUILD_64=ON -DCMAKE_BUILD_TYPE=Release ../../ \ + && make wallet cryptonote_protocol \ + && cd ../../../../../../ + +RUN cd ./external/monero-cpp/ \ + && mkdir -p build \ + && cd build \ + && cmake .. \ + && cmake --build . \ + && make . \ + && cp libmonero-cpp* /usr/lib/ \ + && cd ../../../ + +RUN pip3 install . --break-system-packages + +ENTRYPOINT ["./entrypoint.sh"] diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml new file mode 100644 index 0000000..5587d6d --- /dev/null +++ b/tests/docker-compose.yml @@ -0,0 +1,118 @@ +services: + + tests: + build: + context: .. + dockerfile: tests/Dockerfile + environment: + XMR_DAEMON_URI: http://node_2:18081 + XMR_WALLET_DOMAIN: http://xmr_wallet_1 + XMR_WALLET_1_URI: http://xmr_wallet_1:18082 + XMR_WALLET_2_URI: http://xmr_wallet_2:18083 + TESTS_INCONTAINER: "true" + depends_on: + - xmr_wallet_1 + - xmr_wallet_2 + extra_hosts: + - "tests:127.0.0.1" + volumes: + - ../coverage:/coverage + + dev: + image: ubuntu:24.04 + container_name: dev + command: [ "/bin/sh", "-c", "trap : TERM INT; while :; do echo Ready to code and debug like a rockstar!!!; sleep 2073600; done & wait" ] + depends_on: + - node_2 + - xmr_wallet_1 + - xmr_wallet_2 + + node_1: + image: btcpayserver/monero:0.18.4.2 + container_name: node_1 + command: [ + "monerod", + "--fixed-difficulty=200", + "--log-level=2", + "--p2p-bind-ip=0.0.0.0", + "--p2p-bind-port=48080", + "--rpc-bind-port=18089", + "--rpc-bind-ip=0.0.0.0", + "--confirm-external-bind", + "--rpc-access-control-origins=*", + "--add-exclusive-node=node_2:18080", + "--regtest", + "--no-igd", + "--hide-my-port", + "--no-zmq", + "--max-connections-per-ip=100", + "--rpc-max-connections-per-private-ip=100", + "--start-mining=42U9v3qs5CjZEePHBZHwuSckQXebuZu299NSmVEmQ41YJZQhKcPyujyMSzpDH4VMMVSBo3U3b54JaNvQLwAjqDhKS3rvM3L", + "--mining-threads=1", + "--non-interactive" + ] + volumes: + - xmr_node_1_data:/data + ports: + - "48080:48080" + - "18089:18089" + + node_2: + image: btcpayserver/monero:0.18.4.2 + container_name: node_2 + command: [ + "monerod", + "--fixed-difficulty=200", + "--log-level=2", + "--p2p-bind-ip=0.0.0.0", + "--p2p-bind-port=18080", + "--rpc-bind-ip=0.0.0.0", + "--confirm-external-bind", + "--rpc-bind-port=18081", + "--rpc-access-control-origins=*", + "--add-exclusive-node=node_1:48080", + "--regtest", + "--no-igd", + "--hide-my-port", + "--no-zmq", + "--max-connections-per-ip=100", + "--rpc-max-connections-per-private-ip=100", + "--non-interactive" + ] + volumes: + - xmr_node_2_data:/data + ports: + - "18080:18080" + - "18081:18081" + depends_on: + - node_1 + + xmr_wallet_1: + image: btcpayserver/monero:0.18.4.2 + container_name: xmr_wallet_1 + command: monero-wallet-rpc --log-level 2 --allow-mismatched-daemon-version --rpc-bind-ip=0.0.0.0 --disable-rpc-login --confirm-external-bind --rpc-bind-port=18082 --non-interactive --trusted-daemon --daemon-address=node_2:18081 --wallet-dir=/wallet --rpc-access-control-origins=* --tx-notify="/bin/sh ./scripts/notifier.sh -k -X GET https://host.docker.internal:14142/monerolikedaemoncallback/tx?cryptoCode=xmr&hash=%s" + ports: + - "18082:18082" + volumes: + - xmr_wallet_1_data:/wallet + depends_on: + - node_1 + - node_2 + + xmr_wallet_2: + image: btcpayserver/monero:0.18.4.2 + container_name: xmr_wallet_2 + command: monero-wallet-rpc --log-level 2 --allow-mismatched-daemon-version --rpc-bind-ip=0.0.0.0 --disable-rpc-login --confirm-external-bind --rpc-bind-port=18083 --non-interactive --trusted-daemon --daemon-address=node_2:18081 --wallet-dir=/wallet --rpc-access-control-origins=* --tx-notify="/bin/sh ./scripts/notifier.sh -k -X GET https://host.docker.internal:14142/monerolikedaemoncallback/tx?cryptoCode=xmr&hash=%s" + ports: + - "18083:18083" + volumes: + - xmr_wallet_2_data:/wallet + depends_on: + - node_1 + - node_2 + +volumes: + xmr_node_1_data: + xmr_node_2_data: + xmr_wallet_1_data: + xmr_wallet_2_data: diff --git a/tests/entrypoint.sh b/tests/entrypoint.sh new file mode 100644 index 0000000..93a7045 --- /dev/null +++ b/tests/entrypoint.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -e + +pytest --color=yes diff --git a/tests/utils/monero_test_utils.py b/tests/utils/monero_test_utils.py index f0799c8..c74388d 100644 --- a/tests/utils/monero_test_utils.py +++ b/tests/utils/monero_test_utils.py @@ -3,7 +3,7 @@ from random import choices, shuffle from time import sleep, time from os.path import exists as path_exists -from os import makedirs +from os import makedirs, environ from monero import ( MoneroNetworkType, MoneroTx, MoneroUtils, MoneroWalletFull, MoneroRpcConnection, MoneroWalletConfig, MoneroDaemonRpc, MoneroWalletRpc, MoneroBlockHeader, MoneroBlockTemplate, @@ -29,7 +29,7 @@ class MoneroTestUtils(ABC): _WALLET_KEYS: Optional[MoneroWalletKeys] = None _WALLET_RPC: Optional[MoneroWalletRpc] = None _DAEMON_RPC: Optional[MoneroDaemonRpc] = None - DAEMON_RPC_URI: str = "localhost:28081" + DAEMON_RPC_URI: str = environ.get('XMR_DAEMON_URI', "localhost:28081") """monero daemon rpc endpoint configuration (change per your configuration)""" DAEMON_RPC_USERNAME: str = "" DAEMON_RPC_PASSWORD: str = "" @@ -49,7 +49,7 @@ class MoneroTestUtils(ABC): WALLET_RPC_USERNAME: str = "rpc_user" WALLET_RPC_PASSWORD: str = "abc123" WALLET_RPC_ZMQ_DOMAIN: str = "127.0.0.1" - WALLET_RPC_DOMAIN: str = "localhost" + WALLET_RPC_DOMAIN: str = environ.get('XMR_WALLET_DOMAIN', "localhost") WALLET_RPC_URI = WALLET_RPC_DOMAIN + ":" + str(WALLET_RPC_PORT_START) WALLET_RPC_ZMQ_URI = "tcp:#" + WALLET_RPC_ZMQ_DOMAIN + ":" + str(WALLET_RPC_ZMQ_PORT_START) WALLET_RPC_LOCAL_PATH = MONERO_BINS_DIR + "/monero-wallet-rpc" @@ -458,9 +458,7 @@ def test_block_header(cls, header: MoneroBlockHeader, is_full: bool): else: assert header.nonce is not None cls.assert_true(header.nonce > 0) - cls.assert_is_none(header.pow_hash) # never seen defined - if is_full: assert header.size is not None assert header.depth is not None @@ -748,7 +746,6 @@ def test_info(cls, info: MoneroDaemonInfo): assert info.block_weight_limit is not None assert info.block_weight_median is not None assert info.database_size is not None - cls.assert_not_none(info.version) cls.assert_true(info.num_alt_blocks >= 0) cls.assert_true(info.block_size_limit > 0)