HttpTransportTest::testConstructor()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 1
eloc 6
c 3
b 0
f 0
nc 1
nop 0
dl 0
loc 13
rs 10
1
<?php
2
declare(strict_types=1);
3
4
/*
5
 * This file is part of the php-gelf package.
6
 *
7
 * (c) Benjamin Zikarsky <http://benjamin-zikarsky.de>
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
namespace Gelf\Test\Transport;
14
15
use Gelf\Encoder\CompressedJsonEncoder;
16
use Gelf\Encoder\EncoderInterface;
17
use Gelf\MessageInterface;
18
use Gelf\Transport\HttpTransport;
19
use Gelf\Transport\SslOptions;
20
use Gelf\Transport\StreamSocketClient;
21
use InvalidArgumentException;
22
use PHPUnit\Framework\MockObject\MockObject;
23
use PHPUnit\Framework\TestCase;
24
use ReflectionObject;
25
use RuntimeException;
26
27
class HttpTransportTest extends TestCase
28
{
29
    private MockObject|StreamSocketClient $socketClient;
30
    private MockObject|MessageInterface $message;
31
    private MockObject|EncoderInterface $encoder;
32
    
33
    private HttpTransport $transport;
34
    private string $testMessage;
35
36
    public function setUp(): void
37
    {
38
        $this->testMessage = str_repeat("0123456789", 30); // 300 char string
39
40
        $this->socketClient = $this->createMock(StreamSocketClient::class);
41
        $this->message = $this->createMock(MessageInterface::class);
42
43
        // create an encoder always return $testMessage
44
        $this->encoder = $this->createMock(EncoderInterface::class);
45
        $this->encoder->expects($this->any())->method('encode')->will(
46
            $this->returnValue($this->testMessage)
47
        );
48
49
        $this->transport = $this->getTransport();
50
    }
51
52
    private function getTransport(): HttpTransport
53
    {
54
        // initialize transport with an unlimited packet-size
55
        // and the mocked message encoder
56
        $transport = new HttpTransport();
57
        $transport->setMessageEncoder($this->encoder);
58
59
        // replace internal stream socket client with our mock
60
        $reflectedTransport = new ReflectionObject($transport);
61
        $reflectedClient = $reflectedTransport->getProperty('socketClient');
62
        $reflectedClient->setAccessible(true);
63
64
        $reflectedClient->setValue($transport, $this->socketClient);
65
66
        return $transport;
67
    }
68
69
    public function testConstructor(): void
70
    {
71
        $transport = new HttpTransport();
72
        $this->validateTransport($transport, '127.0.0.1', 12202, '/gelf');
73
74
        $transport = new HttpTransport('test.local', 80, '');
75
        $this->validateTransport($transport, 'test.local', 80, '');
76
77
        // test defaults:
78
        //   path NULL                                   => path: /gelf
79
        //   port 443 without explicit SSL options       => sslOptions: default
80
        $transport = new HttpTransport('localhost', 443);
81
        $this->validateTransport($transport, 'localhost', 443, '/gelf', new SslOptions());
82
    }
83
84
    public function testFromUrlConstructorInvalidUri(): void
85
    {
86
        self::expectException(InvalidArgumentException::class);
0 ignored issues
show
Bug Best Practice introduced by
The method PHPUnit\Framework\TestCase::expectException() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

86
        self::/** @scrutinizer ignore-call */ 
87
              expectException(InvalidArgumentException::class);
Loading history...
87
        HttpTransport::fromUrl('-://:-');
88
    }
89
90
    public function testFromUrlConstructorInvalidScheme(): void
91
    {
92
        self::expectException(InvalidArgumentException::class);
0 ignored issues
show
Bug Best Practice introduced by
The method PHPUnit\Framework\TestCase::expectException() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

92
        self::/** @scrutinizer ignore-call */ 
93
              expectException(InvalidArgumentException::class);
Loading history...
93
        HttpTransport::fromUrl('ftp://foobar');
94
    }
95
96
    public function testFromUrlConstructor()
