bade2a76c695869793daa7751799fa520b277622
[yaffs-website] / web / core / modules / rest / tests / src / Functional / EntityResource / Comment / CommentResourceTestBase.php
1 <?php
2
3 namespace Drupal\Tests\rest\Functional\EntityResource\Comment;
4
5 use Drupal\comment\Entity\Comment;
6 use Drupal\comment\Entity\CommentType;
7 use Drupal\comment\Tests\CommentTestTrait;
8 use Drupal\entity_test\Entity\EntityTest;
9 use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
10 use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
11 use Drupal\user\Entity\User;
12 use GuzzleHttp\RequestOptions;
13
14 abstract class CommentResourceTestBase extends EntityResourceTestBase {
15
16   use CommentTestTrait, BcTimestampNormalizerUnixTestTrait;
17
18   /**
19    * {@inheritdoc}
20    */
21   public static $modules = ['comment', 'entity_test'];
22
23   /**
24    * {@inheritdoc}
25    */
26   protected static $entityTypeId = 'comment';
27
28   /**
29    * {@inheritdoc}
30    */
31   protected static $patchProtectedFieldNames = [
32     'status',
33     'pid',
34     'entity_id',
35     'uid',
36     'name',
37     'homepage',
38     'created',
39     'changed',
40     'thread',
41     'entity_type',
42     'field_name',
43   ];
44
45   /**
46    * @var \Drupal\comment\CommentInterface
47    */
48   protected $entity;
49
50   /**
51    * {@inheritdoc}
52    */
53   protected function setUpAuthorization($method) {
54     switch ($method) {
55       case 'GET':
56         $this->grantPermissionsToTestedRole(['access comments', 'view test entity']);
57         break;
58       case 'POST':
59         $this->grantPermissionsToTestedRole(['post comments']);
60         break;
61       case 'PATCH':
62         // Anononymous users are not ever allowed to edit their own comments. To
63         // be able to test PATCHing comments as the anonymous user, the more
64         // permissive 'administer comments' permission must be granted.
65         // @see \Drupal\comment\CommentAccessControlHandler::checkAccess
66         if (static::$auth) {
67           $this->grantPermissionsToTestedRole(['edit own comments']);
68         }
69         else {
70           $this->grantPermissionsToTestedRole(['administer comments']);
71         }
72         break;
73       case 'DELETE':
74         $this->grantPermissionsToTestedRole(['administer comments']);
75         break;
76     }
77   }
78
79   /**
80    * {@inheritdoc}
81    */
82   protected function createEntity() {
83     // Create a "bar" bundle for the "entity_test" entity type and create.
84     $bundle = 'bar';
85     entity_test_create_bundle($bundle, NULL, 'entity_test');
86
87     // Create a comment field on this bundle.
88     $this->addDefaultCommentField('entity_test', 'bar', 'comment');
89
90     // Create a "Camelids" test entity that the comment will be assigned to.
91     $commented_entity = EntityTest::create([
92       'name' => 'Camelids',
93       'type' => 'bar',
94     ]);
95     $commented_entity->save();
96
97     // Create a "Llama" comment.
98     $comment = Comment::create([
99       'comment_body' => [
100         'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
101         'format' => 'plain_text',
102       ],
103       'entity_id' => $commented_entity->id(),
104       'entity_type' => 'entity_test',
105       'field_name' => 'comment',
106     ]);
107     $comment->setSubject('Llama')
108       ->setOwnerId(static::$auth ? $this->account->id() : 0)
109       ->setPublished(TRUE)
110       ->setCreatedTime(123456789)
111       ->setChangedTime(123456789);
112     $comment->save();
113
114     return $comment;
115   }
116
117   /**
118    * {@inheritdoc}
119    */
120   protected function getExpectedNormalizedEntity() {
121     $author = User::load($this->entity->getOwnerId());
122     return [
123       'cid' => [
124         ['value' => 1],
125       ],
126       'uuid' => [
127         ['value' => $this->entity->uuid()],
128       ],
129       'langcode' => [
130         [
131           'value' => 'en',
132         ],
133       ],
134       'comment_type' => [
135         [
136           'target_id' => 'comment',
137           'target_type' => 'comment_type',
138           'target_uuid' => CommentType::load('comment')->uuid(),
139         ],
140       ],
141       'subject' => [
142         [
143           'value' => 'Llama',
144         ],
145       ],
146       'status' => [
147         [
148           'value' => TRUE,
149         ],
150       ],
151       'created' => [
152         $this->formatExpectedTimestampItemValues(123456789),
153       ],
154       'changed' => [
155         $this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
156       ],
157       'default_langcode' => [
158         [
159           'value' => TRUE,
160         ],
161       ],
162       'uid' => [
163         [
164           'target_id' => (int) $author->id(),
165           'target_type' => 'user',
166           'target_uuid' => $author->uuid(),
167           'url' => base_path() . 'user/' . $author->id(),
168         ],
169       ],
170       'pid' => [],
171       'entity_type' => [
172         [
173           'value' => 'entity_test',
174         ],
175       ],
176       'entity_id' => [
177         [
178           'target_id' => 1,
179           'target_type' => 'entity_test',
180           'target_uuid' => EntityTest::load(1)->uuid(),
181           'url' => base_path() . 'entity_test/1',
182         ],
183       ],
184       'field_name' => [
185         [
186           'value' => 'comment',
187         ],
188       ],
189       'name' => [],
190       'homepage' => [],
191       'thread' => [
192         [
193           'value' => '01/',
194         ],
195       ],
196       'comment_body' => [
197         [
198           'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
199           'format' => 'plain_text',
200         ],
201       ],
202     ];
203   }
204
205   /**
206    * {@inheritdoc}
207    */
208   protected function getNormalizedPostEntity() {
209     return [
210       'comment_type' => [
211         [
212           'target_id' => 'comment',
213         ],
214       ],
215       'entity_type' => [
216         [
217           'value' => 'entity_test',
218         ],
219       ],
220       'entity_id' => [
221         [
222           'target_id' => (int) EntityTest::load(1)->id(),
223         ],
224       ],
225       'field_name' => [
226         [
227           'value' => 'comment',
228         ],
229       ],
230       'subject' => [
231         [
232           'value' => 'Dramallama',
233         ],
234       ],
235       'comment_body' => [
236         [
237           'value' => 'Llamas are awesome.',
238           'format' => 'plain_text',
239         ],
240       ],
241     ];
242   }
243
244   /**
245    * {@inheritdoc}
246    */
247   protected function getNormalizedPatchEntity() {
248     return array_diff_key($this->getNormalizedPostEntity(), ['entity_type' => TRUE, 'entity_id' => TRUE, 'field_name' => TRUE]);
249   }
250
251   /**
252    * Tests POSTing a comment without critical base fields.
253    *
254    * testPost() is testing with the most minimal normalization possible: the one
255    * returned by ::getNormalizedPostEntity().
256    *
257    * But Comment entities have some very special edge cases:
258    * - base fields that are not marked as required in
259    *   \Drupal\comment\Entity\Comment::baseFieldDefinitions() yet in fact are
260    *   required.
261    * - base fields that are marked as required, but yet can still result in
262    *   validation errors other than "missing required field".
263    */
264   public function testPostDxWithoutCriticalBaseFields() {
265     $this->initAuthentication();
266     $this->provisionEntityResource();
267     $this->setUpAuthorization('POST');
268
269     $url = $this->getEntityResourcePostUrl()->setOption('query', ['_format' => static::$format]);
270     $request_options = [];
271     $request_options[RequestOptions::HEADERS]['Accept'] = static::$mimeType;
272     $request_options[RequestOptions::HEADERS]['Content-Type'] = static::$mimeType;
273     $request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('POST'));
274
275     // DX: 422 when missing 'entity_type' field.
276     $request_options[RequestOptions::BODY] = $this->serializer->encode(array_diff_key($this->getNormalizedPostEntity(), ['entity_type' => TRUE]), static::$format);
277     $response = $this->request('POST', $url, $request_options);
278     // @todo Uncomment, remove next 3 lines in https://www.drupal.org/node/2820364.
279     $this->assertSame(500, $response->getStatusCode());
280     $this->assertSame(['text/plain; charset=UTF-8'], $response->getHeader('Content-Type'));
281     $this->assertStringStartsWith('The website encountered an unexpected error. Please try again later.</br></br><em class="placeholder">Symfony\Component\HttpKernel\Exception\HttpException</em>: Internal Server Error in <em class="placeholder">Drupal\rest\Plugin\rest\resource\EntityResource-&gt;post()</em>', (string) $response->getBody());
282     // $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response);
283
284     // DX: 422 when missing 'entity_id' field.
285     $request_options[RequestOptions::BODY] = $this->serializer->encode(array_diff_key($this->getNormalizedPostEntity(), ['entity_id' => TRUE]), static::$format);
286     // @todo Remove the try/catch in favor of the two commented lines in
287     // https://www.drupal.org/node/2820364.
288     try {
289       $response = $this->request('POST', $url, $request_options);
290       // This happens on DrupalCI.
291       // $this->assertSame(500, $response->getStatusCode());
292     }
293     catch (\Exception $e) {
294       // This happens on Wim's local machine.
295       // $this->assertSame("Error: Call to a member function get() on null\nDrupal\\comment\\Plugin\\Validation\\Constraint\\CommentNameConstraintValidator->getAnonymousContactDetailsSetting()() (Line: 96)\n", $e->getMessage());
296     }
297     // $response = $this->request('POST', $url, $request_options);
298     // $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response);
299
300     // DX: 422 when missing 'entity_type' field.
301     $request_options[RequestOptions::BODY] = $this->serializer->encode(array_diff_key($this->getNormalizedPostEntity(), ['field_name' => TRUE]), static::$format);
302     $response = $this->request('POST', $url, $request_options);
303     // @todo Uncomment, remove next 2 lines in https://www.drupal.org/node/2820364.
304     $this->assertSame(500, $response->getStatusCode());
305     $this->assertSame(['text/plain; charset=UTF-8'], $response->getHeader('Content-Type'));
306     // $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nfield_name: This value should not be null.\n", $response);
307   }
308
309   /**
310    * {@inheritdoc}
311    */
312   protected function getExpectedUnauthorizedAccessMessage($method) {
313     if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
314       return parent::getExpectedUnauthorizedAccessMessage($method);
315     }
316
317     switch ($method) {
318       case 'GET';
319         return "The 'access comments' permission is required and the comment must be published.";
320       case 'POST';
321         return "The 'post comments' permission is required.";
322       default:
323         return parent::getExpectedUnauthorizedAccessMessage($method);
324     }
325   }
326
327   /**
328    * Tests POSTing a comment with and without 'skip comment approval'
329    */
330   public function testPostSkipCommentApproval() {
331     $this->initAuthentication();
332     $this->provisionEntityResource();
333     $this->setUpAuthorization('POST');
334
335     // Create request.
336     $request_options = [];
337     $request_options[RequestOptions::HEADERS]['Accept'] = static::$mimeType;
338     $request_options[RequestOptions::HEADERS]['Content-Type'] = static::$mimeType;
339     $request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('POST'));
340     $request_options[RequestOptions::BODY] = $this->serializer->encode($this->getNormalizedPostEntity(), static::$format);
341
342     $url = $this->getEntityResourcePostUrl()->setOption('query', ['_format' => static::$format]);
343
344     // Status should be FALSE when posting as anonymous.
345     $response = $this->request('POST', $url, $request_options);
346     $unserialized = $this->serializer->deserialize((string) $response->getBody(), get_class($this->entity), static::$format);
347     $this->assertResourceResponse(201, FALSE, $response);
348     $this->assertFalse($unserialized->getStatus());
349
350     // Grant anonymous permission to skip comment approval.
351     $this->grantPermissionsToTestedRole(['skip comment approval']);
352
353     // Status should be TRUE when posting as anonymous and skip comment approval.
354     $response = $this->request('POST', $url, $request_options);
355     $unserialized = $this->serializer->deserialize((string) $response->getBody(), get_class($this->entity), static::$format);
356     $this->assertResourceResponse(201, FALSE, $response);
357     $this->assertTrue($unserialized->getStatus());
358   }
359
360 }