Completed
Push — master ( ba7b5b...e5a757 )
by Robbie
01:33
created

tests/unit/RestfulServerTest.php (16 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * 
4
 * @todo Test Relation getters
5
 * @todo Test filter and limit through GET params
6
 * @todo Test DELETE verb
7
 *
8
 */
9
class RestfulServerTest extends SapphireTest
10
{
11
    public static $fixture_file = 'RestfulServerTest.yml';
12
13
    protected $extraDataObjects = array(
14
        'RestfulServerTest_Comment',
15
        'RestfulServerTest_SecretThing',
16
        'RestfulServerTest_Page',
17
        'RestfulServerTest_Author',
18
        'RestfulServerTest_AuthorRating',
19
    );
20
21 View Code Duplication
    public function testApiAccess()
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
testApiAccess uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
22
    {
23
        $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
24
        $page1 = $this->objFromFixture('RestfulServerTest_Page', 'page1');
25
        
26
        // normal GET should succeed with $api_access enabled
27
        $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
28
        $response = Director::test($url, null, null, 'GET');
29
        $this->assertEquals($response->getStatusCode(), 200);
30
        
31
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
32
        $_SERVER['PHP_AUTH_PW'] = 'user';
33
        
34
        // even with logged in user a GET with $api_access disabled should fail
35
        $url = "/api/v1/RestfulServerTest_Page/" . $page1->ID;
36
        $response = Director::test($url, null, null, 'GET');
37
        $this->assertEquals($response->getStatusCode(), 401);
38
        
39
        unset($_SERVER['PHP_AUTH_USER']);
40
        unset($_SERVER['PHP_AUTH_PW']);
41
    }
42
    
43
    public function testApiAccessBoolean()
44
    {
45
        $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
46
        
47
        $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
48
        $response = Director::test($url, null, null, 'GET');
49
        $this->assertContains('<ID>', $response->getBody());
50
        $this->assertContains('<Name>', $response->getBody());
51
        $this->assertContains('<Comment>', $response->getBody());
52
        $this->assertContains('<Page', $response->getBody());
53
        $this->assertContains('<Author', $response->getBody());
54
    }
55
    
56 View Code Duplication
    public function testAuthenticatedGET()
0 ignored issues
show
testAuthenticatedGET uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
57
    {
58
        $thing1 = $this->objFromFixture('RestfulServerTest_SecretThing', 'thing1');
59
        $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
60
61
        // @todo create additional mock object with authenticated VIEW permissions
62
        $url = "/api/v1/RestfulServerTest_SecretThing/" . $thing1->ID;
63
        $response = Director::test($url, null, null, 'GET');
64
        $this->assertEquals($response->getStatusCode(), 401);
65
        
66
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
67
        $_SERVER['PHP_AUTH_PW'] = 'user';
68
        
69
        $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
70
        $response = Director::test($url, null, null, 'GET');
71
        $this->assertEquals($response->getStatusCode(), 200);
72
        
73
        unset($_SERVER['PHP_AUTH_USER']);
74
        unset($_SERVER['PHP_AUTH_PW']);
75
    }
76
    
77
    public function testAuthenticatedPUT()
0 ignored issues
show
testAuthenticatedPUT uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
78
    {
79
        $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
80
        
81
        $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
82
        $data = array('Comment' => 'created');
83
        
84
        $response = Director::test($url, $data, null, 'PUT');
85
        $this->assertEquals($response->getStatusCode(), 401); // Permission failure
86
87
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
88
        $_SERVER['PHP_AUTH_PW'] = 'editor';
89
        $response = Director::test($url, $data, null, 'PUT');
90
        $this->assertEquals($response->getStatusCode(), 200); // Success
91
92
        unset($_SERVER['PHP_AUTH_USER']);
93
        unset($_SERVER['PHP_AUTH_PW']);
94
    }
95
    
96
    public function testGETRelationshipsXML()
97
    {
98
        $author1 = $this->objFromFixture('RestfulServerTest_Author', 'author1');
99
        $rating1 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating1');
100
        $rating2 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating2');
101
        
102
        // @todo should be set up by fixtures, doesn't work for some reason...
103
        $author1->Ratings()->add($rating1);
104
        $author1->Ratings()->add($rating2);
105
        
106
        $url = "/api/v1/RestfulServerTest_Author/" . $author1->ID;
107
        $response = Director::test($url, null, null, 'GET');
108
        $this->assertEquals($response->getStatusCode(), 200);
109
    
110
        $responseArr = Convert::xml2array($response->getBody());
111
        $ratingsArr = $responseArr['Ratings']['RestfulServerTest_AuthorRating'];
112
        $this->assertEquals(count($ratingsArr), 2);
113
        $ratingIDs = array(
114
            (int)$ratingsArr[0]['@attributes']['id'],
115
            (int)$ratingsArr[1]['@attributes']['id']
116
        );
117
        $this->assertContains($rating1->ID, $ratingIDs);
118
        $this->assertContains($rating2->ID, $ratingIDs);
119
    }
120
    
121
    public function testGETManyManyRelationshipsXML()
122
    {
123
        // author4 has related authors author2 and author3
124
        $author2 = $this->objFromFixture('RestfulServerTest_Author', 'author2');
125
        $author3 = $this->objFromFixture('RestfulServerTest_Author', 'author3');
126
        $author4 = $this->objFromFixture('RestfulServerTest_Author', 'author4');
127
        
128
        $url = "/api/v1/RestfulServerTest_Author/" . $author4->ID . '/RelatedAuthors';
129
        $response = Director::test($url, null, null, 'GET');
130
        $this->assertEquals(200, $response->getStatusCode());
131
        $arr = Convert::xml2array($response->getBody());
132
        $authorsArr = $arr['RestfulServerTest_Author'];
133
        
134
        $this->assertEquals(count($authorsArr), 2);
135
        $ratingIDs = array(
136
            (int)$authorsArr[0]['ID'],
137
            (int)$authorsArr[1]['ID']
138
        );
139
        $this->assertContains($author2->ID, $ratingIDs);
140
        $this->assertContains($author3->ID, $ratingIDs);
141
    }
142
143
    public function testPUTWithFormEncoded()
0 ignored issues
show
testPUTWithFormEncoded uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
144
    {
145
        $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
146
        
147
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
148
        $_SERVER['PHP_AUTH_PW'] = 'editor';
149
    
150
        $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
151
        $body = 'Name=Updated Comment&Comment=updated';
152
        $headers = array(
153
            'Content-Type' => 'application/x-www-form-urlencoded'
154
        );
155
        $response = Director::test($url, null, null, 'PUT', $body, $headers);
156
        $this->assertEquals($response->getStatusCode(), 200); // Success
157
        // Assumption: XML is default output
158
        $responseArr = Convert::xml2array($response->getBody());
159
        $this->assertEquals($responseArr['ID'], $comment1->ID);
160
        $this->assertEquals($responseArr['Comment'], 'updated');
161
        $this->assertEquals($responseArr['Name'], 'Updated Comment');
162
    
163
        unset($_SERVER['PHP_AUTH_USER']);
164
        unset($_SERVER['PHP_AUTH_PW']);
165
    }
166
    
167
    public function testPOSTWithFormEncoded()
0 ignored issues
show
testPOSTWithFormEncoded uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
168
    {
169
        $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
170
        
171
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
172
        $_SERVER['PHP_AUTH_PW'] = 'editor';
173
    
174
        $url = "/api/v1/RestfulServerTest_Comment";
175
        $body = 'Name=New Comment&Comment=created';
176
        $headers = array(
177
            'Content-Type' => 'application/x-www-form-urlencoded'
178
        );
179
        $response = Director::test($url, null, null, 'POST', $body, $headers);
180
        $this->assertEquals($response->getStatusCode(), 201); // Created
181
        // Assumption: XML is default output
182
        $responseArr = Convert::xml2array($response->getBody());
183
        $this->assertTrue($responseArr['ID'] > 0);
184
        $this->assertNotEquals($responseArr['ID'], $comment1->ID);
185
        $this->assertEquals($responseArr['Comment'], 'created');
186
        $this->assertEquals($responseArr['Name'], 'New Comment');
187
        $this->assertEquals(
188
            $response->getHeader('Location'),
189
            Controller::join_links(Director::absoluteBaseURL(), $url, $responseArr['ID'])
190
        );
191
    
192
        unset($_SERVER['PHP_AUTH_USER']);
193
        unset($_SERVER['PHP_AUTH_PW']);
194
    }
195
    
196
    public function testPUTwithJSON()
0 ignored issues
show
testPUTwithJSON uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
197
    {
198
        $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
199
        
200
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
201
        $_SERVER['PHP_AUTH_PW'] = 'editor';
202
        
203
        // by mimetype
204
        $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
205
        $body = '{"Comment":"updated"}';
206
        $response = Director::test($url, null, null, 'PUT', $body, array('Content-Type'=>'application/json'));
207
        $this->assertEquals($response->getStatusCode(), 200); // Updated
208
        $obj = Convert::json2obj($response->getBody());
209
        $this->assertEquals($obj->ID, $comment1->ID);
0 ignored issues
show
The method assertEquals() does not seem to exist on object<RestfulServerTest>.

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...
210
        $this->assertEquals($obj->Comment, 'updated');
0 ignored issues
show
The method assertEquals() does not seem to exist on object<RestfulServerTest>.

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...
211
    
212
        // by extension
213
        $url = sprintf("/api/v1/RestfulServerTest_Comment/%d.json", $comment1->ID);
214
        $body = '{"Comment":"updated"}';
215
        $response = Director::test($url, null, null, 'PUT', $body);
216
        $this->assertEquals($response->getStatusCode(), 200); // Updated
217
        $this->assertEquals(
218
            $response->getHeader('Location'),
219
            Controller::join_links(Director::absoluteBaseURL(), $url)
220
        );
221
        $obj = Convert::json2obj($response->getBody());
222
        $this->assertEquals($obj->ID, $comment1->ID);
223
        $this->assertEquals($obj->Comment, 'updated');
224
        
225
        unset($_SERVER['PHP_AUTH_USER']);
226
        unset($_SERVER['PHP_AUTH_PW']);
227
    }
228
    
229
    public function testPUTwithXML()
0 ignored issues
show
testPUTwithXML uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
230
    {
231
        $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
232
        
233
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
234
        $_SERVER['PHP_AUTH_PW'] = 'editor';
235
        
236
        // by mimetype
237
        $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
238
        $body = '<RestfulServerTest_Comment><Comment>updated</Comment></RestfulServerTest_Comment>';
239
        $response = Director::test($url, null, null, 'PUT', $body, array('Content-Type'=>'text/xml'));
240
        $this->assertEquals($response->getStatusCode(), 200); // Updated
241
        $obj = Convert::xml2array($response->getBody());
242
        $this->assertEquals($obj['ID'], $comment1->ID);
0 ignored issues
show
The method assertEquals() does not seem to exist on object<RestfulServerTest>.

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...
243
        $this->assertEquals($obj['Comment'], 'updated');
0 ignored issues
show
The method assertEquals() does not seem to exist on object<RestfulServerTest>.

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...
244
    
245
        // by extension
246
        $url = sprintf("/api/v1/RestfulServerTest_Comment/%d.xml", $comment1->ID);
247
        $body = '<RestfulServerTest_Comment><Comment>updated</Comment></RestfulServerTest_Comment>';
248
        $response = Director::test($url, null, null, 'PUT', $body);
249
        $this->assertEquals($response->getStatusCode(), 200); // Updated
250
        $this->assertEquals(
251
            $response->getHeader('Location'),
252
            Controller::join_links(Director::absoluteBaseURL(), $url)
253
        );
254
        $obj = Convert::xml2array($response->getBody());
255
        $this->assertEquals($obj['ID'], $comment1->ID);
256
        $this->assertEquals($obj['Comment'], 'updated');
257
        
258
        unset($_SERVER['PHP_AUTH_USER']);
259
        unset($_SERVER['PHP_AUTH_PW']);
260
    }
261
        
262
    public function testHTTPAcceptAndContentType()
263
    {
264
        $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
265
        
266
        $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
267
        
268
        $headers = array('Accept' => 'application/json');
269
        $response = Director::test($url, null, null, 'GET', null, $headers);
270
        $this->assertEquals($response->getStatusCode(), 200); // Success
271
        $obj = Convert::json2obj($response->getBody());
272
        $this->assertEquals($obj->ID, $comment1->ID);
273
        $this->assertEquals($response->getHeader('Content-Type'), 'application/json');
274
    }
275
    
276
    public function testNotFound()
277
    {
278
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
279
        $_SERVER['PHP_AUTH_PW'] = 'user';
280
        
281
        $url = "/api/v1/RestfulServerTest_Comment/99";
282
        $response = Director::test($url, null, null, 'GET');
283
        $this->assertEquals($response->getStatusCode(), 404);
284
        
285
        unset($_SERVER['PHP_AUTH_USER']);
286
        unset($_SERVER['PHP_AUTH_PW']);
287
    }
288
    
289 View Code Duplication
    public function testMethodNotAllowed()
290
    {
291
        $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
292
        
293
        $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
294
        $response = Director::test($url, null, null, 'UNKNOWNHTTPMETHOD');
295
        $this->assertEquals($response->getStatusCode(), 405);
296
    }
297
    
298 View Code Duplication
    public function testConflictOnExistingResourceWhenUsingPost()
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
299
    {
300
        $rating1 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating1');
301
        
302
        $url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID;
303
        $response = Director::test($url, null, null, 'POST');
304
        $this->assertEquals($response->getStatusCode(), 409);
305
    }
306
    
307
    public function testUnsupportedMediaType()
0 ignored issues
show
testUnsupportedMediaType uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
308
    {
309
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
310
        $_SERVER['PHP_AUTH_PW'] = 'user';
311
    
312
        $url = "/api/v1/RestfulServerTest_Comment";
313
        $data = "Comment||\/||updated"; // weird format
314
        $headers = array('Content-Type' => 'text/weirdformat');
315
        $response = Director::test($url, null, null, 'POST', $data, $headers);
316
        $this->assertEquals($response->getStatusCode(), 415);
317
        
318
        unset($_SERVER['PHP_AUTH_USER']);
319
        unset($_SERVER['PHP_AUTH_PW']);
320
    }
321
    
322
    public function testXMLValueFormatting()
323
    {
324
        $rating1 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating1');
325
        
326
        $url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID;
327
        $response = Director::test($url, null, null, 'GET');
328
        $this->assertContains('<ID>' . $rating1->ID . '</ID>', $response->getBody());
329
        $this->assertContains('<Rating>' . $rating1->Rating . '</Rating>', $response->getBody());
330
    }
331
    
332
    public function testApiAccessFieldRestrictions()
333
    {
334
        $author1 = $this->objFromFixture('RestfulServerTest_Author', 'author1');
335
        $rating1 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating1');
336
        
337
        $url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID;
338
        $response = Director::test($url, null, null, 'GET');
339
        $this->assertContains('<ID>', $response->getBody());
340
        $this->assertContains('<Rating>', $response->getBody());
341
        $this->assertContains('<Author', $response->getBody());
342
        $this->assertNotContains('<SecretField>', $response->getBody());
343
        $this->assertNotContains('<SecretRelation>', $response->getBody());
344
        
345
        $url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID . '?add_fields=SecretField,SecretRelation';
346
        $response = Director::test($url, null, null, 'GET');
347
        $this->assertNotContains('<SecretField>', $response->getBody(),
348
            '"add_fields" URL parameter filters out disallowed fields from $api_access'
349
        );
350
        $this->assertNotContains('<SecretRelation>', $response->getBody(),
351
            '"add_fields" URL parameter filters out disallowed relations from $api_access'
352
        );
353
        
354
        $url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID . '?fields=SecretField,SecretRelation';
355
        $response = Director::test($url, null, null, 'GET');
356
        $this->assertNotContains('<SecretField>', $response->getBody(),
357
            '"fields" URL parameter filters out disallowed fields from $api_access'
358
        );
359
        $this->assertNotContains('<SecretRelation>', $response->getBody(),
360
            '"fields" URL parameter filters out disallowed relations from $api_access'
361
        );
362
        
363
        $url = "/api/v1/RestfulServerTest_Author/" . $author1->ID . '/Ratings';
364
        $response = Director::test($url, null, null, 'GET');
365
        $this->assertContains('<Rating>', $response->getBody(),
366
            'Relation viewer shows fields allowed through $api_access'
367
        );
368
        $this->assertNotContains('<SecretField>', $response->getBody(),
369
            'Relation viewer on has-many filters out disallowed fields from $api_access'
370
        );
371
    }
372
    
373
    public function testApiAccessRelationRestrictionsInline()
374
    {
375
        $author1 = $this->objFromFixture('RestfulServerTest_Author', 'author1');
376
        
377
        $url = "/api/v1/RestfulServerTest_Author/" . $author1->ID;
378
        $response = Director::test($url, null, null, 'GET');
379
        $this->assertNotContains('<RelatedPages', $response->getBody(), 'Restricts many-many with api_access=false');
380
        $this->assertNotContains('<PublishedPages', $response->getBody(), 'Restricts has-many with api_access=false');
381
    }
382
    
383
    public function testApiAccessRelationRestrictionsOnEndpoint()
384
    {
385
        $author1 = $this->objFromFixture('RestfulServerTest_Author', 'author1');
386
        
387
        $url = "/api/v1/RestfulServerTest_Author/" . $author1->ID . "/ProfilePage";
388
        $response = Director::test($url, null, null, 'GET');
389
        $this->assertEquals(404, $response->getStatusCode(), 'Restricts has-one with api_access=false');
390
        
391
        $url = "/api/v1/RestfulServerTest_Author/" . $author1->ID . "/RelatedPages";
392
        $response = Director::test($url, null, null, 'GET');
393
        $this->assertEquals(404, $response->getStatusCode(), 'Restricts many-many with api_access=false');
394
        
395
        $url = "/api/v1/RestfulServerTest_Author/" . $author1->ID . "/PublishedPages";
396
        $response = Director::test($url, null, null, 'GET');
0 ignored issues
show
null is of type null, but the function expects a object<Session>|array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
397
        $this->assertEquals(404, $response->getStatusCode(), 'Restricts has-many with api_access=false');
398
    }
399
    
400
    public function testApiAccessWithPUT()
401
    {
402
        $rating1 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating1');
403
        
404
        $url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID;
405
        $data = array(
406
            'Rating' => '42',
407
            'WriteProtectedField' => 'haxx0red'
408
        );
409
        $response = Director::test($url, $data, null, 'PUT');
410
        // Assumption: XML is default output
411
        $responseArr = Convert::xml2array($response->getBody());
412
        $this->assertEquals($responseArr['Rating'], 42);
413
        $this->assertNotEquals($responseArr['WriteProtectedField'], 'haxx0red');
414
    }
415
416
    public function testJSONDataFormatter()
417
    {
418
        $formatter = new JSONDataFormatter();
419
        $editor = $this->objFromFixture('Member', 'editor');
420
        $user = $this->objFromFixture('Member', 'user');
421
422
        $this->assertEquals(
423
            $formatter->convertDataObject($editor, array("FirstName", "Email")),
424
            '{"FirstName":"Editor","Email":"[email protected]"}',
425
            "Correct JSON formatting with field subset");
426
427
        $set = DataObject::get(
428
            "Member",
429
            sprintf('"Member"."ID" IN (%s)', implode(',', array($editor->ID, $user->ID))),
430
            '"Email" ASC' // for sorting for postgres
431
        );
432
        $this->assertEquals(
433
            $formatter->convertDataObjectSet($set, array("FirstName", "Email")),
434
            '{"totalSize":null,"items":[{"FirstName":"Editor","Email":"[email protected]"},' .
435
            '{"FirstName":"User","Email":"[email protected]"}]}',
436
            "Correct JSON formatting on a dataobjectset with field filter");
437
    }
438
    
439
    public function testApiAccessWithPOST()
440
    {
441
        $url = "/api/v1/RestfulServerTest_AuthorRating";
442
        $data = array(
443
            'Rating' => '42',
444
            'WriteProtectedField' => 'haxx0red'
445
        );
446
        $response = Director::test($url, $data, null, 'POST');
447
        // Assumption: XML is default output
448
        $responseArr = Convert::xml2array($response->getBody());
449
        $this->assertEquals($responseArr['Rating'], 42);
450
        $this->assertNotEquals($responseArr['WriteProtectedField'], 'haxx0red');
451
    }
452
453
    public function testCanViewRespectedInList()
0 ignored issues
show
testCanViewRespectedInList uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
454
    {
455
        // Default content type
456
        $url = "/api/v1/RestfulServerTest_SecretThing/";
457
        $response = Director::test($url, null, null, 'GET');
458
        $this->assertEquals($response->getStatusCode(), 200);
459
        $this->assertNotContains('Unspeakable', $response->getBody());
460
461
        // JSON content type
462
        $url = "/api/v1/RestfulServerTest_SecretThing.json";
463
        $response = Director::test($url, null, null, 'GET');
464
        $this->assertEquals($response->getStatusCode(), 200);
465
        $this->assertNotContains('Unspeakable', $response->getBody());
466
467
        // With authentication
468
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
469
        $_SERVER['PHP_AUTH_PW'] = 'editor';
470
        $url = "/api/v1/RestfulServerTest_SecretThing/";
471
        $response = Director::test($url, null, null, 'GET');
472
        $this->assertEquals($response->getStatusCode(), 200);
473
        $this->assertContains('Unspeakable', $response->getBody());
474
        unset($_SERVER['PHP_AUTH_USER']);
475
        unset($_SERVER['PHP_AUTH_PW']);
476
    }
477
}
478
479
/**
480
 * Everybody can view comments, logged in members in the "users" group can create comments,
481
 * but only "editors" can edit or delete them.
482
 *
483
 */
484
class RestfulServerTest_Comment extends DataObject implements PermissionProvider,TestOnly
485
{
486
    public static $api_access = true;
487
    
488
    public static $db = array(
489
        "Name" => "Varchar(255)",
490
        "Comment" => "Text"
491
    );
492
    
493
    public static $has_one = array(
494
        'Page' => 'RestfulServerTest_Page',
495
        'Author' => 'RestfulServerTest_Author',
496
    );
497
    
498
    public function providePermissions()
499
    {
500
        return array(
501
            'EDIT_Comment' => 'Edit Comment Objects',
502
            'CREATE_Comment' => 'Create Comment Objects',
503
            'DELETE_Comment' => 'Delete Comment Objects',
504
        );
505
    }
506
    
507
    public function canView($member = null)
508
    {
509
        return true;
510
    }
511
    
512
    public function canEdit($member = null)
513
    {
514
        return Permission::checkMember($member, 'EDIT_Comment');
515
    }
516
    
517
    public function canDelete($member = null)
518
    {
519
        return Permission::checkMember($member, 'DELETE_Comment');
520
    }
521
    
522
    public function canCreate($member = null)
523
    {
524
        return Permission::checkMember($member, 'CREATE_Comment');
525
    }
526
}
527
528
class RestfulServerTest_SecretThing extends DataObject implements TestOnly,PermissionProvider
529
{
530
    public static $api_access = true;
531
    
532
    public static $db = array(
533
        "Name" => "Varchar(255)",
534
    );
535
    
536
    public function canView($member = null)
537
    {
538
        return Permission::checkMember($member, 'VIEW_SecretThing');
539
    }
540
    
541
    public function providePermissions()
542
    {
543
        return array(
544
            'VIEW_SecretThing' => 'View Secret Things',
545
        );
546
    }
547
}
548
549
class RestfulServerTest_Page extends DataObject implements TestOnly
550
{
551
    public static $api_access = false;
552
    
553
    public static $db = array(
554
        'Title' => 'Text',
555
        'Content' => 'HTMLText',
556
    );
557
    
558
    public static $has_one = array(
559
        'Author' => 'RestfulServerTest_Author',
560
    );
561
    
562
    public static $has_many = array(
563
        'TestComments' => 'RestfulServerTest_Comment'
564
    );
565
    
566
    public static $belongs_many_many = array(
567
        'RelatedAuthors' => 'RestfulServerTest_Author',
568
    );
569
}
570
571
class RestfulServerTest_Author extends DataObject implements TestOnly
572
{
573
    public static $api_access = true;
574
    
575
    public static $db = array(
576
        'Name' => 'Text',
577
    );
578
        
579
    public static $many_many = array(
580
        'RelatedPages' => 'RestfulServerTest_Page',
581
        'RelatedAuthors' => 'RestfulServerTest_Author',
582
    );
583
    
584
    public static $has_many = array(
585
        'PublishedPages' => 'RestfulServerTest_Page',
586
        'Ratings' => 'RestfulServerTest_AuthorRating',
587
    );
588
    
589
    public function canView($member = null)
590
    {
591
        return true;
592
    }
593
}
594
595
class RestfulServerTest_AuthorRating extends DataObject implements TestOnly
596
{
597
    public static $api_access = array(
598
        'view' => array(
599
            'Rating',
600
            'WriteProtectedField',
601
            'Author'
602
        ),
603
        'edit' => array(
604
            'Rating'
605
        )
606
    );
607
    
608
    public static $db = array(
609
        'Rating' => 'Int',
610
        'SecretField' => 'Text',
611
        'WriteProtectedField' => 'Text',
612
    );
613
    
614
    public static $has_one = array(
615
        'Author' => 'RestfulServerTest_Author',
616
        'SecretRelation' => 'RestfulServerTest_Author',
617
    );
618
    
619
    public function canView($member = null)
620
    {
621
        return true;
622
    }
623
    
624
    public function canEdit($member = null)
625
    {
626
        return true;
627
    }
628
    
629
    public function canCreate($member = null)
630
    {
631
        return true;
632
    }
633
}
634