97
    {
98
        $transport = HttpTransport::fromUrl('HTTP://localhost');
99
        $this->validateTransport($transport, 'localhost', 80, '', null, null);
100
101
        $transport = HttpTransport::fromUrl('http://localhost:1234');
102
        $this->validateTransport($transport, 'localhost', 1234, '', null, null);
103
        
104
        $transport = HttpTransport::fromUrl('http://localhost/abc');
105
        $this->validateTransport($transport, 'localhost', 80, '/abc', null, null);
106
        
107
        $transport = HttpTransport::fromUrl('http://localhost:1234/abc');
108
        $this->validateTransport($transport, 'localhost', 1234, '/abc', null, null);
109
110
        $transport = HttpTransport::fromUrl('http://user@localhost');
111
        $this->validateTransport($transport, 'localhost', 80, '', null, 'user:');
112
113
        $transport = HttpTransport::fromUrl('http://user:pass@localhost');
114
        $this->validateTransport($transport, 'localhost', 80, '', null, 'user:pass');
115
116
        $transport = HttpTransport::fromUrl('https://localhost');
117
        $this->validateTransport($transport, 'localhost', 443, '', new SslOptions(), null);
118
        
119
        $sslOptions = new SslOptions();
120
        $sslOptions->setVerifyPeer(false);
121
        $transport = HttpTransport::fromUrl('HTTPS://localhost', $sslOptions);
122
        $this->validateTransport($transport, 'localhost', 443, '', $sslOptions, null);
123
    }
124
125
    public function validateTransport(
126
        HttpTransport $transport,
127
        $host,
128
        $port,
129
        $path,
130
        $sslOptions = null,
131
        $authentication = null
132
    ): void {
133
        $r = new ReflectionObject($transport);
134
135
        $testProperties = array(
136
            'host' => $host,
137
            'port' => $port,
138
            'path' => $path,
139
            'sslOptions' => $sslOptions,
140
            'authentication' => $authentication
141
        );
142
143
        foreach ($testProperties as $property => $value) {
144
            $p = $r->getProperty($property);
145
            $p->setAccessible(true);
146
            self::assertEquals($value, $p->getValue($transport));
147
        }
148
    }
149
150
    public function testSslOptionsAreUsed(): void
151
    {
152
        $sslOptions = $this->createMock(SslOptions::class);
153
        $sslOptions->expects($this->exactly(2))
154
            ->method('toStreamContext')
155
            ->will($this->returnValue(array('ssl' => null)));
156
157
        $transport = new HttpTransport("localhost", 12345, "/gelf", $sslOptions);
158
159
        $reflectedTransport = new ReflectionObject($transport);
160
        $reflectedGetContext = $reflectedTransport->getMethod('getContext');
161
        $reflectedGetContext->setAccessible(true);
162
        $context = $reflectedGetContext->invoke($transport);
163
164
        self::assertEquals(array('ssl' => null), $context);
165
    }
166
167
    public function testSetEncoder(): void
168
    {
169
        $encoder = $this->createMock(EncoderInterface::class);
170
        $this->transport->setMessageEncoder($encoder);
171
172
        self::assertEquals($encoder, $this->transport->getMessageEncoder());
173
    }
174
175
    public function testEmptyResponseException(): void
