GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#69)
by joseph
14:54
created

generateStandardFieldEntity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta;
4
5
use Doctrine\Common\Inflector\Inflector;
6
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Command\GenerateFieldCommand;
7
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\AbstractGenerator;
8
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Field\FieldGenerator;
9
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\RelationsGenerator;
10
use EdmondsCommerce\DoctrineStaticMeta\Entity\Embeddable\Objects\Financial\MoneyEmbeddable;
11
use EdmondsCommerce\DoctrineStaticMeta\Entity\Embeddable\Objects\Geo\AddressEmbeddable;
12
use EdmondsCommerce\DoctrineStaticMeta\Entity\Embeddable\Objects\Identity\FullNameEmbeddable;
13
use EdmondsCommerce\DoctrineStaticMeta\Entity\Embeddable\Traits\Financial\HasMoneyEmbeddableTrait;
14
use EdmondsCommerce\DoctrineStaticMeta\Entity\Embeddable\Traits\Geo\HasAddressEmbeddableTrait;
15
use EdmondsCommerce\DoctrineStaticMeta\Entity\Embeddable\Traits\Identity\HasFullNameEmbeddableTrait;
16
use EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException;
17
use EdmondsCommerce\PHPQA\Constants;
18
19
/**
20
 * Class GeneratedCodeTest
21
 *
22
 * @package EdmondsCommerce\DoctrineStaticMeta\GeneratedCode
23
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
24
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
25
 */
