Passed
Push — master ( 319fcb...7a107a )
by Rafael
04:41
created

ApiTestCase::send()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 12
cts 12
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 11
nc 2
nop 3
crap 2
1
<?php
2
3
/*******************************************************************************
4
 *  This file is part of the GraphQL Bundle package.
5
 *
6
 *  (c) YnloUltratech <[email protected]>
7
 *
8
 *  For the full copyright and license information, please view the LICENSE
9
 *  file that was distributed with this source code.
10
 ******************************************************************************/
11
12
namespace Ynlo\GraphQLBundle\Test;
13
14
use Doctrine\Common\DataFixtures\ReferenceRepository;
15
use Doctrine\Common\Util\ClassUtils;
16
use PHPUnit\Util\Blacklist;
17
use Symfony\Bundle\FrameworkBundle\Client;
18
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
19
use Symfony\Component\HttpFoundation\Request;
20
use Symfony\Component\HttpFoundation\Response;
21
use Symfony\Component\Stopwatch\Stopwatch;
22
use Ynlo\GraphQLBundle\Definition\Registry\DefinitionRegistry;
23
use Ynlo\GraphQLBundle\Model\ID;
24
use Ynlo\GraphQLBundle\Model\NodeInterface;
25
use Ynlo\GraphQLBundle\Test\Assert\DoctrineAssertTrait;
26
use Ynlo\GraphQLBundle\Test\Assert\JsonAssertTrait;
27
use Ynlo\GraphQLBundle\Test\Assert\ResponseAssertTrait;
28
use Ynlo\GraphQLBundle\Test\FixtureLoader\FixtureLoader;
29
use Ynlo\GraphQLBundle\Test\Helper\DoctrineHelperTrait;
30
use Ynlo\GraphQLBundle\Test\Helper\JsonHelperTrait;
31
use Ynlo\GraphQLBundle\Test\Helper\ResponseHelperTrait;
32
33
/**
34
 * @deprecated in favor of Behat tests
35
 */
