• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nette / tester / 22278741586

22 Feb 2026 02:09PM UTC coverage: 81.835%. First build
22278741586

Pull #467

github

web-flow
Merge b74a1f536 into 29a5403e0
Pull Request #467: Feat/structural metrics v2

79 of 107 new or added lines in 18 files covered. (73.83%)

1757 of 2147 relevant lines covered (81.84%)

0.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Framework/HttpAssert.php
1
<?php
2

3
/**
4
 * This file is part of the Nette Tester.
5
 * Copyright (c) 2009 David Grudl (https://davidgrudl.com)
6
 */
7

8
declare(strict_types=1);
9

10
namespace Tester;
11

12
use function curl_error, curl_exec, curl_getinfo, curl_init, curl_setopt, explode, is_int, is_string, rtrim, str_contains, strtoupper, substr, trim;
13

14

15
/**
16
 * HTTP testing helpers.
17
 */
18
class HttpAssert
19
{
20
        private function __construct(
21
                private string $body,
×
22
                private int $code,
×
23
                /** @var array<string, string> */
24
                private array $headers,
×
25
        ) {
26
        }
27

28

29
        /**
30
         * Creates HTTP request, executes it and returns HttpTest instance for chaining expectations.
31
         * @param  string[]  $headers  headers as 'Name: Value' strings or name => value pairs
32
         * @param  array<string, string>  $cookies  cookie name => value pairs
33
         */
34
        public static function fetch(
35
                string $url,
36
                string $method = 'GET',
37
                array $headers = [],
38
                array $cookies = [],
39
                bool $follow = false,
40
                ?string $body = null,
41
        ): self
42
        {
NEW
43
                $ch = curl_init($url);
×
44
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
×
45
                curl_setopt($ch, CURLOPT_HEADER, true);
×
46
                curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $follow);
×
NEW
47
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method) ?: 'GET');
×
48

49
                if ($headers) {
×
50
                        $headerList = [];
×
51
                        foreach ($headers as $key => $value) {
×
52
                                if (is_int($key)) {
×
53
                                        $headerList[] = $value;
×
54
                                } else {
55
                                        $headerList[] = "$key: $value";
×
56
                                }
57
                        }
58
                        curl_setopt($ch, CURLOPT_HTTPHEADER, $headerList);
×
59
                }
60

61
                if ($body !== null) {
×
62
                        curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
×
63
                }
64

65
                if ($cookies) {
×
NEW
66
                        $pairs = [];
×
67
                        foreach ($cookies as $name => $value) {
×
NEW
68
                                $pairs[] = "$name=$value";
×
69
                        }
NEW
70
                        curl_setopt($ch, CURLOPT_COOKIE, implode('; ', $pairs));
×
71
                }
72

73
                $response = curl_exec($ch);
×
NEW
74
                if (!is_string($response)) {
×
75
                        throw new \Exception('HTTP request failed: ' . curl_error($ch));
×
76
                }
77

78
                $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
×
79
                $res = new self(
×
80
                        substr($response, $headerSize),
×
81
                        curl_getinfo($ch, CURLINFO_HTTP_CODE),
×
82
                        [],
×
83
                );
84

85
                $headerString = substr($response, 0, $headerSize);
×
86
                foreach (explode("\r\n", $headerString) as $line) {
×
87
                        if (str_contains($line, ':')) {
×
88
                                [$name, $value] = explode(':', $line, 2);
×
89
                                $res->headers[strtolower(trim($name))] = trim($value);
×
90
                        }
91
                }
92

93
                return $res;
×
94
        }
95

96

97
        /**
98
         * Asserts HTTP response code matches expectation.
99
         * @param int|\Closure(int): bool  $expected
100
         */
101
        public function expectCode(int|\Closure $expected): self
102
        {
103
                if ($expected instanceof \Closure) {
×
104
                        Assert::true($expected($this->code), 'HTTP status code validation failed');
×
105
                } else {
106
                        Assert::same($expected, $this->code, 'HTTP status code validation failed');
×
107
                }
108

109
                return $this;
×
110
        }
111

112

113
        /**
114
         * Asserts HTTP response code does not match expectation.
115
         * @param int|\Closure(int): bool  $expected
116
         */
117
        public function denyCode(int|\Closure $expected): self
