1<?php
2namespace CorsSlim\Tests;
3
4class CorsSlimRouteTest extends \PHPUnit_Framework_TestCase {
5    public function setUp() {
6        ob_start();
7    }
8    public function tearDown() {
9        ob_end_clean();
10    }
11
12    private function runApp($action, $actionName, $mwOptions = NULL, $headers = array()) {
13        \Slim\Environment::mock(array(
14            'REQUEST_METHOD' => 'GET',
15            'SERVER_NAME' => 'localhost',
16            'SERVER_PORT' => 80,
17            'ACCEPT' => 'application/json',
18            'SCRIPT_NAME' => '/index.php',
19            'PATH_INFO' => '/'. $actionName
20        ));
21        $app = new \Slim\Slim();
22        $app->setName($actionName);
23
24        $mw = function() {
25            // Do nothing
26        };
27        if (isset($mwOptions)) {
28            if (is_callable($mwOptions)) {
29                $mw = $mwOptions;
30            }
31            else {
32                $mwOptions['appName'] = $actionName;
33                $mw = \CorsSlim\CorsSlim::routeMiddleware($mwOptions);
34            }
35        }
36
37        $app->get('/:name', $mw, function ($name) use ($app, $action) {
38            if ($app->request->isHead()) {
39                $app->status(204);
40                return;
41            }
42
43            $app->contentType('application/json');
44            $app->response->write(json_encode(array(
45                                                "action" => $action,
46                                                "method" => "GET",
47                                                "name" => $name
48                                                )
49                                            )
50                                    );
51        });
52
53        foreach ($headers as $key => $value) {
54            $app->request->headers()->set($key, $value);
55        }
56
57        $app->run();
58
59        $this->validate($app, 'GET', $action, $actionName);
60
61        return $app;
62    }
63
64    private function runAppHead($action, $actionName, $mwOptions = NULL, $headers = array()) {
65        \Slim\Environment::mock(array(
66            'REQUEST_METHOD' => 'HEAD',
67            'SERVER_NAME' => 'localhost',
68            'SERVER_PORT' => 80,
69            'ACCEPT' => 'application/json',
70            'SCRIPT_NAME' => '/index.php',
71            'PATH_INFO' => '/'. $actionName
72        ));
73        $app = new \Slim\Slim();
74        $app->setName($actionName);
75
76        $mw = function() {
77            // Do nothing
78        };
79        if (isset($mwOptions)) {
80            if (is_callable($mwOptions)) {
81                $mw = $mwOptions;
82            }
83            else {
84                $mwOptions['appName'] = $actionName;
85                $mw = \CorsSlim\CorsSlim::routeMiddleware($mwOptions);
86            }
87        }
88
89        $app->get('/:name', $mw, function ($name) use ($app, $action) {
90            if ($app->request->isHead()) {
91                $app->status(204);
92                return;
93            }
94
95            $app->contentType('application/json');
96            $app->response->write(json_encode(array(
97                                                "action" => $action,
98                                                "method" => "GET",
99                                                "name" => $name
100                                                )
101                                            )
102                                    );
103        });
104
105        foreach ($headers as $key => $value) {
106            $app->request->headers()->set($key, $value);
107        }
108
109        $app->run();
110
111        $this->assertEquals(204, $app->response()->status());
112
113        return $app;
114    }
115
116    private function runAppPost($action, $actionName, $mwOptions = NULL, $headers = array()) {
117        \Slim\Environment::mock(array(
118            'REQUEST_METHOD' => 'POST',
119            'SERVER_NAME' => 'localhost',
120            'SERVER_PORT' => 80,
121            'ACCEPT' => 'application/json',
122            'SCRIPT_NAME' => '/index.php',
123            'PATH_INFO' => '/'. $actionName
124        ));
125        $app = new \Slim\Slim();
126        $app->setName($actionName);
127
128        $mw = function() {
129            // Do nothing
130        };
131        if (isset($mwOptions)) {
132            if (is_callable($mwOptions)) {
133                $mw = $mwOptions;
134            }
135            else {
136                $mwOptions['appName'] = $actionName;
137                $mw = \CorsSlim\CorsSlim::routeMiddleware($mwOptions);
138            }
139        }
140
141        $app->post('/:name', $mw, function ($name) use ($app, $action) {
142            if ($app->request->isHead()) {
143                $app->status(204);
144                return;
145            }
146
147            $app->contentType('application/json');
148            $app->response->write(json_encode(array(
149                                                "action" => $action,
150                                                "method" => "POST",
151                                                "name" => $name
152                                                )
153                                            )
154                                    );
155        });
156
157        foreach ($headers as $key => $value) {
158            $app->request->headers()->set($key, $value);
159        }
160
161        $app->run();
162
163        $this->validate($app, 'POST', $action, $actionName);
164
165        return $app;
166    }
167
168    private function runAppPreFlight($action, $actionName, $mwOptions = NULL, $headers = array()) {
169        \Slim\Environment::mock(array(
170            'REQUEST_METHOD' => 'OPTIONS',
171            'SERVER_NAME' => 'localhost',
172            'SERVER_PORT' => 80,
173            'ACCEPT' => 'application/json',
174            'SCRIPT_NAME' => '/index.php',
175            'PATH_INFO' => '/'. $actionName
176        ));
177        $app = new \Slim\Slim();
178        $app->setName($actionName);
179
180        $mw = function() {
181            // Do nothing
182        };
183        if (isset($mwOptions)) {
184            if (is_callable($mwOptions)) {
185                $mw = $mwOptions;
186            }
187            else {
188                $mwOptions['appName'] = $actionName;
189                $mw = \CorsSlim\CorsSlim::routeMiddleware($mwOptions);
190            }
191        }
192
193        $app->options('/:name', $mw, function ($name) use ($app, $action) {
194        });
195
196        $app->delete('/:name', $mw, function ($name) use ($app, $action) {
197            if ($app->request->isHead()) {
198                $app->status(204);
199                return;
200            }
201
202
203            $app->contentType('application/json');
204            $app->response->write(json_encode(array(
205                                                "action" => $action,
206                                                "method" => "DELETE",
207                                                "name" => $name
208                                                )
209                                            )
210                                    );
211        });
212
213        foreach ($headers as $key => $value) {
214            $app->request->headers()->set($key, $value);
215        }
216
217        $app->run();
218
219        return $app;
220    }
221
222    private function validate($app, $method, $action, $name) {
223        $this->assertEquals(200, $app->response()->status());
224        $this->assertEquals("application/json", $app->response()->header("Content-Type"));
225
226        $content = json_decode($app->response()->body());
227        $this->assertEquals($action, $content->action);
228        $this->assertEquals($method, $content->method);
229        $this->assertEquals($name, $content->name);
230    }
231
232    public function testDefaultCors() {
233        $app = $this->runApp('cors', 'DefaultCors', array());
234        $this->assertEquals("*", $app->response()->header("Access-Control-Allow-Origin"));
235    }
236
237    public function testCorsOrigin() {
238        $app = $this->runApp('cors-origin', 'CorsOrigin', array("origin" => "*"));
239        $this->assertEquals("*", $app->response()->header("Access-Control-Allow-Origin"));
240    }
241
242    public function testCorsOriginSingle() {
243        $app = $this->runApp('cors-origin-single', 'CorsOriginSingle', array("origin" => "http://github.com", "appName" => "CorsOriginSingle"));
244        $this->assertEquals("http://github.com", $app->response()->header("Access-Control-Allow-Origin"));
245    }
246
247    public function testCorsOriginArray() {
248        $app = $this->runApp('cors-origin-array', 'CorsOriginArray', array("origin" => array("http://mozilla.com", "http://php.net", "http://github.com")));
249        $this->assertEquals("http://mozilla.com", $app->response()->header("Access-Control-Allow-Origin"));
250    }
251
252    public function testCorsOriginArraySpecific() {
253        $mwOptions = array("origin" => array("http://mozilla.com", "http://php.net", "http://github.com"));
254        $headers = array('origin' => 'http://php.net');
255        $app = $this->runApp('cors-origin-array-specific', 'CorsOriginArraySpecific', $mwOptions, $headers);
256        $this->assertEquals("http://php.net", $app->response()->header("Access-Control-Allow-Origin"));
257    }
258
259    public function testCorsOriginCallable() {
260        $mwOptions = array("origin" => function($reqOrigin) { return $reqOrigin;});
261        $headers = array('origin' => 'http://www.slimframework.com/');
262        $app = $this->runApp('cors-origin-callable', 'CorsOriginCallable', $mwOptions, $headers);
263        $this->assertEquals("http://www.slimframework.com/", $app->response()->header("Access-Control-Allow-Origin"));
264    }
265
266    // Simple Requests
267    public function testSimpleCorsRequestFail() {
268        $app = $this->runApp('cors', 'SimpleCorsRequestFail');
269        $this->assertNull($app->response()->header("Access-Control-Allow-Origin"));
270    }
271
272    public function testSimpleCorsRequest() {
273        $app = $this->runApp('cors', 'SimpleCorsRequest', array());
274        $this->assertEquals("*", $app->response()->header("Access-Control-Allow-Origin"));
275    }
276
277    public function testSimpleCorsRequestHeadFail() {
278        $app = $this->runAppHead('cors', 'SimpleCorsRequestHeadFail');
279        $this->assertNull($app->response()->header("Access-Control-Allow-Origin"));
280    }
281
282    public function testSimpleCorsRequestHead() {
283        $app = $this->runAppHead('cors', 'SimpleCorsRequestHead', array());
284        $this->assertEquals("*", $app->response()->header("Access-Control-Allow-Origin"));
285    }
286
287    public function testSimpleCorsRequestPostFail() {
288        $app = $this->runAppPost('cors', 'SimpleCorsRequestPostFail');
289        $this->assertNull($app->response()->header("Access-Control-Allow-Origin"));
290    }
291
292    public function testSimpleCorsRequestPost() {
293        $app = $this->runAppPost('cors', 'SimpleCorsRequestPost', array());
294        $this->assertEquals("*", $app->response()->header("Access-Control-Allow-Origin"));
295    }
296
297    // Complex Requests (With Pre-Flight)
298    public function testComplexCorsRequestPreFlightFail() {
299        $app = $this->runAppPreFlight('cors', 'ComplexCorsRequestPreFlightFail');
300        $this->assertEquals(200, $app->response()->status());
301        $this->assertNull($app->response()->header("Access-Control-Allow-Origin"));
302    }
303
304    public function testComplexCorsRequestPreFlight() {
305        $app = $this->runAppPreFlight('cors', 'ComplexCorsRequestPreFlight', array());
306        $this->assertEquals(200, $app->response()->status());
307        $this->assertEquals("*", $app->response()->header("Access-Control-Allow-Origin"));
308    }
309
310    // Access-Control-Expose-Headers
311    public function testAccessControlExposeHeaders() {
312        $app = $this->runApp('cors', 'SimpleCorsRequestAccessControlExposeHeaders', array('exposeHeaders' => 'X-My-Custom-Header'));
313        $this->assertEquals("X-My-Custom-Header", $app->response()->header("Access-Control-Expose-Headers"));
314    }
315
316    public function testAccessControlExposeHeadersArray() {
317        $app = $this->runApp('cors', 'SimpleCorsRequesAccessControlExposeHeadersArrayt', array('exposeHeaders' => array("X-My-Custom-Header", "X-Another-Custom-Header")));
318        $this->assertEquals("X-My-Custom-Header, X-Another-Custom-Header", $app->response()->header("Access-Control-Expose-Headers"));
319    }
320
321    // Access-Control-Max-Age
322    public function testAccessControlMaxAge() {
323        $app = $this->runAppPreFlight('cors', 'SimpleCorsRequestAccessControlMaxAge', array('maxAge' => 1728000));
324        $this->assertEquals(200, $app->response()->status());
325        $this->assertEquals(1728000, $app->response()->header("Access-Control-Max-Age"));
326    }
327
328    // Access-Control-Allow-Credentials
329    public function testAccessControlAllowCredentials() {
330        $app = $this->runApp('cors', 'SimpleCorsRequestAccessControlAllowCredentials', array('allowCredentials' => True));
331        $this->assertEquals("true", $app->response()->header("Access-Control-Allow-Credentials"));
332    }
333
334    // Access-Control-Allow-Methods
335    public function testAccessControlAllowMethods() {
336        $app = $this->runAppPreFlight('cors', 'SimpleCorsRequestAccessControlAllowMethods', array('allowMethods' => array('GET', 'POST')));
337        $this->assertEquals(200, $app->response()->status());
338        $this->assertEquals('GET, POST', $app->response()->header("Access-Control-Allow-Methods"));
339    }
340
341    // Access-Control-Allow-Headers
342    public function testAccessControlAllowHeaders() {
343        $app = $this->runAppPreFlight('cors', 'SimpleCorsRequestAccessControlAllowHeaders', array("allowHeaders" => array("X-PINGOTHER")));
344        $this->assertEquals(200, $app->response()->status());
345        $this->assertEquals('X-PINGOTHER', $app->response()->header("Access-Control-Allow-Headers"));
346    }
347}