Completed
Pull Request — master (#39)
by Eugene
01:51
created

MailgunEventControllerTest   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 296
Duplicated Lines 11.15 %

Coupling/Cohesion

Components 1
Dependencies 12

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 12
dl 33
loc 296
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A testWebHookCreateAndEventDispatching() 0 45 1
A getValidPostData() 0 7 1
A getInvalidPostData() 0 7 1
A getValidSignature() 0 7 1
B testShowLog() 0 81 2
A testWebViewLinks() 0 35 2
A loginUserIfRequired() 33 33 4
A getContainer() 0 8 2
A getRouter() 0 4 1
A getEntityManager() 0 4 1
A getEventDispatcher() 0 4 1
A checkApplication() 0 10 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace Azine\MailgunWebhooksBundle\Tests\Controller;
4
5
use Azine\MailgunWebhooksBundle\DependencyInjection\AzineMailgunWebhooksExtension;
6
use Azine\MailgunWebhooksBundle\Entity\MailgunEvent;
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\EventDispatcher\EventSubscriberInterface;
15
use Symfony\Component\HttpFoundation\File\UploadedFile;
16
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
17
18
class MailgunEventControllerTest extends WebTestCase
19
{
20
    public function testWebHookCreateAndEventDispatching()
21
    {
22
        $this->checkApplication();
23
24
        $client = static::createClient();
25
        $client->request('GET', '/');
26
27
        // create a subscriber to listen to create_events.
28
        $subscriberMock = $this->getMockBuilder("Azine\MailgunWebhooksBundle\Tests\EventSubscriberMock")->setMethods(array('handleCreate'))->getMock();
29
        $this->assertTrue($subscriberMock  instanceof EventSubscriberInterface);
30
        $this->getEventDispatcher()->addSubscriber($subscriberMock);
0 ignored issues
show
Documentation introduced by
$subscriberMock is of type object<PHPUnit\Framework\MockObject\MockObject>, but the function expects a object<Symfony\Component...entSubscriberInterface>.

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...
31
        $this->assertTrue($this->getEventDispatcher()->hasListeners(MailgunEvent::CREATE_EVENT));
32
33
        //		dominik I would expect the method handleCreate to be called once, but for some reason it is not.
34
        //		$subscriberMock->expects($this->once())->method("handleCreate");
35
36
        // get webhook url
37
        $url = $this->getRouter()->generate('mailgunevent_webhook', array('_locale', 'en'), UrlGeneratorInterface::ABSOLUTE_URL);
38
39
        $manager = $this->getEntityManager();
40
        $eventReop = $manager->getRepository("Azine\MailgunWebhooksBundle\Entity\MailgunEvent");
41
        $count = sizeof($eventReop->findAll());
42
43
        // post invalid data to the webhook-url and check the response & database
44
        $invalidPostData = $this->getInvalidPostData();
45
        $webhookdata = json_encode($invalidPostData);
46
        $attachments = array(
47
            'attachment-1' => new UploadedFile(realpath(__DIR__.'/../testAttachment.small.png'), 'some.real.file.name1.png'),
48
            'attachment-2' => new UploadedFile(realpath(__DIR__.'/../testAttachment.small.png'), 'some.real.file.name2.png'),
49
            'attachment-3' => new UploadedFile(realpath(__DIR__.'/../testAttachment.small.png'), 'some.real.file.name3.png'),
50
        );
51
        $crawler = $client->request('POST', $url, $invalidPostData, $attachments);
52
53
        $this->assertSame(401, $client->getResponse()->getStatusCode(), "Response-Code 401 expected for post-data with invalid signature: \n\n$webhookdata\n\n\n");
54
        $this->assertContains('Signature verification failed.', $crawler->text(), 'Response expected.');
55
        $this->assertSame($count, sizeof($eventReop->findAll()), 'No new db entry for the webhook expected!');
56
57
        // post valid data to the webhook-url and check the response
58
        $validPostData = $this->getValidPostData();
59
        $webhookdata = json_encode($validPostData);
60
        $crawler = $client->request('POST', $url, $validPostData, $attachments);
61
        $this->assertSame(200, $client->getResponse()->getStatusCode(), "Response-Code 200 expected for '$url'.\n\n$webhookdata\n\n\n".$client->getResponse()->getContent());
62
        $this->assertContains('Thanx, for the info.', $crawler->text(), 'Response expected.');
63
        $this->assertSame($count + 1, sizeof($eventReop->findAll()), 'One new db entry for the webhook expected!');
64
    }
65
66
    private function getValidPostData()
67
    {
68
        $postData = TestHelper::getPostDataWithoutSignature();
69
        $postData['signature'] = $this->getValidSignature($postData['token'], $postData['timestamp']);
70
71
        return $postData;
72
    }
73
74
    private function getInvalidPostData()
75
    {
76
        $postData = TestHelper::getPostDataWithoutSignature();
77
        $postData['signature'] = 'invalid-signature';
78
79
        return $postData;
80
    }
81
82
    /**
83
     * @param string $token
84
     * @param int    $timestamp
85
     */
86
    private function getValidSignature($token, $timestamp)
87
    {
88
        $key = $this->getContainer()->getParameter(AzineMailgunWebhooksExtension::PREFIX.'_'.AzineMailgunWebhooksExtension::API_KEY);
89
        $signature = hash_hmac('SHA256', $timestamp.$token, $key);
90
91
        return $signature;
92
    }
93
94
    public function testShowLog()
95
    {
96
        $this->checkApplication();
97
98
        // Create a new client to browse the application
99
        $client = static::createClient();
100
        $client->followRedirects();
101
102
        $manager = $this->getEntityManager();
103
        $eventReop = $manager->getRepository("Azine\MailgunWebhooksBundle\Entity\MailgunEvent");
104
105
        $apiKey = $this->getContainer()->getParameter(AzineMailgunWebhooksExtension::PREFIX.'_'.AzineMailgunWebhooksExtension::API_KEY);
106
107
        // make sure there is some data in the application
108
        if (sizeof($eventReop->findAll()) < 102) {
109
            TestHelper::addMailgunEvents($manager, 102, $apiKey);
110
        }
111
        $count = sizeof($eventReop->findAll());
112
113
        // view the list of events
114
        $pageSize = 25;
115
        $listUrl = substr($this->getRouter()->generate('mailgunevent_list', array('_locale' => 'en', 'page' => 1, 'pageSize' => $pageSize, 'clear' => true)), 13);
116
        $crawler = $this->loginUserIfRequired($client, $listUrl);
117
        $this->assertSame($pageSize + 1, $crawler->filter('.eventsTable tr')->count(), "$pageSize Mailgun events (+1 header row) expected on this page ($listUrl)!");
118
119
        // view a single event
120
        $link = $crawler->filter('.eventsTable tr a:first-child')->first()->link();
121
        $posLastSlash = strrpos($link->getUri(), '/');
122
        $posOfIdStart = strrpos($link->getUri(), '/', -6) + 1;
123
        $eventId = substr($link->getUri(), $posOfIdStart, $posLastSlash - $posOfIdStart);
124
        $crawler = $client->click($link);
125
        $this->assertSame(200, $client->getResponse()->getStatusCode(), 'Status 200 expected.');
126
        $this->assertSame($eventId, $crawler->filter('td')->first()->text(), "Content of first td should be the eventId ($eventId)");
127
128
        // delete the event from show-page
129
        $link = $crawler->selectLink('delete')->link();
130
        $crawler = $client->click($link);
131
132
        // check that it is gone from the list
133
        $this->assertSame(0, $crawler->filter("#event$eventId")->count(), 'The deleted event should not be in the list anymore.');
134
135
        // delete the event from list-page
136
        $crawler = $client->followRedirect();
137
        $link = $crawler->filter('.eventsTable tr .deleteLink')->first()->link();
138
        $delUri = $link->getUri();
139
        $eventId = substr($delUri, strrpos($delUri, '/') + 1);
140
        $crawler = $client->click($link);
141
142
        // check that it is gone from the list
143
        $this->assertSame(0, $crawler->filter("#event$eventId")->count(), 'The deleted event should not be in the list anymore.');
144
145
        // filter the list for something
146
        $crawler = $client->followRedirect();
147
        $form = $crawler->selectButton('Filter')->form();
148
        $form['filter[eventType]']->select('delivered');
149
        $crawler = $client->submit($form);
150
        $this->assertSame($crawler->filter('.eventsTable tr')->count() - 1, $crawler->filter(".eventsTable a:contains('delivered')")->count(), "There should only be 'delivered' events in the list");
151
152
        // delete entry with xmlHttpRequest
153
        $eventToDelete = $eventReop->findOneBy(array());
154
        $ajaxUrl = $this->getRouter()->generate('mailgunevent_delete_ajax', array('_locale' => 'en'));
155
        $client->request('POST', $ajaxUrl, array('eventId' => $eventToDelete->getId()), array(), array('HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest'));
156
        $this->assertSame('{"success":true}', $client->getResponse()->getContent(), "JSON response expcted from $ajaxUrl. for event with id:".$eventToDelete->getId());
157
158
        // show/delete inexistent log entry
159
        $inexistentEventId = md5('123invalid');
160
        $url = substr($this->getRouter()->generate('mailgunevent_delete', array('_locale' => 'en', 'eventId' => $inexistentEventId)), 13);
161
        $client->request('GET', $url);
162
        $this->assertSame(404, $client->getResponse()->getStatusCode(), "404 expected for invalid eventId ($inexistentEventId).");
163
164
        $url = substr($this->getRouter()->generate('mailgunevent_show', array('_locale' => 'en', 'id' => $inexistentEventId)), 13);
165
        $client->request('GET', $url);
166
        $this->assertSame(404, $client->getResponse()->getStatusCode(), '404 expected.');
167
168
        // show inexistent page
169
        $maxPage = floor($count / $pageSize);
170
        $beyondListUrl = $this->getRouter()->generate('mailgunevent_list', array('_locale' => 'en', 'page' => $maxPage + 1, 'pageSize' => $pageSize, 'clear' => true));
171
        $client->request('GET', $beyondListUrl);
172
        $crawler = $client->followRedirect();
173
        $this->assertSame(2, $crawler->filter(".pagination .disabled:contains('Next')")->count(), 'Expected to be on the last page => the next button should be disabled.');
174
    }
175
176
177
    public function testWebViewLinks()
178
    {
179
        $this->checkApplication();
180
181
        // Create a new client to browse the application
182
        $client = static::createClient();
183
        $client->followRedirects();
184
185
        $manager = $this->getEntityManager();
186
        $eventReop = $manager->getRepository("Azine\MailgunWebhooksBundle\Entity\MailgunEvent");
187
188
        $apiKey = $this->getContainer()->getParameter(AzineMailgunWebhooksExtension::PREFIX.'_'.AzineMailgunWebhooksExtension::API_KEY);
189
190
        // make sure there is some data in the application
191
        if (sizeof($eventReop->findAll()) < 102) {
192
            TestHelper::addMailgunEvents($manager, 102, $apiKey);
193
        }
194
195
        $events = $eventReop->findAll();
196
197
        $testTokenValue = 'testValue';
198
        $messageHeader = [AzineMailgunWebhooksExtension::WEB_VIEW_TOKEN => $testTokenValue];
199
        $events[0]->setMessageHeaders(json_encode($messageHeader));
200
        $manager->persist($events[0]);
201
        $manager->flush();
202
203
        $events = $eventReop->findAll();
204
        $pageSize = count($events);
205
206
        $listUrl = substr($this->getRouter()->generate('mailgunevent_list', array('_locale' => 'en', 'page' => 1, 'pageSize' => $pageSize, 'clear' => true)), 13);
207
        $crawler = $this->loginUserIfRequired($client, $listUrl);
208
209
        $this->assertSame(1, $crawler->filter("ul:contains('".AzineMailgunWebhooksExtension::WEB_VIEW_TOKEN."')")->count(), 'There should be events with the webView headers in the list');
210
        $this->assertSame(1, $crawler->filter("ul a:contains('".$testTokenValue."')")->count(), 'There should be events with the webView links in the list');
211
    }
212
213
    /**
214
     * Load the url and login if required.
215
     *
216
     * @param string $url
217
     * @param string $username
218
     * @param Client $client
219
     *
220
     * @return Crawler $crawler of the page of the url or the page after the login
221
     */
222 View Code Duplication
    private function loginUserIfRequired(Client $client, $url, $username = 'dominik', $password = 'lkjlkjlkjlkj')
223
    {
224
        // try to get the url
225
        $client->followRedirects();
226
        $crawler = $client->request('GET', $url);
227
228
        $this->assertSame(200, $client->getResponse()->getStatusCode(), 'Status-Code 200 expected.');
229
230
        // if redirected to a login-page, login as admin-user
231
        if (5 == $crawler->filter('input')->count() && 1 == $crawler->filter('#username')->count() && 1 == $crawler->filter('#password')->count()) {
232
            // set the password of the admin
233
            $userProvider = $this->getContainer()->get('fos_user.user_provider.username_email');
234
            $user = $userProvider->loadUserByUsername($username);
235
            $user->setPlainPassword($password);
236
            $user->addRole('ROLE_ADMIN');
237
238
            $userManager = $this->getContainer()->get('fos_user.user_manager');
239
            $userManager->updateUser($user);
240
241
            $crawler = $crawler->filter("input[type='submit']");
242
            $form = $crawler->form();
243
            $form->get('_username')->setValue($username);
244
            $form->get('_password')->setValue($password);
245
            $crawler = $client->submit($form);
246
        }
247
248
        $this->assertSame(200, $client->getResponse()->getStatusCode(), 'Login failed.');
249
        $client->followRedirects(false);
250
251
        $this->assertStringEndsWith($url, $client->getRequest()->getUri(), "Login failed or not redirected to requested url: $url vs. ".$client->getRequest()->getUri());
252
253
        return $crawler;
254
    }
255
256
    /**
257
     * @var ContainerInterface
258
     */
259
    private $appContainer;
260
261
    /**
262
     * Get the current container.
263
     *
264
     * @return \Symfony\Component\DependencyInjection\ContainerInterface
265
     */
266
    private function getContainer()
267
    {
268
        if (null == $this->appContainer) {
269
            $this->appContainer = static::$kernel->getContainer();
270
        }
271
272
        return $this->appContainer;
273
    }
274
275
    /**
276
     * @return UrlGeneratorInterface
277
     */
278
    private function getRouter()
279
    {
280
        return $this->getContainer()->get('router');
281
    }
282
283
    /**
284
     * @return EntityManager
285
     */
286
    private function getEntityManager()
287
    {
288
        return $this->getContainer()->get('doctrine.orm.entity_manager');
289
    }
290
291
    /**
292
     * @return EventDispatcher
293
     */
294
    private function getEventDispatcher()
295
    {
296
        return $this->getContainer()->get('event_dispatcher');
297
    }
298
299
    /**
300
     * Check if the current setup is a full application.
301
     * If not, mark the test as skipped else continue.
302
     */
303
    private function checkApplication()
304
    {
305
        try {
306
            static::$kernel = static::createKernel(array());
307
        } catch (\RuntimeException $ex) {
308
            $this->markTestSkipped('There does not seem to be a full application available (e.g. running tests on travis.org). So this test is skipped.');
309
310
            return;
311
        }
312
    }
313
}
314