Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
- name: Spin up containers
run: docker compose -f tests/docker-compose.yml up -d
env:
MONERO_VERSION: "0.18.4.0"
MONERO_VERSION: "0.18.4.4"

- name: Install PHP
uses: shivammathur/setup-php@v2
Expand Down
25 changes: 25 additions & 0 deletions tests/Attribute/RequiresMoneroVersion.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace RefRing\MoneroRpcPhp\Tests\Attribute;

use Attribute;

/**
* Mark a test method as requiring a specific Monero daemon version.
*
* The test will be skipped if the daemon version is below the minimum
* or above the maximum (if specified).
*
* Version format: "0.18.4.0" (as returned by getInfo()->version)
*/
#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)]
final class RequiresMoneroVersion
{
public function __construct(
public readonly string $minVersion,
public readonly ?string $maxVersion = null,
) {
}
}
70 changes: 70 additions & 0 deletions tests/Trait/RequiresMoneroVersionTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

declare(strict_types=1);

namespace RefRing\MoneroRpcPhp\Tests\Trait;

use ReflectionMethod;
use RefRing\MoneroRpcPhp\DaemonRpcClient;
use RefRing\MoneroRpcPhp\Tests\Attribute\RequiresMoneroVersion;

/**
* Trait to add version requirement checking to integration tests.
*
* Classes using this trait must implement getDaemonRpcClient() to provide
* the client instance for version checking.
*/
trait RequiresMoneroVersionTrait
{
private static ?string $cachedDaemonVersion = null;

abstract protected static function getDaemonRpcClient(): DaemonRpcClient;

protected function checkMoneroVersionRequirements(): void
{
$reflection = new ReflectionMethod($this, $this->name());

// Check method-level attribute first, then class-level
$attributes = $reflection->getAttributes(RequiresMoneroVersion::class);

if (empty($attributes)) {
$classReflection = $reflection->getDeclaringClass();
$attributes = $classReflection->getAttributes(RequiresMoneroVersion::class);
}

if (empty($attributes)) {
return;
}

$requirement = $attributes[0]->newInstance();
$currentVersion = $this->getDaemonVersion();
$currentVersion = preg_replace('/[^0-9.]/', '', $currentVersion);

if (version_compare($currentVersion, $requirement->minVersion, '<')) {
$this->markTestSkipped(sprintf(
'Test requires Monero >= %s (current: %s)',
$requirement->minVersion,
$currentVersion
));
}

if ($requirement->maxVersion !== null) {
if (version_compare($currentVersion, $requirement->maxVersion, '>')) {
$this->markTestSkipped(sprintf(
'Test requires Monero <= %s (current: %s)',
$requirement->maxVersion,
$currentVersion
));
}
}
}

private function getDaemonVersion(): string
{
if (self::$cachedDaemonVersion === null) {
self::$cachedDaemonVersion = static::getDaemonRpcClient()->getInfo()->version;
}

return self::$cachedDaemonVersion;
}
}
8 changes: 4 additions & 4 deletions tests/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
monerod:
image: ghcr.io/refring/monero-containers/monerod:${MONERO_VERSION:-0.18.4.0}
image: ghcr.io/refring/monero-containers/monerod:${MONERO_VERSION:-0.18.4.4}
environment:
NETWORK: regtest
MONEROD_RPC_PORT: 18081
Expand All @@ -11,7 +11,7 @@ services:
- "18081:18081"

monero-wallet-rpc:
image: ghcr.io/refring/monero-containers/monero-wallet-rpc:${MONERO_VERSION:-0.18.4.0}
image: ghcr.io/refring/monero-containers/monero-wallet-rpc:${MONERO_VERSION:-0.18.4.4}
environment:
MONERO_DAEMON_ADDRESS: monerod:18081
MONERO_DAEMON_HOST: monerod:18081
Expand All @@ -22,7 +22,7 @@ services:
- "18083:18083"