118
        {
119
                if ($expected instanceof \Closure) {
×
120
                        Assert::false($expected($this->code), 'HTTP status code validation failed');
×
121
                } else {
122
                        Assert::notSame($expected, $this->code, 'HTTP status code validation failed');
×
123
                }
124

125
                return $this;
×
126
        }
127

128

129
        /**
130
         * Asserts HTTP response header matches expectation.
131
         * @param string|\Closure(string): bool|null  $expected
132
         */
133
        public function expectHeader(
134
                string $name,
135
                string|\Closure|null $expected = null,
136
                ?string $contains = null,
137
                ?string $matches = null,
138
        ): self
139
        {
140
                $headerValue = $this->headers[strtolower($name)] ?? null;
×
141
                if (!isset($headerValue)) {
×
142
                        Assert::fail("Header '$name' should exist");
×
143
                } elseif (is_string($expected)) {
×
144
                        Assert::same($expected, $headerValue, "Header '$name' validation failed");
×
145
                } elseif ($expected instanceof \Closure) {
×
146
                        Assert::true($expected($headerValue), "Header '$name' validation failed");
×
147
                } elseif ($contains !== null) {
×
148
                        Assert::contains($contains, $headerValue, "Header '$name' validation failed");
×
149
                } elseif ($matches !== null) {
×
150
                        Assert::match($matches, $headerValue, "Header '$name' validation failed");
×
151
                }
152

153
                return $this;
×
154
        }
155

156

157
        /**
158
         * Asserts HTTP response header does not match expectation.
159
         * @param string|\Closure(string): bool|null  $expected
160
         */
161
        public function denyHeader(
162
                string $name,
163
                string|\Closure|null $expected = null,
164
                ?string $contains = null,
165
                ?string $matches = null,
166
        ): self
167
        {
168
                $headerValue = $this->headers[strtolower($name)] ?? null;
×
169
                if (!isset($headerValue)) {
×
170
                        return $this;
×
171
                }
172

173
                if (is_string($expected)) {
×
174
                        Assert::notSame($expected, $headerValue, "Header '$name' validation failed");
×
175
                } elseif ($expected instanceof \Closure) {
×
176
                        Assert::falsey($expected($headerValue), "Header '$name' validation failed");
×
177
                } elseif ($contains !== null) {
×
178
                        Assert::notContains($contains, $headerValue, "Header '$name' validation failed");
×
179
                } elseif ($matches !== null) {
×
180
                        Assert::notMatch($matches, $headerValue, "Header '$name' validation failed");
×
181
                } else {
182
                        Assert::fail("Header '$name' should not exist");
×
183
                }
184

185
                return $this;
×
186
        }
187

188

189
        /**
190
         * Asserts HTTP response body matches expectation.
191
         * @param string|\Closure(string): bool|null  $expected
192
         */
193
        public function expectBody(
194
                string|\Closure|null $expected = null,
195
                ?string $contains = null,
196
                ?string $matches = null,
197
        ): self
198
        {
199
                if (is_string($expected)) {
×
200
                        Assert::same($expected, $this->body, 'Body validation failed');
×
201
                } elseif ($expected instanceof \Closure) {
×
202
                        Assert::true($expected($this->body), 'Body validation failed');
×
203
                } elseif ($contains !== null) {
×
204
                        Assert::contains($contains, $this->body, 'Body validation failed');
×
205
                } elseif ($matches !== null) {
×
206
                        Assert::match($matches, $this->body, 'Body validation failed');
×
207
                }
208

209
                return $this;
×
210
        }
211

212

213
        /**
214
         * Asserts HTTP response body does not match expectation.
215
         * @param string|\Closure(string): bool|null  $expected
216
         */
217
        public function denyBody(
218
                string|\Closure|null $expected = null,
219
                ?string $contains = null,
220
                ?string $matches = null,
221
        ): self
222
        {
223
                if (is_string($expected)) {
×
224
                        Assert::notSame($expected, $this->body, 'Body validation failed');
×
225
                } elseif ($expected instanceof \Closure) {
×
226
                        Assert::falsey($expected($this->body), 'Body validation failed');
×
227
                } elseif ($contains !== null) {
×
228
                        Assert::notContains($contains, $this->body, 'Body validation failed');
×
229
                } elseif ($matches !== null) {
×
230
                        Assert::notMatch($matches, $this->body, 'Body validation failed');
×
231
                }
232
                return $this;
×
233
        }
234
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc