Skip to content

Commit b52f618

Browse files
committed
Update tests and add MockResolver
1 parent d883ab7 commit b52f618

11 files changed

Lines changed: 335 additions & 359 deletions

composer.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@
3838
"bench:wrk2": "bash benchmarks/wrk2.sh",
3939
"bench:compare": "bash benchmarks/compare-http-servers.sh",
4040
"bench:compare-tcp": "bash benchmarks/compare-tcp-servers.sh",
41-
"test": "phpunit",
42-
"test:integration": "bash tests/integration/run.sh",
43-
"lint": "pint",
44-
"analyse": "phpstan analyse"
41+
"test": "phpunit --testsuite Unit",
42+
"test:integration": "phpunit --testsuite Integration",
43+
"test:all": "phpunit",
44+
"lint": "pint --test --config=pint.json",
45+
"format": "pint --config=pint.json",
46+
"check": "phpstan analyse --level=max src tests"
4547
},
4648
"config": {
4749
"php": "8.4",

tests/AdapterActionsTest.php

Lines changed: 71 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -3,157 +3,123 @@
33
namespace Utopia\Tests;
44

55
use PHPUnit\Framework\TestCase;
6-
use Utopia\Platform\Action;
76
use Utopia\Proxy\Adapter\HTTP\Swoole as HTTPAdapter;
87
use Utopia\Proxy\Adapter\SMTP\Swoole as SMTPAdapter;
98
use Utopia\Proxy\Adapter\TCP\Swoole as TCPAdapter;
10-
use Utopia\Proxy\Service\HTTP as HTTPService;
11-
use Utopia\Proxy\Service\SMTP as SMTPService;
12-
use Utopia\Proxy\Service\TCP as TCPService;
9+
use Utopia\Proxy\Resolver\Exception as ResolverException;
1310

1411
class AdapterActionsTest extends TestCase
1512
{
13+
protected MockResolver $resolver;
14+
1615
protected function setUp(): void
1716
{
18-
if (!\extension_loaded('swoole')) {
17+
if (! \extension_loaded('swoole')) {
1918
$this->markTestSkipped('ext-swoole is required to run adapter tests.');
2019
}
20+
21+
$this->resolver = new MockResolver();
2122
}
2223

23-
public function testDefaultServicesAreAssigned(): void
24+
public function test_resolver_is_assigned_to_adapters(): void
2425
{
25-
$http = new HTTPAdapter();
26-
$tcp = new TCPAdapter(port: 5432);
27-
$smtp = new SMTPAdapter();
26+
$http = new HTTPAdapter($this->resolver);
27+
$tcp = new TCPAdapter($this->resolver, port: 5432);
28+
$smtp = new SMTPAdapter($this->resolver);
2829

29-
$this->assertInstanceOf(HTTPService::class, $http->getService());
30-
$this->assertInstanceOf(TCPService::class, $tcp->getService());
31-
$this->assertInstanceOf(SMTPService::class, $smtp->getService());
30+
$this->assertSame($this->resolver, $http->getResolver());
31+
$this->assertSame($this->resolver, $tcp->getResolver());
32+
$this->assertSame($this->resolver, $smtp->getResolver());
3233
}
3334

34-
public function testResolveActionRoutesAndRunsLifecycleActions(): void
35+
public function test_resolve_routes_and_returns_endpoint(): void
3536
{
36-
$adapter = new HTTPAdapter();
37-
$service = new HTTPService();
38-
39-
$initHost = null;
40-
$shutdownEndpoint = null;
41-
42-
$service->addAction('resolve', (new class extends Action {})
43-
->callback(function (string $hostname): string {
44-
return "127.0.0.1:8080";
45-
}));
46-
47-
$service->addAction('beforeRoute', (new class extends Action {})
48-
->setType(Action::TYPE_INIT)
49-
->callback(function (string $hostname) use (&$initHost) {
50-
$initHost = $hostname;
51-
}));
52-
53-
$service->addAction('afterRoute', (new class extends Action {})
54-
->setType(Action::TYPE_SHUTDOWN)
55-
->callback(function (string $hostname, string $endpoint, $result) use (&$shutdownEndpoint) {
56-
$shutdownEndpoint = $endpoint;
57-
}));
58-
59-
$adapter->setService($service);
37+
$this->resolver->setEndpoint('127.0.0.1:8080');
38+
$adapter = new HTTPAdapter($this->resolver);
39+
$adapter->setSkipValidation(true);
6040

6141
$result = $adapter->route('api.example.com');
6242

6343
$this->assertSame('127.0.0.1:8080', $result->endpoint);
64-
$this->assertSame('api.example.com', $initHost);
65-
$this->assertSame('127.0.0.1:8080', $shutdownEndpoint);
44+
$this->assertSame('http', $result->protocol);
6645
}
6746

68-
public function testErrorActionRunsOnRoutingFailure(): void
47+
public function test_notify_connect_delegates_to_resolver(): void
6948
{
70-
$adapter = new HTTPAdapter();
71-
$service = new HTTPService();
72-
73-
$errorMessage = null;
74-
$errorHost = null;
75-
76-
$service->addAction('resolve', (new class extends Action {})
77-
->callback(function (string $hostname): string {
78-
throw new \Exception("No backend");
79-
}));
80-
81-
$service->addAction('onRoutingError', (new class extends Action {})
82-
->setType(Action::TYPE_ERROR)
83-
->callback(function (string $hostname, \Exception $e) use (&$errorMessage, &$errorHost) {
84-
$errorHost = $hostname;
85-
$errorMessage = $e->getMessage();
86-
}));
87-
88-
$adapter->setService($service);
89-
90-
try {
91-
$adapter->route('api.example.com');
92-
$this->fail('Expected routing error was not thrown.');
93-
} catch (\Exception $e) {
94-
$this->assertSame('No backend', $e->getMessage());
95-
}
49+
$adapter = new HTTPAdapter($this->resolver);
50+
51+
$adapter->notifyConnect('resource-123', ['extra' => 'data']);
9652

97-
$this->assertSame('api.example.com', $errorHost);
98-
$this->assertSame('No backend', $errorMessage);
53+
$connects = $this->resolver->getConnects();
54+
$this->assertCount(1, $connects);
55+
$this->assertSame('resource-123', $connects[0]['resourceId']);
56+
$this->assertSame(['extra' => 'data'], $connects[0]['metadata']);
9957
}
10058

101-
public function testMissingResolveActionThrows(): void
59+
public function test_notify_close_delegates_to_resolver(): void
10260
{
103-
$adapter = new HTTPAdapter();
104-
$adapter->setService(new HTTPService());
61+
$adapter = new HTTPAdapter($this->resolver);
10562

106-
$this->expectException(\Exception::class);
107-
$this->expectExceptionMessage('No resolve action registered');
63+
$adapter->notifyClose('resource-123', ['extra' => 'data']);
10864

109-
$adapter->route('api.example.com');
65+
$disconnects = $this->resolver->getDisconnects();
66+
$this->assertCount(1, $disconnects);
67+
$this->assertSame('resource-123', $disconnects[0]['resourceId']);
68+
$this->assertSame(['extra' => 'data'], $disconnects[0]['metadata']);
11069
}
11170

112-
public function testResolveActionRejectsEmptyEndpoint(): void
71+
public function test_track_activity_delegates_to_resolver_with_throttling(): void
11372
{
114-
$adapter = new HTTPAdapter();
115-
$service = new HTTPService();
73+
$adapter = new HTTPAdapter($this->resolver);
74+
$adapter->setActivityInterval(1); // 1 second throttle
11675

117-
$service->addAction('resolve', (new class extends Action {})
118-
->callback(function (string $hostname): string {
119-
return '';
120-
}));
76+
// First call should trigger activity tracking
77+
$adapter->trackActivity('resource-123');
78+
$this->assertCount(1, $this->resolver->getActivities());
12179

122-
$adapter->setService($service);
80+
// Immediate second call should be throttled
81+
$adapter->trackActivity('resource-123');
82+
$this->assertCount(1, $this->resolver->getActivities());
12383

124-
$this->expectException(\Exception::class);
125-
$this->expectExceptionMessage('Resolve action returned empty endpoint');
84+
// Wait for throttle interval to pass
85+
sleep(2);
12686

127-
$adapter->route('api.example.com');
87+
// Third call should trigger activity tracking
88+
$adapter->trackActivity('resource-123');
89+
$this->assertCount(2, $this->resolver->getActivities());
12890
}
12991

130-
public function testInitActionsRunInRegistrationOrder(): void
92+
public function test_routing_error_throws_exception(): void
13193
{
132-
$adapter = new HTTPAdapter();
133-
$service = new HTTPService();
94+
$this->resolver->setException(new ResolverException('No backend found'));
95+
$adapter = new HTTPAdapter($this->resolver);
13496

135-
$calls = [];
97+
$this->expectException(ResolverException::class);
98+
$this->expectExceptionMessage('No backend found');
13699

137-
$service->addAction('resolve', (new class extends Action {})
138-
->callback(function (string $hostname): string {
139-
return '127.0.0.1:8080';
140-
}));
100+
$adapter->route('api.example.com');
101+
}
141102

142-
$service->addAction('first', (new class extends Action {})
143-
->setType(Action::TYPE_INIT)
144-
->callback(function () use (&$calls) {
145-
$calls[] = 'first';
146-
}));
103+
public function test_empty_endpoint_throws_exception(): void
104+
{
105+
$this->resolver->setEndpoint('');
106+
$adapter = new HTTPAdapter($this->resolver);
147107

148-
$service->addAction('second', (new class extends Action {})
149-
->setType(Action::TYPE_INIT)
150-
->callback(function () use (&$calls) {
151-
$calls[] = 'second';
152-
}));
108+
$this->expectException(ResolverException::class);
109+
$this->expectExceptionMessage('Resolver returned empty endpoint');
153110

154-
$adapter->setService($service);
155111
$adapter->route('api.example.com');
112+
}
113+
114+
public function test_skip_validation_allows_private_i_ps(): void
115+
{
116+
// 10.0.0.1 is a private IP that would normally be blocked
117+
$this->resolver->setEndpoint('10.0.0.1:8080');
118+
$adapter = new HTTPAdapter($this->resolver);
119+
$adapter->setSkipValidation(true);
156120

157-
$this->assertSame(['first', 'second'], $calls);
121+
// Should not throw exception with validation disabled
122+
$result = $adapter->route('api.example.com');
123+
$this->assertSame('10.0.0.1:8080', $result->endpoint);
158124
}
159125
}

tests/AdapterMetadataTest.php

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,38 @@
99

1010
class AdapterMetadataTest extends TestCase
1111
{
12+
protected MockResolver $resolver;
13+
1214
protected function setUp(): void
1315
{
14-
if (!\extension_loaded('swoole')) {
16+
if (! \extension_loaded('swoole')) {
1517
$this->markTestSkipped('ext-swoole is required to run adapter tests.');
1618
}
19+
20+
$this->resolver = new MockResolver();
1721
}
1822

19-
public function testHttpAdapterMetadata(): void
23+
public function test_http_adapter_metadata(): void
2024
{
21-
$adapter = new HTTPAdapter();
25+
$adapter = new HTTPAdapter($this->resolver);
2226

2327
$this->assertSame('HTTP', $adapter->getName());
2428
$this->assertSame('http', $adapter->getProtocol());
2529
$this->assertSame('HTTP proxy adapter for routing requests to function containers', $adapter->getDescription());
2630
}
2731

28-
public function testSmtpAdapterMetadata(): void
32+
public function test_smtp_adapter_metadata(): void
2933
{
30-
$adapter = new SMTPAdapter();
34+
$adapter = new SMTPAdapter($this->resolver);
3135

3236
$this->assertSame('SMTP', $adapter->getName());
3337
$this->assertSame('smtp', $adapter->getProtocol());
3438
$this->assertSame('SMTP proxy adapter for email server routing', $adapter->getDescription());
3539
}
3640

37-
public function testTcpAdapterMetadata(): void
41+
public function test_tcp_adapter_metadata(): void
3842
{
39-
$adapter = new TCPAdapter(port: 5432);
43+
$adapter = new TCPAdapter($this->resolver, port: 5432);
4044

4145
$this->assertSame('TCP', $adapter->getName());
4246
$this->assertSame('postgresql', $adapter->getProtocol());

tests/AdapterStatsTest.php

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,27 @@
33
namespace Utopia\Tests;
44

55
use PHPUnit\Framework\TestCase;
6-
use Utopia\Platform\Action;
76
use Utopia\Proxy\Adapter\HTTP\Swoole as HTTPAdapter;
8-
use Utopia\Proxy\Service\HTTP as HTTPService;
7+
use Utopia\Proxy\Resolver\Exception as ResolverException;
98

109
class AdapterStatsTest extends TestCase
1110
{
11+
protected MockResolver $resolver;
12+
1213
protected function setUp(): void
1314
{
14-
if (!\extension_loaded('swoole')) {
15+
if (! \extension_loaded('swoole')) {
1516
$this->markTestSkipped('ext-swoole is required to run adapter tests.');
1617
}
18+
19+
$this->resolver = new MockResolver();
1720
}
1821

19-
public function testCacheHitUpdatesStats(): void
22+
public function test_cache_hit_updates_stats(): void
2023
{
21-
$adapter = new HTTPAdapter();
22-
$service = new HTTPService();
23-
24-
$service->addAction('resolve', (new class extends Action {})
25-
->callback(function (string $hostname): string {
26-
return '127.0.0.1:8080';
27-
}));
28-
29-
$adapter->setService($service);
24+
$this->resolver->setEndpoint('127.0.0.1:8080');
25+
$adapter = new HTTPAdapter($this->resolver);
26+
$adapter->setSkipValidation(true);
3027

3128
$start = time();
3229
while (time() === $start) {
@@ -49,22 +46,15 @@ public function testCacheHitUpdatesStats(): void
4946
$this->assertGreaterThan(0, $stats['routing_table_memory']);
5047
}
5148

52-
public function testRoutingErrorIncrementsStats(): void
49+
public function test_routing_error_increments_stats(): void
5350
{
54-
$adapter = new HTTPAdapter();
55-
$service = new HTTPService();
56-
57-
$service->addAction('resolve', (new class extends Action {})
58-
->callback(function (string $hostname): string {
59-
throw new \Exception('No backend');
60-
}));
61-
62-
$adapter->setService($service);
51+
$this->resolver->setException(new ResolverException('No backend'));
52+
$adapter = new HTTPAdapter($this->resolver);
6353

6454
try {
6555
$adapter->route('api.example.com');
6656
$this->fail('Expected routing error was not thrown.');
67-
} catch (\Exception $e) {
57+
} catch (ResolverException $e) {
6858
$this->assertSame('No backend', $e->getMessage());
6959
}
7060

@@ -74,4 +64,18 @@ public function testRoutingErrorIncrementsStats(): void
7464
$this->assertSame(0, $stats['cache_hits']);
7565
$this->assertSame(0.0, $stats['cache_hit_rate']);
7666
}
67+
68+
public function test_resolver_stats_are_included_in_adapter_stats(): void
69+
{
70+
$this->resolver->setEndpoint('127.0.0.1:8080');
71+
$adapter = new HTTPAdapter($this->resolver);
72+
$adapter->setSkipValidation(true);
73+
74+
$adapter->route('api.example.com');
75+
76+
$stats = $adapter->getStats();
77+
$this->assertArrayHasKey('resolver', $stats);
78+
$this->assertIsArray($stats['resolver']);
79+
$this->assertSame('mock', $stats['resolver']['resolver']);
80+
}
7781
}

tests/ConnectionResultTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
class ConnectionResultTest extends TestCase
99
{
10-
public function testConnectionResultStoresValues(): void
10+
public function test_connection_result_stores_values(): void
1111
{
1212
$result = new ConnectionResult(
1313
endpoint: '127.0.0.1:8080',

0 commit comments

Comments
 (0)