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