Passed
Push — master ( 2f8638...569786 )
by Dominik
25:17
created

MailgunWebhookControllerTest::getValidPostData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 12
rs 10
1
<?php
2
3
namespace Azine\MailgunWebhooksBundle\Tests\Controller;
4
5
use Azine\MailgunWebhooksBundle\DependencyInjection\AzineMailgunWebhooksExtension;
6
use Azine\MailgunWebhooksBundle\Entity\EmailTrafficStatistics;
7
use Azine\MailgunWebhooksBundle\Tests\TestHelper;
8
use Doctrine\ORM\EntityManager;
9
use Symfony\Bundle\FrameworkBundle\Client;
10
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
11
use Symfony\Component\DependencyInjection\ContainerInterface;
12
use Symfony\Component\DomCrawler\Crawler;
13
use Symfony\Component\EventDispatcher\EventDispatcher;
14
use Symfony\Component\HttpFoundation\File\UploadedFile;
15
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
16
17
class MailgunWebhookControllerTest extends WebTestCase
18
{
19
    private $testStartTime;
20
    protected function setUp()
21
    {
22
        $this->testStartTime = new \DateTime();
23
    }
24
25
    protected function tearDown(){
26
        $manager = $this->getEntityManager();
27
        $queryBuilder = $manager->getRepository(EmailTrafficStatistics::class)->createQueryBuilder("e");
28
        $ets = $queryBuilder->where('e.created >= :testStartTime')
29
            ->setParameter('testStartTime', $this->testStartTime)
30
            ->getQuery()->execute();
31
32
        if($ets != null && sizeof($ets) > 0){
33
            foreach ($ets as $next){
34
                $manager->remove($next);
35
            }
36
            $manager->flush();
37
        }
38
    }
39
40
    public function testWebHookCreateAndEventDispatching_oldAPI(){
41
        $this->checkApplication();
42
43
        $validPostData = $this->getValidPostData(false);
44
        $invalidPostData = $this->getInvalidPostData(false);
45
        $this->internalWebHookCreateAndEventDispatching($validPostData, $invalidPostData, false);
46
    }
47
48
49
    public function testWebHookCreateAndEventDispatching_newAPI(){
50
        $this->checkApplication();
51
52
        $validPostData = $this->getValidPostData(true);
53
        $invalidPostData = $this->getInvalidPostData(true);
54
        $this->internalWebHookCreateAndEventDispatching($validPostData, $invalidPostData, true);
55
    }
56
57
    private function internalWebHookCreateAndEventDispatching($validPostData, $invalidPostData, $newApi){
58
59
        $client = static::createClient();
60
        $client->request('GET', '/');
61
        $client->enableProfiler();
62
63
        // get webhook url
64
        $url = $this->getRouter()->generate('mailgunevent_webhook', array('_locale', 'en'), UrlGeneratorInterface::ABSOLUTE_URL);
65
66
        $manager = $this->getEntityManager();
67
        $eventReop = $manager->getRepository("Azine\MailgunWebhooksBundle\Entity\MailgunEvent");
68
        $count = sizeof($eventReop->findAll());
69
70
        $attachments = array(
71
            'attachment-1' => new UploadedFile(realpath(__DIR__.'/../testAttachment.small.png'), 'some.real.file.name1.png'),
72
            'attachment-2' => new UploadedFile(realpath(__DIR__.'/../testAttachment.small.png'), 'some.real.file.name2.png'),
73
            'attachment-3' => new UploadedFile(realpath(__DIR__.'/../testAttachment.small.png'), 'some.real.file.name3.png'),
74
        );
75
76
        // post invalid data to the webhook-url and check the response & database
77
        $webhookdata = json_encode($invalidPostData);
78
        if($newApi){
79
            $crawler = $client->request('POST', $url, array(), $attachments, array(), $webhookdata);
80
        } else {
81
            $crawler = $client->request('POST', $url, $invalidPostData, $attachments);
82
        }
83
84
        $this->assertSame(401, $client->getResponse()->getStatusCode(), "Response-Code 401 expected for post-data with invalid signature: \n\n$webhookdata\n\n\n");
85
        $this->assertContains('Signature verification failed.', $crawler->text(), 'Response expected.');
86
        $this->assertSame($count, sizeof($eventReop->findAll()), 'No new db entry for the webhook expected!');
87
88
        // post valid data to the webhook-url and check the response
89
        $webhookdata = json_encode($validPostData);
90
        if($newApi){
91
            $crawler = $client->request('POST', $url, array(), $attachments, array(), $webhookdata);
92
        } else {
93
            $crawler = $client->request('POST', $url, $validPostData, $attachments);
94
        }
95
        $this->assertSame(200, $client->getResponse()->getStatusCode(), "Response-Code 200 expected for '$url'.\n\n$webhookdata\n\n\n".$client->getResponse()->getContent());
96
        $this->assertContains('Thanx, for the info.', $crawler->text(), 'Response expected.');
97
        $this->assertSame($count + 1, sizeof($eventReop->findAll()), 'One new db entry for the webhook expected!');
98
99
100
101
        // post valid data to the webhook-url and check the response
102
        if($newApi){
103
            $validPostData['event-data']['event'] = 'opened';
104
            $webhookdata = json_encode($validPostData);
105
            $crawler = $client->request('POST', $url, array(), $attachments, array(), $webhookdata);
0 ignored issues
show
Unused Code introduced by
The assignment to $crawler is dead and can be removed.
Loading history...
106
            $crawler = $client->request('POST', $url, array(), $attachments, array(), $webhookdata);
107
        } else {
108
            $validPostData['event'] = 'opened';
109
            $webhookdata = json_encode($validPostData);
110
            $crawler = $client->request('POST', $url, $validPostData, $attachments);
111
            $crawler = $client->request('POST', $url, $validPostData, $attachments);
112
        }
113
        $this->assertSame(200, $client->getResponse()->getStatusCode(), "Response-Code 200 expected for '$url'.\n\n$webhookdata\n\n\n".$client->getResponse()->getContent());
114
        $this->assertContains('Thanx, for the info.', $crawler->text(), 'Response expected.');
115
        
116
117
118
        // post a complaint event to check if mail is triggered.
119
        if($newApi){
120
            $validPostData['event-data']['event'] = 'complained';
121
            $webhookdata = json_encode($validPostData);
122
            $crawler = $client->request('POST', $url, array(), $attachments, array(), $webhookdata);
123
        } else {
124
            $validPostData['event'] = 'complained';
125
            $webhookdata = json_encode($validPostData);
126
            $crawler = $client->request('POST', $url, $validPostData, $attachments);
127
        }
128
        $this->assertSame(200, $client->getResponse()->getStatusCode(), "Response-Code 200 expected for '$url'.\n\n$webhookdata\n\n\n".$client->getResponse()->getContent());
129
        $this->assertContains('Thanx, for the info.', $crawler->text(), 'Response expected.');
130
        $this->assertSame($count + 4, sizeof($eventReop->findAll()), 'One new db entry for the webhook expected!');
131
132
        $mailCollector = $client->getProfile()->getCollector('swiftmailer');
133
134
        // checks that an email was sent from the listener
135
        $this->assertSame(1, $mailCollector->getMessageCount());
0 ignored issues
show
Bug introduced by
The method getMessageCount() does not exist on Symfony\Component\HttpKe...\DataCollectorInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

135
        $this->assertSame(1, $mailCollector->/** @scrutinizer ignore-call */ getMessageCount());

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...
136
    }
137
138
    private function getValidPostData($newApi)
139
    {
140
        $postData = TestHelper::getPostDataWithoutSignature($newApi);
141
142
        $key = 'fake_api_key';//$this->getContainer()->getParameter(AzineMailgunWebhooksExtension::PREFIX.'_'.AzineMailgunWebhooksExtension::API_KEY);
143
144
        if($newApi) {
145
            $postData['signature']['signature'] = hash_hmac('SHA256', $postData['signature']['timestamp'].$postData['signature']['token'], $key);
146
        } else {
147
            $postData['signature'] = hash_hmac('SHA256', $postData['timestamp'].$postData['token'], $key);
148
        }
149
        return $postData;
150
    }
151
152
    private function getInvalidPostData($newApi)
153
    {
154
        $postData = TestHelper::getPostDataWithoutSignature($newApi);
155
156
        if($newApi) {
157
            $postData['signature']['signature'] = 'invalid-signature';
158
        } else {
159
            $postData['signature'] = 'invalid-signature';
160
        }
161
162
        return $postData;
163
    }
164
165
    public function testShowLog()
166
    {
167
        $this->checkApplication();
168
169
        // Create a new client to browse the application
170
        $client = static::createClient();
171
        $client->followRedirects();
172
173
        $manager = $this->getEntityManager();
174
        $eventReop = $manager->getRepository("Azine\MailgunWebhooksBundle\Entity\MailgunEvent");
175
176
        $apiKey = $this->getContainer()->getParameter(AzineMailgunWebhooksExtension::PREFIX.'_'.AzineMailgunWebhooksExtension::API_KEY);
177
178
        // make sure there is plenty of data in the application to be able to verify paging
179
        if (sizeof($eventReop->findAll()) < 102) {
180
            TestHelper::addMailgunEvents($manager, 102, $apiKey);
181
        }
182
        $count = sizeof($eventReop->findAll());
183
184
        // view the list of events
185
        $pageSize = 25;
186
        $listUrl = substr($this->getRouter()->generate('mailgunevent_list', array('_locale' => 'en', 'page' => 1, 'pageSize' => $pageSize, 'clear' => true)), 13);
187
        $crawler = $this->loginUserIfRequired($client, $listUrl);
188
        $this->assertSame($pageSize + 1, $crawler->filter('.eventsTable tr')->count(), "$pageSize Mailgun events (+1 header row) expected on this page ($listUrl)!");
189
190
        // view a single event
191
        $link = $crawler->filter('.eventsTable tr a:first-child')->first()->link();
192
        $posLastSlash = strrpos($link->getUri(), '/');
193
        $posOfIdStart = strrpos($link->getUri(), '/', -6) + 1;
194
        $eventId = substr($link->getUri(), $posOfIdStart, $posLastSlash - $posOfIdStart);
195
        $crawler = $client->click($link);
196
        $this->assertSame(200, $client->getResponse()->getStatusCode(), 'Status 200 expected.');
197
        $this->assertSame($eventId, $crawler->filter('td')->first()->text(), "Content of first td should be the eventId ($eventId)");
198
199
        // delete the event from show-page
200
        $link = $crawler->selectLink('delete')->link();
201
        $crawler = $client->click($link);
202
203
        // check that it is gone from the list
204
        $this->assertSame(0, $crawler->filter("#event$eventId")->count(), 'The deleted event should not be in the list anymore.');
205
206
        // delete the event from list-page
207
        $crawler = $client->followRedirect();
208
        $link = $crawler->filter('.eventsTable tr .deleteLink')->first()->link();
209
        $delUri = $link->getUri();
210
        $eventId = substr($delUri, strrpos($delUri, '/') + 1);
211
        $crawler = $client->click($link);
212
213
        // check that it is gone from the list
214
        $this->assertSame(0, $crawler->filter("#event$eventId")->count(), 'The deleted event should not be in the list anymore.');
215
216
        // filter the list for something
217
        $crawler = $client->followRedirect();
218
        $form = $crawler->selectButton('Filter')->form();
219
        $form['filter[eventType]']->select('delivered');
0 ignored issues
show
Bug introduced by
The method select() does not exist on Symfony\Component\DomCrawler\Field\FormField. It seems like you code against a sub-type of Symfony\Component\DomCrawler\Field\FormField such as Symfony\Component\DomCrawler\Field\ChoiceFormField. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

219
        $form['filter[eventType]']->/** @scrutinizer ignore-call */ 
220
                                    select('delivered');
Loading history...
220
        $crawler = $client->submit($form);
221
        $this->assertSame($crawler->filter('.eventsTable tr')->count() - 1, $crawler->filter(".eventsTable a:contains('delivered')")->count(), "There should only be 'delivered' events in the list");
222
223
        // delete entry with xmlHttpRequest
224
        $eventToDelete = $eventReop->findOneBy(array());
225
        $ajaxUrl = $this->getRouter()->generate('mailgunevent_delete_ajax', array('_locale' => 'en'));
226
        $client->request('POST', $ajaxUrl, array('eventId' => $eventToDelete->getId()), array(), array('HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest'));
227
        $this->assertSame('{"success":true}', $client->getResponse()->getContent(), "JSON response expcted from $ajaxUrl. for event with id:".$eventToDelete->getId());
228
229
        // show/delete inexistent log entry
230
        $inexistentEventId = md5('123invalid');
231
        $url = substr($this->getRouter()->generate('mailgunevent_delete', array('_locale' => 'en', 'eventId' => $inexistentEventId)), 13);
232
        $client->request('GET', $url);
233
        $this->assertSame(404, $client->getResponse()->getStatusCode(), "404 expected for invalid eventId ($inexistentEventId).");
234
235
        $url = substr($this->getRouter()->generate('mailgunevent_show', array('_locale' => 'en', 'id' => $inexistentEventId)), 13);
236
        $client->request('GET', $url);
237
        $this->assertSame(404, $client->getResponse()->getStatusCode(), '404 expected.');
238
239
        // show inexistent page
240
        $maxPage = floor($count / $pageSize);
241
        $beyondListUrl = $this->getRouter()->generate('mailgunevent_list', array('_locale' => 'en', 'page' => $maxPage + 1, 'pageSize' => $pageSize, 'clear' => true));
242
        $crawler = $client->followRedirects();
0 ignored issues
show
Unused Code introduced by
The assignment to $crawler is dead and can be removed.
Loading history...
Bug introduced by
Are you sure the assignment to $crawler is correct as $client->followRedirects() targeting Symfony\Component\Browse...ient::followRedirects() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
243
        $crawler = $client->request('GET', $beyondListUrl);
244
        $this->assertSame(2, $crawler->filter(".pagination .disabled:contains('Next')")->count(), 'Expected to be on the last page => the next button should be disabled.');
245
    }
246
247
    public function testWebViewLinks()
248
    {
249
        $this->checkApplication();
250
251
        // Create a new client to browse the application
252
        $client = static::createClient();
253
        $client->followRedirects();
254
255
        $manager = $this->getEntityManager();
256
        $eventReop = $manager->getRepository("Azine\MailgunWebhooksBundle\Entity\MailgunEvent");
257
258
        $apiKey = $this->getContainer()->getParameter(AzineMailgunWebhooksExtension::PREFIX.'_'.AzineMailgunWebhooksExtension::API_KEY);
259
260
        // make sure there is data in the application
261
        $events = $eventReop->findAll();
262
        if (sizeof($events) < 5) {
263
            TestHelper::addMailgunEvents($manager, 5, $apiKey);
264
            $events = $eventReop->findAll();
265
        }
266
267
        $webViewTokenName = $this->getContainer()->getParameter(AzineMailgunWebhooksExtension::PREFIX.'_'.AzineMailgunWebhooksExtension::WEB_VIEW_TOKEN);
268
269
        $testTokenValue = 'testValue';
270
        $messageHeader = array($webViewTokenName => $testTokenValue);
271
        $events[0]->setMessageHeaders(json_encode($messageHeader));
272
        $manager->persist($events[0]);
273
        $manager->flush();
274
275
        $events = $eventReop->findAll();
276
        $pageSize = count($events);
277
278
        $listUrl = substr($this->getRouter()->generate('mailgunevent_list', array('_locale' => 'en', 'page' => 1, 'pageSize' => $pageSize, 'clear' => true)), 13);
279
        $crawler = $this->loginUserIfRequired($client, $listUrl);
280
281
        $this->assertSame(1, $crawler->filter("ul:contains('$webViewTokenName')")->count(), 'There should be events with the webView headers in the list');
282
        $this->assertSame(1, $crawler->filter("ul a:contains('$testTokenValue')")->count(), 'There should be events with the webView links in the list');
283
    }
284
285
    /**
286
     * Load the url and login if required.
287
     *
288
     * @param string $url
289
     * @param string $username
290
     * @param Client $client
291
     *
292
     * @return Crawler $crawler of the page of the url or the page after the login
293
     */
294
    private function loginUserIfRequired(Client $client, $url, $username = 'dominik', $password = 'lkjlkjlkjlkj')
295
    {
296
        // try to get the url
297
        $client->followRedirects();
298
        $crawler = $client->request('GET', $url);
299
300
        $this->assertSame(200, $client->getResponse()->getStatusCode(), 'Status-Code 200 expected.');
301
302
        // if redirected to a login-page, login as admin-user
303
        if (5 == $crawler->filter('input')->count() && 1 == $crawler->filter('#username')->count() && 1 == $crawler->filter('#password')->count()) {
304
            // set the password of the admin
305
            $userProvider = $this->getContainer()->get('fos_user.user_provider.username_email');
306
            $user = $userProvider->loadUserByUsername($username);
307
            $user->setPlainPassword($password);
308
            $user->addRole('ROLE_ADMIN');
309
310
            $userManager = $this->getContainer()->get('fos_user.user_manager');
311
            $userManager->updateUser($user);
312
313
            $crawler = $crawler->filter("input[type='submit']");
314
            $form = $crawler->form();
315
            $form->get('_username')->setValue($username);
316
            $form->get('_password')->setValue($password);
317
            $crawler = $client->submit($form);
318
        }
319
320
        $this->assertSame(200, $client->getResponse()->getStatusCode(), 'Login failed.');
321
        $client->followRedirects(false);
322
323
        $this->assertStringEndsWith($url, $client->getRequest()->getUri(), "Login failed or not redirected to requested url: $url vs. ".$client->getRequest()->getUri());
324
325
        return $crawler;
326
    }
327
328
    /**
329
     * @var ContainerInterface
330
     */
331
    private $appContainer;
332
333
    /**
334
     * Get the current container.
335
     *
336
     * @return \Symfony\Component\DependencyInjection\ContainerInterface
337
     */
338
    private function getContainer()
339
    {
340
        if (null == $this->appContainer) {
341
            $this->appContainer = static::$kernel->getContainer();
342
        }
343
344
        return $this->appContainer;
345
    }
346
347
    /**
348
     * @return UrlGeneratorInterface
349
     */
350
    private function getRouter()
351
    {
352
        return $this->getContainer()->get('router');
353
    }
354
355
    /**
356
     * @return EntityManager
357
     */
358
    private function getEntityManager()
359
    {
360
        return $this->getContainer()->get('doctrine.orm.entity_manager');
361
    }
362
363
    /**
364
     * @return EventDispatcher
365
     */
366
    private function getEventDispatcher()
0 ignored issues
show
Unused Code introduced by
The method getEventDispatcher() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
367
    {
368
        return $this->getContainer()->get('event_dispatcher');
369
    }
370
371
    /**
372
     * Check if the current setup is a full application.
373
     * If not, mark the test as skipped else continue.
374
     */
375
    private function checkApplication()
376
    {
377
        try {
378
            static::$kernel = static::createKernel(array());
379
            //static::$kernel->boot();
380
        } catch (\RuntimeException $ex) {
381
            $this->markTestSkipped('There does not seem to be a full application available (e.g. running tests on travis.org). So this test is skipped.');
382
383
            return;
384
        }
385
    }
386
}
387