26
class FullProjectBuildFunctionalTest extends AbstractFunctionalTest
27
{
28
    public const TEST_ENTITY_NAMESPACE_BASE = self::TEST_PROJECT_ROOT_NAMESPACE
29
                                              .'\\'.AbstractGenerator::ENTITIES_FOLDER_NAME;
30
31
    public const TEST_FIELD_TRAIT_NAMESPACE = self::TEST_FIELD_NAMESPACE_BASE.'\\Traits\\';
32
33
    public const TEST_ENTITY_PERSON        = self::TEST_ENTITY_NAMESPACE_BASE.'\\Person';
34
    public const TEST_ENTITY_ADDRESS       = self::TEST_ENTITY_NAMESPACE_BASE.'\\Attributes\\Address';
35
    public const TEST_ENTITY_EMAIL         = self::TEST_ENTITY_NAMESPACE_BASE.'\\Attributes\\Email';
36
    public const TEST_ENTITY_COMPANY       = self::TEST_ENTITY_NAMESPACE_BASE.'\\Company';
37
    public const TEST_ENTITY_DIRECTOR      = self::TEST_ENTITY_NAMESPACE_BASE.'\\Company\\Director';
38
    public const TEST_ENTITY_ORDER         = self::TEST_ENTITY_NAMESPACE_BASE.'\\Order';
39
    public const TEST_ENTITY_ORDER_ADDRESS = self::TEST_ENTITY_NAMESPACE_BASE.'\\Order\\Address';
40
41
    public const TEST_ENTITY_NAME_SPACING_COMPANY        = self::TEST_ENTITY_NAMESPACE_BASE.'\\Company';
42
    public const TEST_ENTITY_NAME_SPACING_SOME_CLIENT    = self::TEST_ENTITY_NAMESPACE_BASE.'\\Some\\Client';
43
    public const TEST_ENTITY_NAME_SPACING_ANOTHER_CLIENT = self::TEST_ENTITY_NAMESPACE_BASE
44
                                                           .'\\Another\\Deeply\\Nested\\Client';
45
46
    public const TEST_ENTITIES = [
47
        self::TEST_ENTITY_PERSON,
48
        self::TEST_ENTITY_ADDRESS,
49
        self::TEST_ENTITY_EMAIL,
50
        self::TEST_ENTITY_COMPANY,
51
        self::TEST_ENTITY_DIRECTOR,
52
        self::TEST_ENTITY_ORDER,
53
        self::TEST_ENTITY_ORDER_ADDRESS,
54
        self::TEST_ENTITY_NAME_SPACING_COMPANY,
55
        self::TEST_ENTITY_NAME_SPACING_SOME_CLIENT,
56
        self::TEST_ENTITY_NAME_SPACING_ANOTHER_CLIENT,
57
    ];
58
59
    public const TEST_RELATIONS = [
60
        [self::TEST_ENTITY_PERSON, RelationsGenerator::HAS_UNIDIRECTIONAL_MANY_TO_ONE, self::TEST_ENTITY_ADDRESS],
61
        [self::TEST_ENTITY_PERSON, RelationsGenerator::HAS_ONE_TO_MANY, self::TEST_ENTITY_EMAIL],
62
        [self::TEST_ENTITY_COMPANY, RelationsGenerator::HAS_MANY_TO_MANY, self::TEST_ENTITY_DIRECTOR],
63
        [self::TEST_ENTITY_COMPANY, RelationsGenerator::HAS_ONE_TO_MANY, self::TEST_ENTITY_ADDRESS],
64
        [self::TEST_ENTITY_COMPANY, RelationsGenerator::HAS_UNIDIRECTIONAL_ONE_TO_MANY, self::TEST_ENTITY_EMAIL],
65
        [self::TEST_ENTITY_DIRECTOR, RelationsGenerator::HAS_ONE_TO_ONE, self::TEST_ENTITY_PERSON],
66
        [self::TEST_ENTITY_ORDER, RelationsGenerator::HAS_MANY_TO_ONE, self::TEST_ENTITY_PERSON],
67
        [self::TEST_ENTITY_ORDER, RelationsGenerator::HAS_ONE_TO_MANY, self::TEST_ENTITY_ORDER_ADDRESS],
68
        [self::TEST_ENTITY_ORDER_ADDRESS, RelationsGenerator::HAS_UNIDIRECTIONAL_ONE_TO_ONE, self::TEST_ENTITY_ADDRESS],
69
        [
70
            self::TEST_ENTITY_NAME_SPACING_COMPANY,
71
            RelationsGenerator::HAS_ONE_TO_ONE,
72
            self::TEST_ENTITY_NAME_SPACING_SOME_CLIENT,
73
        ],
74
        [
75
            self::TEST_ENTITY_NAME_SPACING_COMPANY,
76
            RelationsGenerator::HAS_ONE_TO_ONE,
77
            self::TEST_ENTITY_NAME_SPACING_ANOTHER_CLIENT,
78
        ],
79
    ];
80
81
    public const TEST_FIELD_NAMESPACE_BASE = self::TEST_PROJECT_ROOT_NAMESPACE.'\\Entity\\Fields';
82
83
    public const UNIQUEABLE_FIELD_TYPES = [
84
        MappingHelper::TYPE_INTEGER,
85
        MappingHelper::TYPE_STRING,
86
    ];
87
88
    public const EMBEDDABLE_TRAIT_BASE = self::TEST_PROJECT_ROOT_NAMESPACE.'\\Entity\\Embeddable\\Traits';
89
90
    public const TEST_EMBEDDABLES = [
91
        [
92
            MoneyEmbeddable::class,
93
            self::EMBEDDABLE_TRAIT_BASE.'\\Financial\\HasPriceEmbeddableTrait',
94
            'PriceEmbeddable',
95
        ],
96
        [
97
            AddressEmbeddable::class,
98
            self::EMBEDDABLE_TRAIT_BASE.'\\Geo\\HasHeadOfficeEmbeddableTrait',
99
            'HeadOfficeEmbeddable',
100
        ],
101
        [
102
            FullNameEmbeddable::class,
103
            self::EMBEDDABLE_TRAIT_BASE.'\\Identity\\HasPersonEmbeddableTrait',
104
            'PersonEmbeddable',
105
        ],
106
    ];
107
108
    protected function assertWeCheckAllPossibleRelationTypes(): void
109
    {
110
        $included = $toTest = [];
111
        foreach (RelationsGenerator::HAS_TYPES as $hasType) {
112
            if (0 === \strpos($hasType, RelationsGenerator::PREFIX_INVERSE)) {
113
                continue;
114
            }
115
            $toTest[$hasType] = true;
116
        }
117
        \ksort($toTest);
118
        foreach (self::TEST_RELATIONS as $relation) {
119
            $included[$relation[1]] = true;
120
        }
121
        \ksort($included);
122
        $missing = \array_diff(\array_keys($toTest), \array_keys($included));
123
        self::assertEmpty(
124
            $missing,
125
            'We are not testing all relation types - '
126
            .'these ones have not been included: '
127
            .print_r($missing, true)
128
        );
129
    }
130
131
    public const BASH_PHPNOXDEBUG_FUNCTION = <<<'BASH'
132
function phpNoXdebug {
133
    debugMode="off"
134
    if [[ "$-" == *x* ]]
135
    then
136
        debugMode='on'
137
    fi
138
    if [[ "$debugMode" == "on" ]]
139
    then
140
        set +x
141
    fi
142
    local returnCode;
143
    local temporaryPath="$(mktemp -t php.XXXX).ini"
144
    # Using awk to ensure that files ending without newlines do not lead to configuration error
145
    /usr/bin/php -i | grep "\.ini" | grep -o -e '\(/[a-z0-9._-]\+\)\+\.ini' | grep -v xdebug \
146
        | xargs awk 'FNR==1{print ""}1' > "$temporaryPath"
147
    #Run PHP with temp config with no xdebug, display errors on stderr
148
    set +e
149
    /usr/bin/php -n -c "$temporaryPath" "$@"    
150
    returnCode=$?
151
    set -e
152
    rm -f "$temporaryPath"
153
    if [[ "$debugMode" == "on" ]]
154
    then
155
        set -x
156
    fi
157
    return ${returnCode};    
158
}
159
160
BASH;
161
    /**
162
     * @var string
163
     */
164
    private $workDir;
165
166
    /**
167
     * We need to check for uncommited changes in the main project. If there are, then the generated code tests will
168
     * not get them as it works by cloning this repo via the filesystem
169
     */
170
    protected function assertNoUncommitedChanges(): void
171
    {
172
        if ($this->isTravis()) {
173
            return;
174
        }
175
        exec("git status | grep -E 'nothing to commit, working .*? clean' ", $output, $exitCode);
176
        if (0 !== $exitCode) {
177
            $this->markTestSkipped(
178
                'uncommitted changes detected in this project, '
179
                .'there is no point running the generated code test as it will not have your uncommitted changes.'
180
                ."\n\n".implode("\n", $output)
181
            );
182
        }
183
    }
184
185
    /**
186
     * @throws \Exception
187
     * @throws \Psr\Container\ContainerExceptionInterface
188
     * @throws \Psr\Container\NotFoundExceptionInterface
189
     * @SuppressWarnings(PHPMD.Superglobals)
190
     * @SuppressWarnings(PHPMD.StaticAccess)
191
     */
192
    public function setup()
193
    {
194
        if (isset($_SERVER[Constants::QA_QUICK_TESTS_KEY])
195
            && (int)$_SERVER[Constants::QA_QUICK_TESTS_KEY] === Constants::QA_QUICK_TESTS_ENABLED
196
        ) {
197
            return;
198
        }
199
        $this->assertNoUncommitedChanges();
200
        $this->workDir      = $this->isTravis() ?
201
            AbstractIntegrationTest::VAR_PATH.'/GeneratedCodeTest'
202
            : sys_get_temp_dir().'/dsm/test-project';
203
        $this->entitiesPath = $this->workDir.'/src/Entities';
204
        $this->getFileSystem()->mkdir($this->workDir);
205
        $this->emptyDirectory($this->workDir.'');
206
        $this->getFileSystem()->mkdir($this->entitiesPath);
207
        $this->setupContainer($this->entitiesPath);
208
        $this->initRebuildFile();
209
        $this->setupGeneratedDb();
210
        $this->initComposerAndInstall();
211
        $fileSystem = $this->getFileSystem();
212
        $fileSystem->mkdir(
213
            [
214
                $this->workDir.'/tests/',
215
                $this->workDir.'/cache/Proxies',
216
                $this->workDir.'/cache/qa',
217
                $this->workDir.'/qaConfig',
218
            ]
219
        );
220
        file_put_contents(
221
            $this->workDir.'/qaConfig/phpunit.xml',
222
            <<<XML
223
<phpunit
224
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
225
        xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/3.7/phpunit.xsd"
226
        cacheTokens="false"
227
        colors="true"
228
        verbose="true"
229
        bootstrap="../tests/bootstrap.php"
230
>
231
    <testsuites>
232
        <testsuite name="tests">
233
            <directory suffix="Test.php">../tests/</directory>
234
        </testsuite>
235
    </testsuites>
236
</phpunit>
237
XML
238
        );
239
        $fileSystem->symlink($this->workDir.'/qaConfig/phpunit.xml', $this->workDir.'/phpunit.xml');
240
241
        $fileSystem->copy(
242
            __DIR__.'/../../qaConfig/qaConfig.inc.bash',
243
            $this->workDir.'/qaConfig/qaConfig.inc.bash'
244
        );
245
        $fileSystem->copy(__DIR__.'/../../cli-config.php', $this->workDir.'/cli-config.php');
246
        file_put_contents($this->workDir.'/README.md', '#Generated Code');
247
248
        $this->addToRebuildFile(self::BASH_PHPNOXDEBUG_FUNCTION);
249
250
        $entities            = $this->generateEntities();
251
        $standardFieldEntity = $this->generateStandardFieldEntity();
252
        $this->generateRelations();
253
        $this->generateFields();
254
        $this->setFields(
255
            $entities,
256
            $this->getFieldFqns()
257
        );
258
        $this->setFields(
259
            [$standardFieldEntity],
260
            FieldGenerator::STANDARD_FIELDS
261
        );
262
        foreach ($entities as $entityFqn) {
263
            foreach ([
264
                         HasMoneyEmbeddableTrait::class,
265
                         HasFullNameEmbeddableTrait::class,
266
                         HasAddressEmbeddableTrait::class,
267
                     ] as $embeddableTraitFqn) {
268
                $this->setEmbeddable($entityFqn, $embeddableTraitFqn);
269
            }
270
            foreach (self::TEST_EMBEDDABLES as list($archetypeObject, $traitFqn, $className)) {
271
                $this->generateEmbeddable(
272
                    '\\'.$archetypeObject,
273
                    $className
274
                );
275
                $this->setEmbeddable($entityFqn, $traitFqn);
276
            }
277
        }
278
    }
279
280
    protected function initRebuildFile(): void
281
    {
282
        $bash =
283
            <<<'BASH'
284
#!/usr/bin/env bash
285
readonly DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )";
286
cd "$DIR";
287
set -e
288
set -u
289
set -o pipefail
290
standardIFS="$IFS"
291
IFS=$'\n\t'
292
echo "
293
===========================================
294
$(hostname) $0 $@
295
===========================================
296
"
297
# Error Handling
298
backTraceExit () {
299
    local err=$?
300
    set +o xtrace
301
    local code="${1:-1}"
302
    printf "\n\nError in ${BASH_SOURCE[1]}:${BASH_LINENO[0]}. '${BASH_COMMAND}'\n\n exited with status: \n\n$err\n\n"
303
    # Print out the stack trace described by $function_stack
304
    if [ ${#FUNCNAME[@]} -gt 2 ]
305
    then
306
        echo "Call tree:"
307
        for ((i=1;i<${#FUNCNAME[@]}-1;i++))
308
        do
309
            echo " $i: ${BASH_SOURCE[$i+1]}:${BASH_LINENO[$i]} ${FUNCNAME[$i]}(...)"
310
        done
311
    fi
312
    echo "Exiting with status ${code}"
313
    exit "${code}"
314
}
315
trap 'backTraceExit' ERR
316
set -o errtrace
317
# Error Handling Ends
318
319
echo "clearing out generated code"
320
rm -rf src/* tests/*
321
322
echo "preparing empty Entities directory"
323
mkdir src/Entities
324
325
echo "making sure we have the latest version of code"
326
(cd vendor/edmondscommerce/doctrine-static-meta && git pull)
327
328
BASH;
329
        if (!$this->isTravis()) {
330
            $bash .= self::BASH_PHPNOXDEBUG_FUNCTION;
331
        }
332
        file_put_contents(
333
            $this->workDir.'/rebuild.bash',
334
            "\n\n".$bash
335
        );
336
    }
337
338
339
    /**
340
     * @return string Generated Database Name
341
     * @throws \Exception
342
     * @throws \Psr\Container\ContainerExceptionInterface
343
     * @throws \Psr\Container\NotFoundExceptionInterface
344
     */
345
    protected function setupGeneratedDb(): string
346
    {
347
        $dbHost = $this->container->get(Config::class)->get(ConfigInterface::PARAM_DB_HOST);
348
        $dbUser = $this->container->get(Config::class)->get(ConfigInterface::PARAM_DB_USER);
349
        $dbPass = $this->container->get(Config::class)->get(ConfigInterface::PARAM_DB_PASS);
350
        $dbName = $this->container->get(Config::class)->get(ConfigInterface::PARAM_DB_NAME);
351
        $link   = mysqli_connect($dbHost, $dbUser, $dbPass);
352
        if (!$link) {
0 ignored issues
show
introduced by
$link is of type mysqli, thus it always evaluated to true.
Loading history...
353
            throw new DoctrineStaticMetaException('Failed getting connection in '.__METHOD__);
354
        }
355
        $generatedDbName = $dbName.'_generated';
356
        mysqli_query($link, "DROP DATABASE IF EXISTS $generatedDbName");
357
        mysqli_query($link, "CREATE DATABASE $generatedDbName 
358
        CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci");
359
        mysqli_close($link);
360
361
        $rebuildBash = <<<BASH
362
echo "Dropping and creating the DB $generatedDbName"        
363
mysql -u $dbUser -p$dbPass -h $dbHost -e "DROP DATABASE IF EXISTS $generatedDbName";
364
mysql -u $dbUser -p$dbPass -h $dbHost -e "CREATE DATABASE $generatedDbName 
365
CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci";
366
BASH;
367
        $this->addToRebuildFile($rebuildBash);
368
        file_put_contents(
369
            $this->workDir.'/.env',
370
            <<<EOF
371
export dbUser="{$dbUser}"
372
export dbPass="{$dbPass}"
373
export dbHost="{$dbHost}"
374
export dbName="$generatedDbName"
375
EOF
376
        );
377
378
        return $generatedDbName;
379
    }
380
381
    /**
382
     * @param string $bash
383
     *
384
     * @return bool
385
     * @throws \Exception
386
     */
387
    protected function addToRebuildFile(string $bash): bool
388
    {
389
        $result = file_put_contents(
390
            $this->workDir.'/rebuild.bash',
391
            "\n\n".$bash."\n\n",
392
            FILE_APPEND
393
        );
394
        if (!$result) {
395
            throw new \RuntimeException('Failed writing to rebuild file');
396
        }
397
398
        return true;
399
    }
400
401
    protected function initComposerAndInstall(): void
402
    {
403
        $vcsPath      = realpath(__DIR__.'/../../../doctrine-static-meta/');
404
        $namespace    = str_replace('\\', '\\\\', self::TEST_PROJECT_ROOT_NAMESPACE);
405
        $composerJson = <<<JSON
406
{
407
  "require": {
408
    "edmondscommerce/doctrine-static-meta": "dev-%s",
409
    "edmondscommerce/typesafe-functions": "dev-master@dev"
410
  },
411
  "repositories": [
412
    {
413
      "type": "vcs",
414
      "url": "%s"
415
    },
416
    {
417
      "type": "vcs",
418
      "url": "https://github.com/edmondscommerce/Faker.git"
419
    }
420
  ],
421
  "minimum-stability": "stable",
422
  "require-dev": {
423
    "fzaninotto/faker": "dev-dsm-patches@dev",
424
    "edmondscommerce/phpqa": "1.0.1"
425
  },
426
  "autoload": {
427
    "psr-4": {
428
      "$namespace\\\\": [
429
        "src/"
430
      ]
431
    }
432
  },
433
  "autoload-dev": {
434
    "psr-4": {
435
      "$namespace\\\\": [
436
        "tests/"
437
      ]
438
    }
439
  },
440
  "config": {
441
    "bin-dir": "bin",
442
    "preferred-install": {
443
      "edmondscommerce/*": "source",
444
      "fzaninotto/faker": "source",
445
      "*": "dist"
446
    },
447
    "optimize-autoloader": true
448
  }
449
}
450
JSON;
451
452
        $gitCurrentBranchName = trim(shell_exec("git branch | grep '*' | cut -d ' ' -f 2"));
453
        file_put_contents(
454
            $this->workDir.'/composer.json',
455
            sprintf($composerJson, $gitCurrentBranchName, $vcsPath)
456
        );
457
458
        $phpCmd   = $this->isTravis() ? 'php' : 'phpNoXdebug';
459
        $bashCmds = <<<BASH
460
           
461
$phpCmd $(which composer) install \
462
    --prefer-dist
463
464
$phpCmd $(which composer) dump-autoload --optimize
465
466
BASH;
467
        $this->execBash($bashCmds);
468
    }
469
470
    /**
471
     * Runs bash with strict error handling and verbose logging
472
     *
473
     * Will ensure the phpNoXdebugFunction is available and will CD into the correct directory before running commands
474
     *
475
     * Asserts that the command returns with an exit code of 0
476
     *
477
     * Appends to the rebuild file allowing easy rerunning of the commmands in the test project
478
     *
479
     * @param string $bashCmds
480
     *
481
     * @throws \Exception
482
     */
483
    protected function execBash(string $bashCmds): void
484
    {
485
        fwrite(STDERR, "\n\t# Executing:\n\t$bashCmds");
486
        $startTime = microtime(true);
487
488
        $fullCmds = '';
489
        if (!$this->isTravis()) {
490
            $fullCmds .= "\n".self::BASH_PHPNOXDEBUG_FUNCTION."\n\n";
491
        }
492
        $fullCmds .= "set -xe;\n";
493
        $fullCmds .= "cd {$this->workDir};\n";
494
        #$fullCmds .= "exec 2>&1;\n";
495
        $fullCmds .= "$bashCmds\n";
496
497
        $output   = [];
498
        $exitCode = 0;
499
        exec($fullCmds, $output, $exitCode);
500
501
        if (0 !== $exitCode) {
502
            throw new \RuntimeException(
503
                "Error running bash commands:\n\nOutput:\n----------\n\n"
504
                .implode("\n", $output)
505
                ."\n\nCommands:\n----------\n"
506
                .str_replace(
507
                    "\n",
508
                    "\n\t",
509
                    "\n$bashCmds"
510
                )."\n\n"
511
            );
512
        }
513
514
        $seconds = round(microtime(true) - $startTime, 2);
515
        fwrite(STDERR, "\n\t\t#Completed in $seconds seconds\n");
516
    }
517
518
    protected function generateEntity(string $entityFqn): void
519
    {
520
        $namespace   = self::TEST_PROJECT_ROOT_NAMESPACE;
521
        $doctrineCmd = <<<DOCTRINE
522
 dsm:generate:entity \
523
    --project-root-namespace="{$namespace}" \
524
    --entity-fully-qualified-name="{$entityFqn}"
525
DOCTRINE;
526
        $this->execDoctrine($doctrineCmd);
527
    }
528
529
    protected function generateUuidEntity(string $entityFqn): void
530
    {
531
        $namespace   = self::TEST_PROJECT_ROOT_NAMESPACE;
532
        $doctrineCmd = <<<DOCTRINE
533
 dsm:generate:entity \
534
    --project-root-namespace="{$namespace}" \
535
    --entity-fully-qualified-name="{$entityFqn}" \
536
    --uuid-primary-key
537
DOCTRINE;
538
        $this->execDoctrine($doctrineCmd);
539
    }
540
541
    /**
542
     * @param string     $propertyName
543
     * @param string     $type
544
     * @param mixed|null $default
545
     * @param bool       $isUnique
546
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
547
     */
548
    protected function generateField(
549
        string $propertyName,
550
        string $type,
551
        $default = null,
552
        bool $isUnique = false
553
    ): void {
554
        $namespace   = self::TEST_PROJECT_ROOT_NAMESPACE;
555
        $doctrineCmd = <<<DOCTRINE
556
 dsm:generate:field \
557
    --project-root-path="{$this->workDir}" \
558
    --project-root-namespace="{$namespace}" \
559
    --field-fully-qualified-name="{$propertyName}" \
560
    --field-property-doctrine-type="{$type}"
561
DOCTRINE;
562
        if (null !== $default) {
563
            $doctrineCmd .= ' --'.GenerateFieldCommand::OPT_DEFAULT_VALUE.'="'.$default.'"'."\\\n";
564
        }
565
        if (true === $isUnique) {
566
            $doctrineCmd .= ' --'.GenerateFieldCommand::OPT_IS_UNIQUE."\\\n";
567
        }
568
        $this->execDoctrine($doctrineCmd);
569
    }
570
571
    protected function setField(string $entityFqn, string $fieldFqn): void
572
    {
573
        $namespace   = self::TEST_PROJECT_ROOT_NAMESPACE;
574
        $doctrineCmd = <<<DOCTRINE
575
 dsm:set:field \
576
    --project-root-path="{$this->workDir}" \
577
    --project-root-namespace="{$namespace}" \
578
    --entity="{$entityFqn}" \
579
    --field="{$fieldFqn}"
580
DOCTRINE;
581
        $this->execDoctrine($doctrineCmd);
582
    }
583
584
    protected function generateEmbeddable(string $archetypeFqn, string $newClassName): void
585
    {
586
        $doctrineCmd = <<<DOCTRINE
587
dsm:generate:embeddable \
588
    --classname="{$newClassName}" \
589
    --archetype="{$archetypeFqn}"
590
DOCTRINE;
591
        $this->execDoctrine($doctrineCmd);
592
    }
593
594
    protected function setEmbeddable(string $entityFqn, string $embeddableTraitFqn): void
595
    {
596
        $doctrineCmd = <<<DOCTRINE
597
 dsm:set:embeddable \
598
    --entity="{$entityFqn}" \
599
    --embeddable="{$embeddableTraitFqn}"
600
DOCTRINE;
601
        $this->execDoctrine($doctrineCmd);
602
    }
603
604
    protected function execDoctrine(string $doctrineCmd): void
605
    {
606
        $phpCmd  = $this->isTravis() ? 'php' : 'phpNoXdebug';
607
        $bash    = <<<BASH
608
$phpCmd bin/doctrine $doctrineCmd    
609
BASH;
610
        $error   = false;
611
        $message = '';
612
        try {
613
            $this->execBash($bash);
614
        } catch (\RuntimeException $e) {
615
            $this->addToRebuildFile("\n\nexit 0;\n\n#The command below failed...\n\n");
616
            $error   = true;
617
            $message = $e->getMessage();
618
        }
619
        $this->addToRebuildFile($bash);
620
        self::assertFalse($error, $message);
621
    }
622
623
    protected function setRelation(string $entity1, string $type, string $entity2): void
624
    {
625
        $namespace = self::TEST_PROJECT_ROOT_NAMESPACE;
626
        $this->execDoctrine(
627
            <<<DOCTRINE
628
dsm:set:relation \
629
    --project-root-path="{$this->workDir}" \
630
    --project-root-namespace="{$namespace}" \
631
    --entity1="{$entity1}" \
632
    --hasType="{$type}" \
633
    --entity2="{$entity2}"    
634
DOCTRINE
635
        );
636
    }
637
638
    /**
639
     * @SuppressWarnings(PHPMD.Superglobals)
640
     * @throws \Exception
641
     */
642
    public function testRunTests(): void
643
    {
644
        $this->assertWeCheckAllPossibleRelationTypes();
645
        if (isset($_SERVER[Constants::QA_QUICK_TESTS_KEY])
646
            && (int)$_SERVER[Constants::QA_QUICK_TESTS_KEY] === Constants::QA_QUICK_TESTS_ENABLED
647
        ) {
648
            $this->markTestSkipped('Quick tests is enabled');
649
        }
650
        /** @lang bash */
651
        $bashCmds = <<<BASH
652
653
set +x
654
655
echo "
656
657
--------------------------------------------------
658
STARTS Running Tests In {$this->workDir}
659
--------------------------------------------------
660
661
"
662
663
#Prevent the retry tool dialogue etc
664
export CI=true
665
666
bash -x bin/qa
667
668
echo "
669
670
--------------------------------------------------
671
DONE Running Tests In {$this->workDir}
672
--------------------------------------------------
673
674
"
675
set -x
676
BASH;
677
        $this->execBash($bashCmds);
678
    }
679
680
    /**
681
     * Generate all test entities
682
     *
683
     * @return array
684
     */
685
    protected function generateEntities(): array
686
    {
687
        foreach (self::TEST_ENTITIES as $entityFqn) {
688
            $this->generateEntity($entityFqn);
689
        }
690
691
        return self::TEST_ENTITIES;
692
    }
693
694
    /**
695
     * @return string
696
     */
697
    protected function generateStandardFieldEntity(): string
698
    {
699
        $entityFqn = self::TEST_ENTITY_NAMESPACE_BASE.'\\Standard\\Field';
700
        $this->generateUuidEntity($entityFqn);
701
702
        return $entityFqn;
703
    }
704
705
    /**
706
     * Generate all test relations
707
     *
708
     * @return void
709
     */
710
    protected function generateRelations(): void
711
    {
712
        foreach (self::TEST_RELATIONS as $relation) {
713
            $this->setRelation(...$relation);
0 ignored issues
show
Bug introduced by
The call to EdmondsCommerce\Doctrine...onalTest::setRelation() has too few arguments starting with type. ( Ignorable by Annotation )

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

713
            $this->/** @scrutinizer ignore-call */ 
714
                   setRelation(...$relation);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
714
        }
715
    }
716
717
    /**
718
     * Generate one field per common type
719
     *
720
     * @return void
721
     */
722
    protected function generateFields(): void
723
    {
724
        foreach (MappingHelper::COMMON_TYPES as $type) {
725
            $fieldFqn = self::TEST_FIELD_TRAIT_NAMESPACE.'\\'.$type;
726
            $this->generateField($fieldFqn, $type);
727
        }
728
        foreach (self::UNIQUEABLE_FIELD_TYPES as $uniqueableType) {
729
            $fieldFqn = self::TEST_FIELD_TRAIT_NAMESPACE.'\\Unique'.ucwords($uniqueableType);
730
            $this->generateField($fieldFqn, $uniqueableType, null, true);
731
        }
732
    }
733
734
    /**
735
     * Set each field type on each entity type
736
     *
737
     * @param array $entities
738
     * @param array $fields
739
     *
740
     * @return void
741
     */
742
    protected function setFields(array $entities, array $fields): void
743
    {
744
        foreach ($entities as $entityFqn) {
745
            foreach ($fields as $fieldFqn) {
746
                $this->setField($entityFqn, $fieldFqn);
747
            }
748
        }
749
    }
750
751
    /**
752
     * @return array
753
     * @SuppressWarnings(PHPMD.StaticAccess)
754
     */
755
    protected function getFieldFqns(): array
756
    {
757
        $fieldFqns = [];
758
        foreach (MappingHelper::COMMON_TYPES as $type) {
759
            $fieldFqns[] = self::TEST_FIELD_TRAIT_NAMESPACE.Inflector::classify($type).'FieldTrait';
760
        }
761
        foreach (self::UNIQUEABLE_FIELD_TYPES as $type) {
762
            $fieldFqns[] = self::TEST_FIELD_TRAIT_NAMESPACE.Inflector::classify('unique_'.$type).'FieldTrait';
763
        }
764
765
        return $fieldFqns;
766
    }
767
}
768