176
    {
177
        self::expectException(RuntimeException::class);
0 ignored issues
show
Bug Best Practice introduced by
The method PHPUnit\Framework\TestCase::expectException() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

177
        self::/** @scrutinizer ignore-call */ 
178
              expectException(RuntimeException::class);
Loading history...
178
        self::expectExceptionMessage(
0 ignored issues
show
Bug Best Practice introduced by
The method PHPUnit\Framework\TestCa...xpectExceptionMessage() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

178
        self::/** @scrutinizer ignore-call */ 
179
              expectExceptionMessage(
Loading history...
179
            "Graylog-Server didn't answer properly, expected 'HTTP/1.x 202 Accepted', response is ''"
180
        );
181
182
        $this->transport->send($this->message);
183
    }
184
185
    public function testSendUncompressed(): void
186
    {
187
        $request = "POST /gelf HTTP/1.1"."\r\n"
188
                 . "Host: 127.0.0.1:12202"."\r\n"
189
                 . "Content-Length: 300"."\r\n"
190
                 . "Content-Type: application/json"."\r\n"
191
                 . "Connection: Keep-Alive"."\r\n"
192
                 . "Accept: */*"."\r\n"
193
                 . "\r\n"
194
                 . $this->testMessage;
195
196
        $this->socketClient
197
            ->expects($this->once())
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Gelf\Transport\StreamSocketClient. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

197
            ->/** @scrutinizer ignore-call */ 
198
              expects($this->once())

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
198
            ->method("write")
199
            ->with($request);
200
201
        $this->socketClient
202
            ->expects($this->once())
203
            ->method("read")
204
            ->will($this->returnValue("HTTP/1.1 202 Accepted\r\n\r\n"));
205
206
        $this->transport->send($this->message);
207
    }
208
209
    public function testAuthentication(): void
210
    {
211
        $this->transport->setAuthentication("test", "test");
212
213
        $this->socketClient->expects($this->once())
214
            ->method("write")
215
            ->will($this->returnCallback(function ($data) {
216
                self::assertStringContainsString("Authorization: Basic " . base64_encode("test:test"), $data);
217
                return 1;
218
            }));
219
220
221
222
        $this->socketClient
223
            ->expects($this->once())
224
            ->method("read")
225
            ->will($this->returnValue("HTTP/1.1 202 Accepted\r\n\r\n"));
226
227
        $this->transport->send($this->message);
228
    }
229
230
    public function testProxy(): void
231
    {
232
        $this->socketClient->expects($this->once())
233
            ->method("setContext")
234
            ->willReturnCallback(function (array $context) {
235
                self::assertArrayHasKey("http", $context);
236
                self::assertEquals(
237
                    array(
238
                        "proxy" => "tcp://proxy.example.com:5100",
239
                        "request_fulluri" => true
240
                    ),
241
                    $context["http"]
242
                );
243
            });
244
245
        $this->transport->setProxy("tcp://proxy.example.com:5100", true);
246
    }
247
248
    public function testSendCompressed(): void
249
    {
250
        $request = "POST /gelf HTTP/1.1"."\r\n"
251
                 . "Host: 127.0.0.1:12202"."\r\n"
252
                 . "Content-Length: 300"."\r\n"
253
                 . "Content-Type: application/json"."\r\n"
254
                 . "Connection: Keep-Alive"."\r\n"
255
                 . "Accept: */*"."\r\n"
256
                 . "Content-Encoding: gzip"."\r\n"
257
                 . ""."\r\n"
258
                 . $this->testMessage;
259
260
        $this->socketClient
261
            ->expects($this->once())
262
            ->method("write")
263
            ->with($request);
264
265
        $this->socketClient
266
            ->expects($this->once())
267
            ->method("read")
268
            ->will($this->returnValue("HTTP/1.1 202 Accepted\r\n\r\n"));
269
270
        $compressedEncoder = $this->createMock(CompressedJsonEncoder::class);
271
        $compressedEncoder
272
            ->expects($this->any())
273
            ->method('encode')
274
            ->will(
275
                $this->returnValue($this->testMessage)
276
            );
277
        $this->transport->setMessageEncoder($compressedEncoder);
278
279
        $this->transport->send($this->message);
280
    }
281
282
    public function testCloseSocketOnHttpOneZero(): void
283
    {
284
        $this->socketClient
285
            ->expects($this->once())
286
            ->method("read")
287
            ->will($this->returnValue("HTTP/1.0 202 Accepted\r\n\r\n"));
288
289
        $this->socketClient
290
            ->expects($this->once())
291
            ->method("close");
292
293
        $this->transport->send($this->message);
294
    }
295
296
    public function testCloseSocketOnConnectionClose(): void
297
    {
298
        $this->socketClient
299
            ->expects($this->once())
300
            ->method("read")
301
            ->will($this->returnValue("HTTP/1.1 202 Accepted\r\nConnection: Close\r\n\r\n"));
302
303
        $this->socketClient
304
            ->expects($this->once())
305
            ->method("close");
306
307
        $this->transport->send($this->message);
308
    }
309
310
    public function testConnectTimeout(): void
311
    {
312
        $this->socketClient
313
            ->expects($this->once())
314
            ->method('getConnectTimeout')
315
            ->will($this->returnValue(123));
316
317
        self::assertEquals(123, $this->transport->getConnectTimeout());
318
319
        $this->socketClient
320
            ->expects($this->once())
321
            ->method('setConnectTimeout')
322
            ->with(123);
323
324
        $this->transport->setConnectTimeout(123);
325
    }
326
}
327