Completed
Push — master ( 533532...ee214c )
by Rafael
04:23
created

ApiTestCase::getClient()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
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
 * Class ApiTestCase
35
 */
36
class ApiTestCase extends WebTestCase
37
{
38
    // helpers
39
    use DoctrineHelperTrait;
40
    use JsonHelperTrait;
41
    use ResponseHelperTrait;
42
43
    // asserts
44
    use DoctrineAssertTrait;
45
    use JsonAssertTrait;
46
    use ResponseAssertTrait;
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 = true;
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 22
    public function before()
107
    {
108
109 22
    }
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 13
    protected static function getFixtureReference(string $name)
151
    {
152 13
        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();
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