Passed
Push — master ( 074d29...da7189 )
by Dominik
17:14 queued 12:54
created

MailgunEventControllerTest   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 294
Duplicated Lines 0 %

Importance

Changes 15
Bugs 2 Features 3
Metric Value
eloc 141
c 15
b 2
f 3
dl 0
loc 294
rs 10
wmc 19

12 Methods

Rating   Name   Duplication   Size   Complexity  
A testWebHookCreateAndEventDispatching() 0 44 1
A loginUserIfRequired() 0 32 4
A getRouter() 0 3 1
A getContainer() 0 7 2
A getInvalidPostData() 0 6 1
A testWebViewLinks() 0 36 2
A getEntityManager() 0 3 1
A getValidSignature() 0 6 1
A getValidPostData() 0 6 1
A getEventDispatcher() 0 3 1
A checkApplication() 0 8 2
A testShowLog() 0 80 2
1
<?php
2
3
namespace Azine\MailgunWebhooksBundle\Tests\Controller;
4
5
use Azine\MailgunWebhooksBundle\DependencyInjection\AzineMailgunWebhooksExtension;
6
use Azine\MailgunWebhooksBundle\Tests\TestHelper;
7
use Doctrine\ORM\EntityManager;
8
use Symfony\Bundle\FrameworkBundle\Client;
9
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
10
use Symfony\Component\DependencyInjection\ContainerInterface;
11
use Symfony\Component\DomCrawler\Crawler;
12
use Symfony\Component\EventDispatcher\EventDispatcher;
13
use Symfony\Component\HttpFoundation\File\UploadedFile;
14
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
15
16
class MailgunEventControllerTest extends WebTestCase
17
{
18
    public function testWebHookCreateAndEventDispatching()
19
    {
20
        $this->checkApplication();
21
22
        $client = static::createClient();
23
        $client->request('GET', '/');
24
        $client->enableProfiler();
25
26
        // get webhook url
27
        $url = $this->getRouter()->generate('mailgunevent_webhook', array('_locale', 'en'), UrlGeneratorInterface::ABSOLUTE_URL);
28
29
        $manager = $this->getEntityManager();
30
        $eventReop = $manager->getRepository("Azine\MailgunWebhooksBundle\Entity\MailgunEvent");
31
        $count = sizeof($eventReop->findAll());
32
33
        // post invalid data to the webhook-url and check the response & database
34
        $invalidPostData = $this->getInvalidPostData();
35
        $webhookdata = json_encode($invalidPostData);
36
        $attachments = array(
37
            'attachment-1' => new UploadedFile(realpath(__DIR__.'/../testAttachment.small.png'), 'some.real.file.name1.png'),
38
            'attachment-2' => new UploadedFile(realpath(__DIR__.'/../testAttachment.small.png'), 'some.real.file.name2.png'),
39
            'attachment-3' => new UploadedFile(realpath(__DIR__.'/../testAttachment.small.png'), 'some.real.file.name3.png'),
40
        );
41
        $crawler = $client->request('POST', $url, $invalidPostData, $attachments);
42
43
        $this->assertSame(401, $client->getResponse()->getStatusCode(), "Response-Code 401 expected for post-data with invalid signature: \n\n$webhookdata\n\n\n");
44
        $this->assertContains('Signature verification failed.', $crawler->text(), 'Response expected.');
45
        $this->assertSame($count, sizeof($eventReop->findAll()), 'No new db entry for the webhook expected!');
46
47
        // post valid data to the webhook-url and check the response
48
        $validPostData = $this->getValidPostData();
49
        $webhookdata = json_encode($validPostData);
50
        $crawler = $client->request('POST', $url, $validPostData, $attachments);
51
        $this->assertSame(200, $client->getResponse()->getStatusCode(), "Response-Code 200 expected for '$url'.\n\n$webhookdata\n\n\n".$client->getResponse()->getContent());
52
        $this->assertContains('Thanx, for the info.', $crawler->text(), 'Response expected.');
53
        $this->assertSame($count + 1, sizeof($eventReop->findAll()), 'One new db entry for the webhook expected!');
54
55
        $validPostData['event'] = 'complained';
56
        $client->request('POST', $url, $validPostData, $attachments);
57
58
        $mailCollector = $client->getProfile()->getCollector('swiftmailer');
59
60
        // checks that an email was sent from the listener
61
        $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

61
        $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...
62
    }
63
64
    private function getValidPostData()
65
    {
66
        $postData = TestHelper::getPostDataWithoutSignature();
67
        $postData['signature'] = $this->getValidSignature($postData['token'], $postData['timestamp']);
68
69
        return $postData;
70
    }
71
72
    private function getInvalidPostData()
73
    {
74
        $postData = TestHelper::getPostDataWithoutSignature();
75
        $postData['signature'] = 'invalid-signature';
76
77
        return $postData;
78
    }
79
80
    /**
81
     * @param string $token
82
     * @param int    $timestamp
83
     */
84
    private function getValidSignature($token, $timestamp)
85
    {
86
        $key = $this->getContainer()->getParameter(AzineMailgunWebhooksExtension::PREFIX.'_'.AzineMailgunWebhooksExtension::API_KEY);
87
        $signature = hash_hmac('SHA256', $timestamp.$token, $key);
88
89
        return $signature;
90
    }
91
92
    public function testShowLog()
93
    {
94
        $this->checkApplication();
95
96
        // Create a new client to browse the application
97
        $client = static::createClient();
98
        $client->followRedirects();
99
100
        $manager = $this->getEntityManager();
101
        $eventReop = $manager->getRepository("Azine\MailgunWebhooksBundle\Entity\MailgunEvent");
102
103
        $apiKey = $this->getContainer()->getParameter(AzineMailgunWebhooksExtension::PREFIX.'_'.AzineMailgunWebhooksExtension::API_KEY);
104
105
        // make sure there is plenty of data in the application to be able to verify paging
106
        if (sizeof($eventReop->findAll()) < 102) {
107
            TestHelper::addMailgunEvents($manager, 102, $apiKey);
108
        }
109
        $count = sizeof($eventReop->findAll());
110
111
        // view the list of events
112
        $pageSize = 25;
113
        $listUrl = substr($this->getRouter()->generate('mailgunevent_list', array('_locale' => 'en', 'page' => 1, 'pageSize' => $pageSize, 'clear' => true)), 13);
114
        $crawler = $this->loginUserIfRequired($client, $listUrl);
115
        $this->assertSame($pageSize + 1, $crawler->filter('.eventsTable tr')->count(), "$pageSize Mailgun events (+1 header row) expected on this page ($listUrl)!");
116
117
        // view a single event
118
        $link = $crawler->filter('.eventsTable tr a:first-child')->first()->link();
119
        $posLastSlash = strrpos($link->getUri(), '/');
120
        $posOfIdStart = strrpos($link->getUri(), '/', -6) + 1;
121
        $eventId = substr($link->getUri(), $posOfIdStart, $posLastSlash - $posOfIdStart);
122
        $crawler = $client->click($link);
123
        $this->assertSame(200, $client->getResponse()->getStatusCode(), 'Status 200 expected.');
124
        $this->assertSame($eventId, $crawler->filter('td')->first()->text(), "Content of first td should be the eventId ($eventId)");
125
126
        // delete the event from show-page
127
        $link = $crawler->selectLink('delete')->link();
128
        $crawler = $client->click($link);
129
130
        // check that it is gone from the list
131
        $this->assertSame(0, $crawler->filter("#event$eventId")->count(), 'The deleted event should not be in the list anymore.');
132
133
        // delete the event from list-page
134
        $crawler = $client->followRedirect();
135
        $link = $crawler->filter('.eventsTable tr .deleteLink')->first()->link();
136
        $delUri = $link->getUri();
137
        $eventId = substr($delUri, strrpos($delUri, '/') + 1);
138
        $crawler = $client->click($link);
139
140
        // check that it is gone from the list
141
        $this->assertSame(0, $crawler->filter("#event$eventId")->count(), 'The deleted event should not be in the list anymore.');
142
143
        // filter the list for something
144
        $crawler = $client->followRedirect();
145
        $form = $crawler->selectButton('Filter')->form();
146
        $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

146
        $form['filter[eventType]']->/** @scrutinizer ignore-call */ 
147
                                    select('delivered');
Loading history...
147
        $crawler = $client->submit($form);
148
        $this->assertSame($crawler->filter('.eventsTable tr')->count() - 1, $crawler->filter(".eventsTable a:contains('delivered')")->count(), "There should only be 'delivered' events in the list");
149
150
        // delete entry with xmlHttpRequest
151
        $eventToDelete = $eventReop->findOneBy(array());
152
        $ajaxUrl = $this->getRouter()->generate('mailgunevent_delete_ajax', array('_locale' => 'en'));
153
        $client->request('POST', $ajaxUrl, array('eventId' => $eventToDelete->getId()), array(), array('HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest'));
154
        $this->assertSame('{"success":true}', $client->getResponse()->getContent(), "JSON response expcted from $ajaxUrl. for event with id:".$eventToDelete->getId());
155
156
        // show/delete inexistent log entry
157
        $inexistentEventId = md5('123invalid');
158
        $url = substr($this->getRouter()->generate('mailgunevent_delete', array('_locale' => 'en', 'eventId' => $inexistentEventId)), 13);
159
        $client->request('GET', $url);
160
        $this->assertSame(404, $client->getResponse()->getStatusCode(), "404 expected for invalid eventId ($inexistentEventId).");
161
162
        $url = substr($this->getRouter()->generate('mailgunevent_show', array('_locale' => 'en', 'id' => $inexistentEventId)), 13);
163
        $client->request('GET', $url);
164
        $this->assertSame(404, $client->getResponse()->getStatusCode(), '404 expected.');
165
166
        // show inexistent page
167
        $maxPage = floor($count / $pageSize);
168
        $beyondListUrl = $this->getRouter()->generate('mailgunevent_list', array('_locale' => 'en', 'page' => $maxPage + 1, 'pageSize' => $pageSize, 'clear' => true));
169
        $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...
170
        $crawler = $client->request('GET', $beyondListUrl);
171
        $this->assertSame(2, $crawler->filter(".pagination .disabled:contains('Next')")->count(), 'Expected to be on the last page => the next button should be disabled.');
172
    }
173
174
    public function testWebViewLinks()
175
    {
176
        $this->checkApplication();
177
178
        // Create a new client to browse the application
179
        $client = static::createClient();
180
        $client->followRedirects();
181
182
        $manager = $this->getEntityManager();
183
        $eventReop = $manager->getRepository("Azine\MailgunWebhooksBundle\Entity\MailgunEvent");
184
185
        $apiKey = $this->getContainer()->getParameter(AzineMailgunWebhooksExtension::PREFIX.'_'.AzineMailgunWebhooksExtension::API_KEY);
186
187
        // make sure there is data in the application
188
        $events = $eventReop->findAll();
189
        if (sizeof($events) < 5) {
190
            TestHelper::addMailgunEvents($manager, 5, $apiKey);
191
            $events = $eventReop->findAll();
192
        }
193
194
        $webViewTokenName = $this->appContainer->getParameter(AzineMailgunWebhooksExtension::PREFIX.'_'.AzineMailgunWebhooksExtension::WEB_VIEW_TOKEN);
0 ignored issues
show
Bug introduced by
The method getParameter() does not exist on null. ( Ignorable by Annotation )

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

194
        /** @scrutinizer ignore-call */ 
195
        $webViewTokenName = $this->appContainer->getParameter(AzineMailgunWebhooksExtension::PREFIX.'_'.AzineMailgunWebhooksExtension::WEB_VIEW_TOKEN);

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...
195
196
        $testTokenValue = 'testValue';
197
        $messageHeader = array($webViewTokenName => $testTokenValue);
198
        $events[0]->setMessageHeaders(json_encode($messageHeader));
199
        $manager->persist($events[0]);
200
        $manager->flush();
201
202
        $events = $eventReop->findAll();
203
        $pageSize = count($events);
204
205
        $listUrl = substr($this->getRouter()->generate('mailgunevent_list', array('_locale' => 'en', 'page' => 1, 'pageSize' => $pageSize, 'clear' => true)), 13);
206
        $crawler = $this->loginUserIfRequired($client, $listUrl);
207
208
        $this->assertSame(1, $crawler->filter("ul:contains('$webViewTokenName')")->count(), 'There should be events with the webView headers in the list');
209
        $this->assertSame(1, $crawler->filter("ul a:contains('$testTokenValue')")->count(), 'There should be events with the webView links in the list');
210
    }
211
212
    /**
213
     * Load the url and login if required.
214
     *
215
     * @param string $url
216
     * @param string $username
217
     * @param Client $client
218
     *
219
     * @return Crawler $crawler of the page of the url or the page after the login
220
     */
221
    private function loginUserIfRequired(Client $client, $url, $username = 'dominik', $password = 'lkjlkjlkjlkj')
222
    {
223
        // try to get the url
224
        $client->followRedirects();
225
        $crawler = $client->request('GET', $url);
226
227
        $this->assertSame(200, $client->getResponse()->getStatusCode(), 'Status-Code 200 expected.');
228
229
        // if redirected to a login-page, login as admin-user
230
        if (5 == $crawler->filter('input')->count() && 1 == $crawler->filter('#username')->count() && 1 == $crawler->filter('#password')->count()) {
231
            // set the password of the admin
232
            $userProvider = $this->getContainer()->get('fos_user.user_provider.username_email');
233
            $user = $userProvider->loadUserByUsername($username);
234
            $user->setPlainPassword($password);
235
            $user->addRole('ROLE_ADMIN');
236
237
            $userManager = $this->getContainer()->get('fos_user.user_manager');
238
            $userManager->updateUser($user);
239
240
            $crawler = $crawler->filter("input[type='submit']");
241
            $form = $crawler->form();
242
            $form->get('_username')->setValue($username);
243
            $form->get('_password')->setValue($password);
244
            $crawler = $client->submit($form);
245
        }
246
247
        $this->assertSame(200, $client->getResponse()->getStatusCode(), 'Login failed.');
248
        $client->followRedirects(false);
249
250
        $this->assertStringEndsWith($url, $client->getRequest()->getUri(), "Login failed or not redirected to requested url: $url vs. ".$client->getRequest()->getUri());
251
252
        return $crawler;
253
    }
254
255
    /**
256
     * @var ContainerInterface
257
     */
258
    private $appContainer;
259
260
    /**
261
     * Get the current container.
262
     *
263
     * @return \Symfony\Component\DependencyInjection\ContainerInterface
264
     */
265
    private function getContainer()
266
    {
267
        if (null == $this->appContainer) {
268
            $this->appContainer = static::$kernel->getContainer();
269
        }
270
271
        return $this->appContainer;
272
    }
273
274
    /**
275
     * @return UrlGeneratorInterface
276
     */
277
    private function getRouter()
278
    {
279
        return $this->getContainer()->get('router');
280
    }
281
282
    /**
283
     * @return EntityManager
284
     */
285
    private function getEntityManager()
286
    {
287
        return $this->getContainer()->get('doctrine.orm.entity_manager');
288
    }
289
290
    /**
291
     * @return EventDispatcher
292
     */
293
    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...
294
    {
295
        return $this->getContainer()->get('event_dispatcher');
296
    }
297
298
    /**
299
     * Check if the current setup is a full application.
300
     * If not, mark the test as skipped else continue.
301
     */
302
    private function checkApplication()
303
    {
304
        try {
305
            static::$kernel = static::createKernel(array());
306
        } catch (\RuntimeException $ex) {
307
            $this->markTestSkipped('There does not seem to be a full application available (e.g. running tests on travis.org). So this test is skipped.');
308
309
            return;
310
        }
311
    }
312
}
313