36
class ApiTestCase extends WebTestCase
37
{
38
    // helpers
39
    use DoctrineHelperTrait;
0 ignored issues
show
Deprecated Code introduced by
The trait Ynlo\GraphQLBundle\Test\Helper\DoctrineHelperTrait has been deprecated: in favor of Behat tests ( Ignorable by Annotation )

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

39
    use /** @scrutinizer ignore-deprecated */ DoctrineHelperTrait;

This trait has been deprecated. The supplier of the trait has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the trait will be removed and what other trait to use instead.

Loading history...
40
    use JsonHelperTrait;
0 ignored issues
show
Deprecated Code introduced by
The trait Ynlo\GraphQLBundle\Test\Helper\JsonHelperTrait has been deprecated: in favor of Behat tests ( Ignorable by Annotation )

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

40
    use /** @scrutinizer ignore-deprecated */ JsonHelperTrait;

This trait has been deprecated. The supplier of the trait has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the trait will be removed and what other trait to use instead.

Loading history...
41
    use ResponseHelperTrait;
0 ignored issues
show
Deprecated Code introduced by
The trait Ynlo\GraphQLBundle\Test\Helper\ResponseHelperTrait has been deprecated: in favor of Behat tests ( Ignorable by Annotation )

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

41
    use /** @scrutinizer ignore-deprecated */ ResponseHelperTrait;

This trait has been deprecated. The supplier of the trait has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the trait will be removed and what other trait to use instead.

Loading history...
42
43
    // asserts
44
    use DoctrineAssertTrait;
0 ignored issues
show
Deprecated Code introduced by
The trait Ynlo\GraphQLBundle\Test\Assert\DoctrineAssertTrait has been deprecated: in favor of Behat tests ( Ignorable by Annotation )

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

44
    use /** @scrutinizer ignore-deprecated */ DoctrineAssertTrait;

This trait has been deprecated. The supplier of the trait has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the trait will be removed and what other trait to use instead.

Loading history...
45
    use JsonAssertTrait;
0 ignored issues
show
Deprecated Code introduced by
The trait Ynlo\GraphQLBundle\Test\Assert\JsonAssertTrait has been deprecated: in favor of Behat tests ( Ignorable by Annotation )

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

45
    use /** @scrutinizer ignore-deprecated */ JsonAssertTrait;

This trait has been deprecated. The supplier of the trait has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the trait will be removed and what other trait to use instead.

Loading history...
46
    use ResponseAssertTrait;
0 ignored issues
show
Bug introduced by
The trait Ynlo\GraphQLBundle\Test\Assert\ResponseAssertTrait requires the property $headers which is not provided by Ynlo\GraphQLBundle\Test\ApiTestCase.
Loading history...
Deprecated Code introduced by
The trait Ynlo\GraphQLBundle\Test\Assert\ResponseAssertTrait has been deprecated: in favor of Behat tests ( Ignorable by Annotation )

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

46
    use /** @scrutinizer ignore-deprecated */ ResponseAssertTrait;

This trait has been deprecated. The supplier of the trait has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the trait will be removed and what other trait to use instead.

Loading history...
47
48
    /**
49
     * @var ReferenceRepository
50
     */
51
    protected static $referenceRepository;
52
53
    /**
54
     * @var Client
55
     */
56
    protected static $client;
57
58
    /**
59
     * Whether to insulate each request or not.
60
     * The request ran into a separate process to avoid
61
     * any conflict (cache, container, doctrine etc..) with previous requests.
62
     *
63
     * NOTE: should be disabled for debugging
64
     *
65
     * @var bool
66
     */
67
    protected static $insulateRequests = false;
68
69
    /**
70
     * Endpoint where the API is available, e.g. /api
71
     *
72
     * @var string
73
     */
74
    protected static $endpoint = '';
75
76
    private static $lastQuery;
77
78
    private static $lastQueryExecutionTime = 0;
79
80
    public function __construct(string $name = null, array $data = [], string $dataName = '')
81
    {
82
        parent::__construct($name, $data, $dataName);
83
84
        Blacklist::$blacklistedClassNames['Ynlo\GraphQLBundle\Test\ApiTestCase'] = 1;
85
    }
86
87
    final public static function setUpBeforeClass()
88
    {
89
        static::$client = null;
90
    }
91
92 22
    final public function setUp()
93
    {
94 22
        parent::setUp();
95
96 22
        self::$lastQuery = null;
97 22
        self::$lastQueryExecutionTime = 0;
98
99 22
        static::loadFixtures();
100 22
        $this->before();
101 22
    }
102
103
    /**
104
     * This method is called before a test is executed.
105
     */
106
    public function before()
107
    {
108
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114 22
    final protected function tearDown()
115
    {
116 22
        parent::tearDown();
117 22
        static::$client = null;
118 22
        $this->after();
119 22
    }
120
121
    /**
122
     * This method is called after a test is executed.
123
     */
124 22
    public function after()
125
    {
126
127 22
    }
128
129 22
    protected static function createClient(array $options = [], array $server = [])
130
    {
131 22
        return static::$client = parent::createClient($options, $server);
132
    }
133
134 22
    protected static function getClient(): Client
135
    {
136 22
        return static::$client ?? static::createClient();
137
    }
138
139 22
    protected static function loadFixtures($classNames = [])
140
    {
141
        /** @var Client $client */
142 22
        $client = static::getClient();
143 22
        $container = $client->getContainer();
144 22
        if ($container) {
145 22
            $fixtureLoader = new FixtureLoader($container, $container->get('doctrine'));
146 22
            static::$referenceRepository = $fixtureLoader->loadFixtures($classNames);
147
        }
148 22
    }
149
150 22
    protected static function getFixtureReference(string $name)
151
    {
152 22
        return self::$referenceRepository->getReference($name);
153
    }
154
155
    /**
156
     * @param string    $query     query or mutation in GraphQL format
157
     * @param array     $variables array of variables
158
     * @param bool|null $insulate  Whether to insulate the requests or not. Leave null to use default value.
159
     *                             The request ran into a separate process to avoid
160
     *                             any conflict (cache, container, doctrine etc..) with previous requests.
161
     *                             NOTE: should be disabled for debugging
162
     *
163
     * @return Response
164
     */
165 22
    protected static function send($query, array $variables = [], $insulate = null): Response
166
    {
167 22
        self::$lastQuery = ['query' => $query, 'variables' => $variables];
168
169 22
        if (null === $insulate) {
170 22
            $insulate = static::$insulateRequests;
171
        }
172
173 22
        $client = static::getClient();
174 22
        $client->insulate($insulate);
175
176 22
        $watch = new Stopwatch();
177 22
        $watch->start('query');
178 22
        $client->request(Request::METHOD_POST, self::$endpoint, [], [], [], json_encode(self::$lastQuery));
179 22
        $watch->stop('query');
180 22
        self::$lastQueryExecutionTime = $watch->getEvent('query')->getDuration();
181
182 22
        return $client->getResponse();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $client->getResponse() could return the type null which is incompatible with the type-hinted return Symfony\Component\HttpFoundation\Response. Consider adding an additional type-check to rule them out.
Loading history...
183
    }
184
185
    /**
186
     * Use this method to get the global ID of a existent fixture
187
     *
188
     * getFixtureGlobalId('user1') => VXNlcjox
189
     */
190 3
    protected static function getFixtureGlobalId(string $name): string
191
    {
192 3
        $fixture = static::getFixtureReference($name);
193 3
        if ($fixture instanceof NodeInterface) {
194 3
            return static::encodeID($fixture);
195
        }
196
197
        throw new \RuntimeException(sprintf('The fixture must implements %s', NodeInterface::class));
198
    }
199
200
    /**
201
     * Encode given id or node interface to global ID
202
     *
203
     * encodeID($user) => VXNlcjox
204
     * encodeID(1, 'User') => VXNlcjox
205
     *
206
     * @param string|NodeInterface $node     id of the node or instance
207
     * @param string               $nodeType required in case the first argument is scalar
208
     *
209
     * @return string
210
     */
211 11
    protected static function encodeID($node, $nodeType = null)
212
    {
213 11
        if (!is_object($node) && !$nodeType) {
214
            throw new \RuntimeException('Node type is required when use scalar ID as node');
215
        }
216
217 11
        if ($node instanceof NodeInterface) {
218 11
            $nodeType = static::getClient()
219 11
                              ->getContainer()
220 11
                              ->get(DefinitionRegistry::class)
221 11
                              ->getEndpoint()
222 11
                              ->getTypeForClass(ClassUtils::getClass($node));
223
224 11
            $node = $node->getId();
225
        }
226
227 11
        return ID::encode($nodeType, $node);
228
    }
229
230
    /**
231
     * Decode globalId to get type and real database id
232
     */
233
    protected static function decodeID(string $globalID): ID
234
    {
235
        return ID::createFromString($globalID);
236
    }
237
238
    /**
239
     * Print helpful debug information for latest executed query
240
     */
241
    protected static function debugLastQuery()
242
    {
243
        if (self::$lastQuery) {
244
            $query = self::$lastQuery['query'] ?? null;
245
246
            $type = 'QUERY';
247
            if (preg_match('/^\s*mutation/', $query)) {
248
                $type = 'MUTATION';
249
            }
250
251
            $variables = self::$lastQuery['variables'] ?? null;
252
253
            print_r("\033[43m\n\n----------------------- $type ---------------------\n\n\033[0m");
254
            print_r($query ?? null);
255
            print_r("\n\n");
256
            print_r("\033[46m------------------- VARIABLES-----------------------\n\n\033[0m");
257
            print_r(json_encode($variables, JSON_PRETTY_PRINT));
258
            print_r("\n\n");
259
260
            /** @var Response $response */
261
            $response = static::getClient()->getResponse();
262
            $bg = $response->getStatusCode() >= 400 ? 41: 42;
263
            print_r("\033[{$bg}m-------------------- RESPONSE ----------------------\n\n\033[0m");
264
            print_r(sprintf("STATUS: [%s] %s \n", $response->getStatusCode(), Response::$statusTexts[$response->getStatusCode()] ?? 'Unknown Status'));
265
            print_r(sprintf("TIME: %s ms \n\n", self::$lastQueryExecutionTime));
266
267
            $content = $response->getContent();
268
            $json = @json_decode($content, true);
269
            if ($json) {
270
                print_r(json_encode($json, JSON_PRETTY_PRINT));
271
            } else {
272
                print_r($content);
273
            }
274
            print_r("\n\n");
275
            print_r("-----------------------------------------------------\n\n");
276
        } else {
277
            throw new \RuntimeException('Does not exist any executed query on current test, try use this method after "send" the query.');
278
        }
279
    }
280
281 22
    protected function runTest()
282
    {
283
        try {
284 22
            parent::runTest();
285
        } catch (\Exception $exception) {
286
            if (self::$lastQuery) {
287
                self::debugLastQuery();
288
            }
289
290
            throw $exception;
291
        }
292 22
    }
293
}
294
295