Skip to content

Commit 5d0e1af

Browse files
authored
Merge pull request #169 from Jibbarth/feature/new-composer-insights
Add ComposerMustBeValid and ComposerLockMustBeFresh insights
2 parents afe7b32 + e04b204 commit 5d0e1af

19 files changed

+686
-10
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
.idea/*
44
.env
55
.phpunit.result.cache
6-
composer.lock
6+
/composer.lock
77
.php_cs.cache

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
],
1212
"require": {
1313
"php": "^7.2",
14-
"ext-json": "*",
1514
"ext-iconv": "*",
15+
"ext-json": "*",
16+
"composer/composer": "^1.7",
1617
"league/container": "^3.2",
1718
"object-calisthenics/phpcs-calisthenics-rules": "^3.5",
1819
"phploc/phploc": "^5.0",

src/Domain/Insights/ComposerFinder.php renamed to src/Domain/ComposerFinder.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22

33
declare(strict_types=1);
44

5-
namespace NunoMaduro\PhpInsights\Domain\Insights;
5+
namespace NunoMaduro\PhpInsights\Domain;
66

7-
use NunoMaduro\PhpInsights\Domain\Collector;
87
use NunoMaduro\PhpInsights\Domain\Exceptions\ComposerNotFound;
98

109
final class ComposerFinder
@@ -15,11 +14,16 @@ final class ComposerFinder
1514
* @return string
1615
*/
1716
public static function contents(Collector $collector): string
17+
{
18+
return (string) file_get_contents(self::getPath($collector));
19+
}
20+
21+
public static function getPath(Collector $collector): string
1822
{
1923
$filePath = $collector->getDir() . '/composer.json';
2024

2125
if (file_exists($filePath)) {
22-
return (string) file_get_contents($filePath);
26+
return $filePath;
2327
}
2428

2529
throw new ComposerNotFound('`composer.json` not found.');

src/Domain/ComposerLoader.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace NunoMaduro\PhpInsights\Domain;
6+
7+
use Composer\Composer;
8+
use Composer\Factory;
9+
use Composer\IO\NullIO;
10+
11+
/**
12+
* @internal
13+
*/
14+
final class ComposerLoader
15+
{
16+
public static function getInstance(Collector $collector): Composer
17+
{
18+
$io = new NullIO();
19+
return Factory::create($io, ComposerFinder::getPath($collector));
20+
}
21+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace NunoMaduro\PhpInsights\Domain\Insights\Composer;
6+
7+
use NunoMaduro\PhpInsights\Domain\ComposerLoader;
8+
use NunoMaduro\PhpInsights\Domain\Contracts\HasDetails;
9+
use NunoMaduro\PhpInsights\Domain\Exceptions\ComposerNotFound;
10+
use NunoMaduro\PhpInsights\Domain\Insights\Insight;
11+
12+
final class ComposerLockMustBeFresh extends Insight implements HasDetails
13+
{
14+
public function hasIssue(): bool
15+
{
16+
try {
17+
$composer = ComposerLoader::getInstance($this->collector);
18+
19+
return ! $composer->getLocker()->isFresh();
20+
} catch (ComposerNotFound $exception) {
21+
return true;
22+
}
23+
}
24+
25+
public function getTitle(): string
26+
{
27+
return 'The lock file is not up to date with the latest changes in composer.json';
28+
}
29+
30+
public function getDetails(): array
31+
{
32+
return ['You may be getting outdated dependencies. Run update to update them.'];
33+
}
34+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace NunoMaduro\PhpInsights\Domain\Insights\Composer;
6+
7+
use Composer\IO\NullIO;
8+
use Composer\Util\ConfigValidator;
9+
use NunoMaduro\PhpInsights\Domain\ComposerFinder;
10+
use NunoMaduro\PhpInsights\Domain\Contracts\HasDetails;
11+
use NunoMaduro\PhpInsights\Domain\Insights\Insight;
12+
13+
final class ComposerMustBeValid extends Insight implements HasDetails
14+
{
15+
/**
16+
* @var array<string>
17+
*/
18+
private $errors;
19+
/**
20+
* @var array<string>
21+
*/
22+
private $publishErrors;
23+
/**
24+
* @var array<string>
25+
*/
26+
private $warnings;
27+
28+
public function hasIssue(): bool
29+
{
30+
$validator = new ConfigValidator(new NullIO());
31+
[$this->errors, $this->publishErrors, $this->warnings] = $validator->validate(ComposerFinder::getPath($this->collector));
32+
33+
return \count(\array_merge($this->errors, $this->publishErrors, $this->warnings)) > 0;
34+
}
35+
36+
public function getTitle(): string
37+
{
38+
return 'Composer.json is not valid';
39+
}
40+
41+
public function getDetails(): array
42+
{
43+
$details = [];
44+
45+
foreach (array_merge($this->errors, $this->publishErrors, $this->warnings) as $issue) {
46+
if (strpos($issue, ' : ') !== false) {
47+
$issue = explode(' : ', $issue)[1];
48+
}
49+
$details[] = 'composer.json: ' . $issue;
50+
}
51+
52+
return $details;
53+
}
54+
}

src/Domain/Insights/ComposerMustContainName.php renamed to src/Domain/Insights/Composer/ComposerMustContainName.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
declare(strict_types=1);
44

5-
namespace NunoMaduro\PhpInsights\Domain\Insights;
5+
namespace NunoMaduro\PhpInsights\Domain\Insights\Composer;
66

7+
use NunoMaduro\PhpInsights\Domain\ComposerFinder;
78
use NunoMaduro\PhpInsights\Domain\Exceptions\ComposerNotFound;
9+
use NunoMaduro\PhpInsights\Domain\Insights\Insight;
810

911
final class ComposerMustContainName extends Insight
1012
{

src/Domain/Insights/ComposerMustExist.php renamed to src/Domain/Insights/Composer/ComposerMustExist.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
declare(strict_types=1);
44

5-
namespace NunoMaduro\PhpInsights\Domain\Insights;
5+
namespace NunoMaduro\PhpInsights\Domain\Insights\Composer;
66

7+
use NunoMaduro\PhpInsights\Domain\ComposerFinder;
78
use NunoMaduro\PhpInsights\Domain\Exceptions\ComposerNotFound;
9+
use NunoMaduro\PhpInsights\Domain\Insights\Insight;
810

911
final class ComposerMustExist extends Insight
1012
{

src/Domain/Insights/Laravel/ComposerCheckLaravelVersion.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
namespace NunoMaduro\PhpInsights\Domain\Insights\Laravel;
66

7+
use NunoMaduro\PhpInsights\Domain\ComposerFinder;
78
use NunoMaduro\PhpInsights\Domain\Exceptions\ComposerNotFound;
8-
use NunoMaduro\PhpInsights\Domain\Insights\ComposerFinder;
99
use NunoMaduro\PhpInsights\Domain\Insights\Insight;
1010

1111
final class ComposerCheckLaravelVersion extends Insight

src/Domain/Metrics/Architecture/Composer.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
namespace NunoMaduro\PhpInsights\Domain\Metrics\Architecture;
66

77
use NunoMaduro\PhpInsights\Domain\Contracts\HasInsights;
8-
use NunoMaduro\PhpInsights\Domain\Insights\ComposerMustContainName;
9-
use NunoMaduro\PhpInsights\Domain\Insights\ComposerMustExist;
8+
use NunoMaduro\PhpInsights\Domain\Insights\Composer\ComposerLockMustBeFresh;
9+
use NunoMaduro\PhpInsights\Domain\Insights\Composer\ComposerMustBeValid;
10+
use NunoMaduro\PhpInsights\Domain\Insights\Composer\ComposerMustContainName;
11+
use NunoMaduro\PhpInsights\Domain\Insights\Composer\ComposerMustExist;
1012

1113
final class Composer implements HasInsights
1214
{
@@ -18,6 +20,8 @@ public function getInsights(): array
1820
return [
1921
ComposerMustExist::class,
2022
ComposerMustContainName::class,
23+
ComposerMustBeValid::class,
24+
ComposerLockMustBeFresh::class,
2125
];
2226
}
2327
}

tests/Domain/ComposerFinderTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Domain;
6+
7+
use NunoMaduro\PhpInsights\Domain\Collector;
8+
use NunoMaduro\PhpInsights\Domain\ComposerFinder;
9+
use NunoMaduro\PhpInsights\Domain\Exceptions\ComposerNotFound;
10+
use PHPUnit\Framework\TestCase;
11+
12+
final class ComposerFinderTest extends TestCase
13+
{
14+
public function testGetComposerPath(): void
15+
{
16+
$collector = new Collector(__DIR__ . '/Insights/Composer/Fixtures/Valid');
17+
$path = ComposerFinder::getPath($collector);
18+
19+
self::assertEquals(
20+
__DIR__ . '/Insights/Composer/Fixtures/Valid/composer.json',
21+
$path
22+
);
23+
}
24+
25+
public function testGetComposerPathThrowExceptionIfNoComposerJsonExist(): void
26+
{
27+
self::expectException(ComposerNotFound::class);
28+
29+
$collector = new Collector(__DIR__ . '/Insights/Composer/Fixtures');
30+
ComposerFinder::getPath($collector);
31+
}
32+
}

tests/Domain/ComposerLoaderTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Domain;
6+
7+
use Composer\Composer;
8+
use NunoMaduro\PhpInsights\Domain\Collector;
9+
use NunoMaduro\PhpInsights\Domain\ComposerLoader;
10+
use NunoMaduro\PhpInsights\Domain\Exceptions\ComposerNotFound;
11+
use PHPUnit\Framework\TestCase;
12+
13+
final class ComposerLoaderTest extends TestCase
14+
{
15+
public function testGetInstance(): void
16+
{
17+
$collector = new Collector(__DIR__ . '/Insights/Composer/Fixtures/Valid');
18+
$composer = ComposerLoader::getInstance($collector);
19+
20+
self::assertEquals(Composer::class, get_class($composer));
21+
}
22+
23+
public function testGetExceptionOnFolderWithoutComposerJson(): void
24+
{
25+
self::expectException(ComposerNotFound::class);
26+
27+
$collector = new Collector(__DIR__ . '/Insights/Composer/Fixtures');
28+
ComposerLoader::getInstance($collector);
29+
}
30+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Domain\Insights\Composer;
6+
7+
use NunoMaduro\PhpInsights\Domain\Collector;
8+
use NunoMaduro\PhpInsights\Domain\Insights\Composer\ComposerLockMustBeFresh;
9+
use PHPUnit\Framework\TestCase;
10+
11+
final class ComposerLockMustBeFreshTest extends TestCase
12+
{
13+
public function testHasIssueWhenComposerLockWasntUpdateFromComposerJson(): void
14+
{
15+
$collector = new Collector(__DIR__ . '/Fixtures/Unfresh');
16+
$insight = new ComposerLockMustBeFresh($collector, []);
17+
18+
self::assertTrue($insight->hasIssue());
19+
}
20+
21+
public function testHasNoIssueWhenComposerLockUpToDate(): void
22+
{
23+
$collector = new Collector(__DIR__ . '/Fixtures/Fresh');
24+
$insight = new ComposerLockMustBeFresh($collector, []);
25+
26+
self::assertFalse($insight->hasIssue());
27+
}
28+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Domain\Insights\Composer;
6+
7+
use NunoMaduro\PhpInsights\Domain\Collector;
8+
use NunoMaduro\PhpInsights\Domain\Insights\Composer\ComposerMustBeValid;
9+
use PHPUnit\Framework\TestCase;
10+
11+
final class ComposerMustBeValidTest extends TestCase
12+
{
13+
public function testComposerIsNotValid(): void
14+
{
15+
$collector = new Collector(__DIR__ . '/Fixtures/Fresh');
16+
$insight = new ComposerMustBeValid($collector, []);
17+
18+
self::assertTrue($insight->hasIssue());
19+
self::assertIsArray($insight->getDetails());
20+
self::assertContains('composer.json: The property name is required', $insight->getDetails());
21+
self::assertContains('composer.json: The property description is required', $insight->getDetails());
22+
self::assertContains('composer.json: No license specified, it is recommended to do so. For closed-source software you may use "proprietary" as license.', $insight->getDetails());
23+
}
24+
25+
public function testComposerIsValid(): void
26+
{
27+
$collector = new Collector(__DIR__ . '/Fixtures/Valid');
28+
$insight = new ComposerMustBeValid($collector, []);
29+
30+
self::assertFalse($insight->hasIssue());
31+
}
32+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"require": {
3+
"symfony/var-dumper": "^4.3"
4+
}
5+
}

0 commit comments

Comments
 (0)