Skip to content

Commit 6c5abc6

Browse files
committed
test: add tests for parsing and merging duplicate annotations
1 parent 6643311 commit 6c5abc6

File tree

1 file changed

+114
-0
lines changed

1 file changed

+114
-0
lines changed

tests/src/Rules/DocBlockHeaderFixerTest.php

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,54 @@ public function testParseExistingAnnotations(): void
196196
self::assertSame($expected, $result);
197197
}
198198

199+
public function testParseExistingAnnotationsWithDuplicates(): void
200+
{
201+
$method = new ReflectionMethod($this->fixer, 'parseExistingAnnotations');
202+
203+
$docBlock = "/**\n * @implements ArrayAccess<int, string>\n * @implements IteratorAggregate<int, string>\n */";
204+
$result = $method->invoke($this->fixer, $docBlock);
205+
206+
self::assertIsArray($result);
207+
self::assertArrayHasKey('implements', $result);
208+
self::assertIsArray($result['implements']);
209+
self::assertCount(2, $result['implements']);
210+
self::assertSame('ArrayAccess<int, string>', $result['implements'][0]);
211+
self::assertSame('IteratorAggregate<int, string>', $result['implements'][1]);
212+
}
213+
214+
public function testParseExistingAnnotationsWithMultipleDuplicateTags(): void
215+
{
216+
$method = new ReflectionMethod($this->fixer, 'parseExistingAnnotations');
217+
218+
$docBlock = "/**\n * @author First Author\n * @license MIT\n * @author Second Author\n * @author Third Author\n */";
219+
$result = $method->invoke($this->fixer, $docBlock);
220+
221+
self::assertIsArray($result);
222+
self::assertArrayHasKey('author', $result);
223+
self::assertIsArray($result['author']);
224+
self::assertCount(3, $result['author']);
225+
self::assertSame('First Author', $result['author'][0]);
226+
self::assertSame('Second Author', $result['author'][1]);
227+
self::assertSame('Third Author', $result['author'][2]);
228+
self::assertSame('MIT', $result['license']);
229+
}
230+
231+
public function testParseExistingAnnotationsWithDuplicateEmptyValues(): void
232+
{
233+
$method = new ReflectionMethod($this->fixer, 'parseExistingAnnotations');
234+
235+
$docBlock = "/**\n * @internal\n * @api\n * @internal\n */";
236+
$result = $method->invoke($this->fixer, $docBlock);
237+
238+
self::assertIsArray($result);
239+
self::assertArrayHasKey('internal', $result);
240+
self::assertIsArray($result['internal']);
241+
self::assertCount(2, $result['internal']);
242+
self::assertSame('', $result['internal'][0]);
243+
self::assertSame('', $result['internal'][1]);
244+
self::assertSame('', $result['api']);
245+
}
246+
199247
public function testMergeAnnotations(): void
200248
{
201249
$method = new ReflectionMethod($this->fixer, 'mergeAnnotations');
@@ -994,4 +1042,70 @@ public function testSkipsAnonymousClassWithReadonlyModifier(): void
9941042
// Anonymous class with readonly modifier should NOT have DocBlock added
9951043
self::assertSame($code, $tokens->generateCode());
9961044
}
1045+
1046+
public function testFullRoundTripWithDuplicateImplementsAnnotations(): void
1047+
{
1048+
$parseMethod = new ReflectionMethod($this->fixer, 'parseExistingAnnotations');
1049+
$buildMethod = new ReflectionMethod($this->fixer, 'buildDocBlock');
1050+
1051+
// Original DocBlock with duplicate @implements
1052+
$originalDocBlock = "/**\n * @author Konrad Michalik\n * @license GPL-3.0\n * @implements ArrayAccess<int|null, IconImage>\n * @implements IteratorAggregate<int, IconImage>\n */";
1053+
1054+
// Parse existing annotations
1055+
$parsed = $parseMethod->invoke($this->fixer, $originalDocBlock);
1056+
1057+
// Verify parsing preserved both @implements
1058+
self::assertIsArray($parsed['implements']);
1059+
self::assertCount(2, $parsed['implements']);
1060+
1061+
// Build DocBlock from parsed annotations
1062+
$rebuilt = $buildMethod->invoke($this->fixer, $parsed, '');
1063+
1064+
// Verify both @implements are present in rebuilt DocBlock
1065+
self::assertStringContainsString('@author Konrad Michalik', $rebuilt);
1066+
self::assertStringContainsString('@license GPL-3.0', $rebuilt);
1067+
self::assertStringContainsString('@implements ArrayAccess<int|null, IconImage>', $rebuilt);
1068+
self::assertStringContainsString('@implements IteratorAggregate<int, IconImage>', $rebuilt);
1069+
}
1070+
1071+
public function testMergeWithExistingDocBlockPreservesDuplicateImplements(): void
1072+
{
1073+
$code = "<?php /**\n * @implements ArrayAccess<int, string>\n * @implements IteratorAggregate<int, string>\n */ class TestClass {}";
1074+
$tokens = Tokens::fromCode($code);
1075+
$annotations = ['author' => 'John Doe'];
1076+
1077+
$method = new ReflectionMethod($this->fixer, 'mergeWithExistingDocBlock');
1078+
1079+
$this->fixer->configure(['preserve_existing' => true]);
1080+
$method->invoke($this->fixer, $tokens, 1, $annotations, 'TestClass');
1081+
1082+
$result = $tokens->generateCode();
1083+
1084+
// Both @implements should be preserved
1085+
self::assertStringContainsString('@implements ArrayAccess<int, string>', $result);
1086+
self::assertStringContainsString('@implements IteratorAggregate<int, string>', $result);
1087+
self::assertStringContainsString('@author John Doe', $result);
1088+
}
1089+
1090+
public function testApplyFixPreservesDuplicateImplementsInExistingDocBlock(): void
1091+
{
1092+
$code = "<?php\n/**\n * @implements ArrayAccess<int, string>\n * @implements IteratorAggregate<int, string>\n */\nclass TestClass {}";
1093+
$tokens = Tokens::fromCode($code);
1094+
$file = new SplFileInfo(__FILE__);
1095+
1096+
$method = new ReflectionMethod($this->fixer, 'applyFix');
1097+
1098+
$this->fixer->configure([
1099+
'annotations' => ['author' => 'John Doe'],
1100+
'preserve_existing' => true,
1101+
]);
1102+
$method->invoke($this->fixer, $file, $tokens);
1103+
1104+
$result = $tokens->generateCode();
1105+
1106+
// Verify both @implements annotations are preserved after merge
1107+
self::assertStringContainsString('@implements ArrayAccess<int, string>', $result);
1108+
self::assertStringContainsString('@implements IteratorAggregate<int, string>', $result);
1109+
self::assertStringContainsString('@author John Doe', $result);
1110+
}
9971111
}

0 commit comments

Comments
 (0)