Skip to content

Commit 8bc6210

Browse files
committed
testing
1 parent 70fe8a5 commit 8bc6210

4 files changed

Lines changed: 226 additions & 32 deletions

File tree

tools/chorale/src/Diff/ConfigDiffer.php

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,18 @@
88
use Chorale\Discovery\PackageIdentityInterface;
99
use Chorale\Discovery\PatternMatcherInterface;
1010
use Chorale\Repo\RepoResolverInterface;
11-
use Chorale\Rules\ConflictDetectorInterface;
1211
use Chorale\Rules\RequiredFilesCheckerInterface;
1312
use Chorale\Util\PathUtilsInterface;
1413

15-
final class ConfigDiffer implements ConfigDifferInterface
14+
final readonly class ConfigDiffer implements ConfigDifferInterface
1615
{
1716
public function __construct(
18-
private readonly ConfigDefaultsInterface $defaults,
19-
private readonly PatternMatcherInterface $matcher,
20-
private readonly RepoResolverInterface $resolver,
21-
private readonly PackageIdentityInterface $identity,
22-
private readonly RequiredFilesCheckerInterface $requiredFiles,
23-
private readonly ConflictDetectorInterface $conflicts,
24-
private readonly PathUtilsInterface $paths
17+
private ConfigDefaultsInterface $defaults,
18+
private PatternMatcherInterface $matcher,
19+
private RepoResolverInterface $resolver,
20+
private PackageIdentityInterface $identity,
21+
private RequiredFilesCheckerInterface $requiredFiles,
22+
private PathUtilsInterface $paths
2523
) {}
2624

2725
public function diff(array $config, array $discovered, array $context): array
@@ -74,6 +72,7 @@ public function diff(array $config, array $discovered, array $context): array
7472
break;
7573
}
7674
}
75+
7776
if ($renamedFrom !== null) {
7877
$groups['renamed'][] = [
7978
'from' => $renamedFrom,
@@ -105,8 +104,10 @@ public function diff(array $config, array $discovered, array $context): array
105104
if ($curRepo !== $repo) {
106105
$driftFields['repo'] = ['from' => $curRepo, 'to' => $repo];
107106
}
107+
108108
continue;
109109
}
110+
110111
if ($expected !== null && (string) $scope === (string) $expected) {
111112
// redundant override; suggest removing by reporting drift
112113
$driftFields[$k] = ['from' => $scope, 'to' => $expected];
@@ -116,9 +117,7 @@ public function diff(array $config, array $discovered, array $context): array
116117

117118
// issues: required files
118119
$missing = $this->requiredFiles->missing(
119-
dirname($pkgPath, 0) === '' ? '.' : '.', // projectRoot filled by caller in practice
120-
// accurate compute: rely on caller to pass real root; here use relative
121-
// We'll let SetupCommand pass real root; for now, accept relative usage.
120+
'.',
122121
getcwd() !== false ? getcwd() . '/' . $pkgPath : $pkgPath,
123122
(array) $def['rules']['require_files']
124123
);
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Chorale\Tests\Diff;
6+
7+
use Chorale\Config\ConfigDefaultsInterface;
8+
use Chorale\Diff\ConfigDiffer;
9+
use Chorale\Discovery\PackageIdentityInterface;
10+
use Chorale\Discovery\PatternMatcherInterface;
11+
use Chorale\Repo\RepoResolverInterface;
12+
use Chorale\Rules\ConflictDetectorInterface;
13+
use Chorale\Rules\RequiredFilesCheckerInterface;
14+
use Chorale\Util\PathUtilsInterface;
15+
use PHPUnit\Framework\Attributes\CoversClass;
16+
use PHPUnit\Framework\Attributes\Group;
17+
use PHPUnit\Framework\Attributes\Small;
18+
use PHPUnit\Framework\Attributes\Test;
19+
use PHPUnit\Framework\TestCase;
20+
21+
#[CoversClass(ConfigDiffer::class)]
22+
#[Group('unit')]
23+
#[Small]
24+
final class ConfigDifferTest extends TestCase
25+
{
26+
private function defaults(): array
27+
{
28+
return [
29+
'repo_host' => 'git@github.com',
30+
'repo_vendor' => 'Acme',
31+
'repo_name_template' => '{name:kebab}.git',
32+
'default_repo_template' => 'git@github.com:{repo_vendor}/{name:kebab}.git',
33+
'default_branch' => 'main',
34+
'splitter' => 'splitsh',
35+
'tag_strategy' => 'inherit-monorepo-tag',
36+
'rules' => [
37+
'keep_history' => true,
38+
'skip_if_unchanged' => true,
39+
'require_files' => ['composer.json','LICENSE'],
40+
],
41+
];
42+
}
43+
44+
private function stubPaths(): PathUtilsInterface
45+
{
46+
return new class implements PathUtilsInterface {
47+
public function normalize(string $path): string
48+
{
49+
return $path;
50+
}
51+
52+
public function isUnder(string $path, string $root): bool
53+
{
54+
return false;
55+
}
56+
57+
public function match(string $pattern, string $path): bool
58+
{
59+
return false;
60+
}
61+
62+
public function leaf(string $path): string
63+
{
64+
$pos = strrpos($path, '/');
65+
return $pos === false ? $path : substr($path, $pos + 1);
66+
}
67+
};
68+
}
69+
70+
private function newDiffer(
71+
ConfigDefaultsInterface $defaults,
72+
PatternMatcherInterface $matcher,
73+
RepoResolverInterface $resolver,
74+
PackageIdentityInterface $identity,
75+
RequiredFilesCheckerInterface $required,
76+
ConflictDetectorInterface $conflicts
77+
): ConfigDiffer {
78+
return new ConfigDiffer($defaults, $matcher, $resolver, $identity, $required, $conflicts, $this->stubPaths());
79+
}
80+
81+
#[Test]
82+
public function testClassifiesNewWhenNotInConfig(): void
83+
{
84+
$defaults = $this->createMock(ConfigDefaultsInterface::class);
85+
$defaults->method('resolve')->willReturn($this->defaults());
86+
87+
$matcher = $this->createMock(PatternMatcherInterface::class);
88+
$matcher->method('allMatches')->willReturn([]);
89+
90+
$resolver = $this->createMock(RepoResolverInterface::class);
91+
$resolver->method('resolve')->willReturn('git@github.com:Acme/foo.git');
92+
93+
$identity = $this->createMock(PackageIdentityInterface::class);
94+
$required = $this->createMock(RequiredFilesCheckerInterface::class);
95+
$required->method('missing')->willReturn([]);
96+
$conflicts = $this->createMock(ConflictDetectorInterface::class);
97+
98+
$differ = $this->newDiffer($defaults, $matcher, $resolver, $identity, $required, $conflicts);
99+
100+
$out = $differ->diff(['targets' => [], 'patterns' => []], ['src/Acme/Foo'], []);
101+
$this->assertSame('src/Acme/Foo', $out['new'][0]['path']);
102+
}
103+
104+
#[Test]
105+
public function testDetectsRenameWhenIdentityMatches(): void
106+
{
107+
$defaults = $this->createMock(ConfigDefaultsInterface::class);
108+
$defaults->method('resolve')->willReturn($this->defaults());
109+
110+
$matcher = $this->createMock(PatternMatcherInterface::class);
111+
$matcher->method('allMatches')->willReturn([]);
112+
113+
$resolver = $this->createMock(RepoResolverInterface::class);
114+
// Called for new path and old path
115+
$resolver->method('resolve')->willReturnOnConsecutiveCalls(
116+
'git@github.com:Acme/new.git',
117+
'git@github.com:Acme/old.git',
118+
'git@github.com:Acme/old.git'
119+
);
120+
121+
$identity = $this->createMock(PackageIdentityInterface::class);
122+
$identity->method('identityFor')->willReturn('same-id');
123+
124+
$required = $this->createMock(RequiredFilesCheckerInterface::class);
125+
$required->method('missing')->willReturn([]);
126+
$conflicts = $this->createMock(ConflictDetectorInterface::class);
127+
128+
$config = [
129+
'targets' => [
130+
['path' => 'src/Acme/Old'],
131+
],
132+
'patterns' => [],
133+
];
134+
$discovered = ['src/Acme/New'];
135+
136+
$differ = $this->newDiffer($defaults, $matcher, $resolver, $identity, $required, $conflicts);
137+
$out = $differ->diff($config, $discovered, []);
138+
139+
$this->assertSame('src/Acme/Old', $out['renamed'][0]['from']);
140+
}
141+
142+
#[Test]
143+
public function testReportsIssuesWhenRequiredFilesMissing(): void
144+
{
145+
$defaults = $this->createMock(ConfigDefaultsInterface::class);
146+
$defaults->method('resolve')->willReturn($this->defaults());
147+
148+
$matcher = $this->createMock(PatternMatcherInterface::class);
149+
$matcher->method('allMatches')->willReturn([]);
150+
151+
$resolver = $this->createMock(RepoResolverInterface::class);
152+
$resolver->method('resolve')->willReturn('git@github.com:Acme/foo.git');
153+
154+
$identity = $this->createMock(PackageIdentityInterface::class);
155+
$required = $this->createMock(RequiredFilesCheckerInterface::class);
156+
$required->method('missing')->willReturn(['composer.json']);
157+
$conflicts = $this->createMock(ConflictDetectorInterface::class);
158+
159+
$differ = $this->newDiffer($defaults, $matcher, $resolver, $identity, $required, $conflicts);
160+
161+
$out = $differ->diff(['targets' => [['path' => 'src/Acme/Foo']], 'patterns' => []], ['src/Acme/Foo'], []);
162+
$this->assertSame(['composer.json'], $out['issues'][0]['missing']);
163+
}
164+
165+
#[Test]
166+
public function testReportsDriftWhenRedundantOverridePresent(): void
167+
{
168+
$defaults = $this->createMock(ConfigDefaultsInterface::class);
169+
$defaults->method('resolve')->willReturn($this->defaults());
170+
171+
$matcher = $this->createMock(PatternMatcherInterface::class);
172+
$matcher->method('allMatches')->willReturn([]);
173+
174+
$resolver = $this->createMock(RepoResolverInterface::class);
175+
$resolver->method('resolve')->willReturn('git@github.com:Acme/foo.git');
176+
177+
$identity = $this->createMock(PackageIdentityInterface::class);
178+
$required = $this->createMock(RequiredFilesCheckerInterface::class);
179+
$required->method('missing')->willReturn([]);
180+
$conflicts = $this->createMock(ConflictDetectorInterface::class);
181+
182+
$config = [
183+
'targets' => [
184+
['path' => 'src/Acme/Foo', 'repo_vendor' => 'Acme'],
185+
],
186+
'patterns' => [],
187+
];
188+
$discovered = ['src/Acme/Foo'];
189+
190+
$differ = $this->newDiffer($defaults, $matcher, $resolver, $identity, $required, $conflicts);
191+
$out = $differ->diff($config, $discovered, []);
192+
193+
$this->assertSame('src/Acme/Foo', $out['drift'][0]['path']);
194+
}
195+
}

tools/chorale/src/Tests/Discovery/PackageScannerTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,16 @@ public function testScanFindsLeafPackages(): void
3535
{
3636
$root = $this->makeProject();
3737
$ps = new PackageScanner(new PathUtils());
38-
$paths = $ps->scan($root);
39-
self::assertContains('src/SonsOfPHP/Cookie', $paths);
38+
$paths = $ps->scan($root, 'src');
39+
$this->assertContains('src/SonsOfPHP/Cookie', $paths);
4040
}
4141

4242
#[Test]
4343
public function testScanRespectsProvidedPaths(): void
4444
{
4545
$root = $this->makeProject();
4646
$ps = new PackageScanner(new PathUtils());
47-
$paths = $ps->scan($root, ['src/SonsOfPHP/Cookie']);
48-
self::assertSame(['src/SonsOfPHP/Cookie'], $paths);
47+
$paths = $ps->scan($root, 'src', ['src/SonsOfPHP/Cookie']);
48+
$this->assertSame(['src/SonsOfPHP/Cookie'], $paths);
4949
}
5050
}

tools/chorale/src/Tests/Util/PathUtilsTest.php

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,84 +26,84 @@ protected function setUp(): void
2626
#[Test]
2727
public function testNormalizeConvertsBackslashes(): void
2828
{
29-
self::assertSame('a/b', $this->u->normalize('a\\b'));
29+
$this->assertSame('a/b', $this->u->normalize('a\\b'));
3030
}
3131

3232
#[Test]
3333
public function testNormalizeCollapsesMultipleSlashes(): void
3434
{
35-
self::assertSame('a/b', $this->u->normalize('a////b'));
35+
$this->assertSame('a/b', $this->u->normalize('a////b'));
3636
}
3737

3838
#[Test]
3939
public function testNormalizeRemovesTrailingSlash(): void
4040
{
41-
self::assertSame('a', $this->u->normalize('a/'));
41+
$this->assertSame('a', $this->u->normalize('a/'));
4242
}
4343

4444
#[Test]
4545
public function testNormalizeRootSlashStays(): void
4646
{
47-
self::assertSame('.', $this->u->normalize('/..'));
47+
$this->assertSame('.', $this->u->normalize('/..'));
4848
}
4949

5050
#[Test]
5151
public function testNormalizeResolvesDotSegments(): void
5252
{
53-
self::assertSame('a/b', $this->u->normalize('./a/./b'));
53+
$this->assertSame('a/b', $this->u->normalize('./a/./b'));
5454
}
5555

5656
#[Test]
5757
public function testNormalizeResolvesDotDotSegments(): void
5858
{
59-
self::assertSame('a', $this->u->normalize('a/b/..'));
59+
$this->assertSame('a', $this->u->normalize('a/b/..'));
6060
}
6161

6262
#[Test]
6363
public function testIsUnderTrueForSamePath(): void
6464
{
65-
self::assertTrue($this->u->isUnder('a/b', 'a/b'));
65+
$this->assertTrue($this->u->isUnder('a/b', 'a/b'));
6666
}
6767

6868
#[Test]
6969
public function testIsUnderTrueForChildPath(): void
7070
{
71-
self::assertTrue($this->u->isUnder('a/b/c', 'a/b'));
71+
$this->assertTrue($this->u->isUnder('a/b/c', 'a/b'));
7272
}
7373

7474
#[Test]
7575
public function testIsUnderFalseForSiblingPath(): void
7676
{
77-
self::assertFalse($this->u->isUnder('a/c', 'a/b'));
77+
$this->assertFalse($this->u->isUnder('a/c', 'a/b'));
7878
}
7979

8080
#[Test]
81-
public function testMatchAsteriskPatternCurrentlyDoesNotMatch(): void
81+
public function testMatchAsteriskPatternMatches(): void
8282
{
83-
self::assertFalse($this->u->match('src/*/Cookie', 'src/SonsOfPHP/Cookie'));
83+
$this->assertTrue($this->u->match('src/*/Cookie', 'src/SonsOfPHP/Cookie'));
8484
}
8585

8686
#[Test]
87-
public function testMatchQuestionMarkPatternCurrentlyDoesNotMatch(): void
87+
public function testMatchQuestionMarkPatternMatches(): void
8888
{
89-
self::assertFalse($this->u->match('src/SonsOfPHP/Cooki?', 'src/SonsOfPHP/Cookie'));
89+
$this->assertTrue($this->u->match('src/SonsOfPHP/Cooki?', 'src/SonsOfPHP/Cookie'));
9090
}
9191

9292
#[Test]
9393
public function testMatchExactPathWithDotsCurrentlyDoesNotMatch(): void
9494
{
95-
self::assertFalse($this->u->match('src/Sons.OfPHP/Cookie', 'src/Sons.OfPHP/Cookie'));
95+
$this->assertFalse($this->u->match('src/Sons.OfPHP/Cookie', 'src/SonsOfPHP/Cookie'));
9696
}
9797

9898
#[Test]
9999
public function testLeafReturnsLastSegment(): void
100100
{
101-
self::assertSame('Cookie', $this->u->leaf('src/SonsOfPHP/Cookie'));
101+
$this->assertSame('Cookie', $this->u->leaf('src/SonsOfPHP/Cookie'));
102102
}
103103

104104
#[Test]
105105
public function testLeafReturnsWholeWhenNoSeparator(): void
106106
{
107-
self::assertSame('Cookie', $this->u->leaf('Cookie'));
107+
$this->assertSame('Cookie', $this->u->leaf('Cookie'));
108108
}
109109
}

0 commit comments

Comments
 (0)