monerod-rpc-authentication:
image: ghcr.io/refring/monero-containers/monerod:${MONERO_VERSION:-0.18.4.0}
image: ghcr.io/refring/monero-containers/monerod:${MONERO_VERSION:-0.18.4.4}
depends_on:
- "monerod"
ports:
Expand All @@ -38,7 +38,7 @@ services:
--fixed-difficulty 1

monero-wallet-rpc-authentication:
image: ghcr.io/refring/monero-containers/monero-wallet-rpc:${MONERO_VERSION:-0.18.4.0}
image: ghcr.io/refring/monero-containers/monero-wallet-rpc:${MONERO_VERSION:-0.18.4.4}
depends_on:
- "monerod"
ports:
Expand Down
30 changes: 22 additions & 8 deletions tests/integration/DaemonTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@
use RefRing\MoneroRpcPhp\Enum\ResponseStatus;
use RefRing\MoneroRpcPhp\Exception\InvalidHostOrSubnetException;
use RefRing\MoneroRpcPhp\Exception\NoIpOrHostSuppliedException;
use RefRing\MoneroRpcPhp\Tests\Attribute\RequiresMoneroVersion;
use RefRing\MoneroRpcPhp\Tests\Trait\RequiresMoneroVersionTrait;

final class DaemonTest extends TestCase
{
use RequiresMoneroVersionTrait;

private static DaemonRpcClient $rpcClient;

public static function setUpBeforeClass(): void
Expand All @@ -22,6 +26,16 @@ public static function setUpBeforeClass(): void
self::$rpcClient = new DaemonRpcClient($httpClient, getenv('DAEMON_RPC_URL'));
}

protected function setUp(): void
{
$this->checkMoneroVersionRequirements();
}

protected static function getDaemonRpcClient(): DaemonRpcClient
{
return self::$rpcClient;
}

public function testSetBansOk(): void
{
$node = new Node('127.0.0.1', null, true, 60);
Expand All @@ -37,12 +51,12 @@ public function testSetBansInvalidHost(): void
$result = self::$rpcClient->setBans([$node]);
}

// @TODO This will fail on versions before v0.18.4.0, find a way to only run tests against certain versions
// public function testSetBansMissingIp(): void
// {
// $this->expectException(NoIpOrHostSuppliedException::class);
//
// $node = new Node(null, null, true, 60);
// $result = self::$rpcClient->setBans([$node]);
// }
#[RequiresMoneroVersion('0.18.4.0')]
public function testSetBansMissingIp(): void
{
$this->expectException(NoIpOrHostSuppliedException::class);

$node = new Node(null, null, true, 60);
$result = self::$rpcClient->setBans([$node]);
}
}
35 changes: 35 additions & 0 deletions tests/unit/RequiresMoneroVersionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace RefRing\MoneroRpcPhp\Tests\unit;

use PHPUnit\Framework\TestCase;
use RefRing\MoneroRpcPhp\Tests\Attribute\RequiresMoneroVersion;

final class RequiresMoneroVersionTest extends TestCase
{
public function testAttributeInstantiation(): void
{
$attr = new RequiresMoneroVersion('0.18.4.0');
$this->assertSame('0.18.4.0', $attr->minVersion);
$this->assertNull($attr->maxVersion);
}

public function testAttributeInstantiationWithMax(): void
{
$attr = new RequiresMoneroVersion('0.18.0.0', '0.19.0.0');
$this->assertSame('0.18.0.0', $attr->minVersion);
$this->assertSame('0.19.0.0', $attr->maxVersion);
}

public function testVersionCompareWorks(): void
{
// Verify PHP's version_compare works correctly with Monero version strings
$this->assertSame(-1, version_compare('0.18.3.0', '0.18.4.0'));
$this->assertSame(0, version_compare('0.18.4.0', '0.18.4.0'));
$this->assertSame(1, version_compare('0.18.5.0', '0.18.4.0'));
$this->assertSame(-1, version_compare('0.17.3.2', '0.18.4.0'));
$this->assertSame(1, version_compare('0.19.0.0', '0.18.4.0'));
}
}