Skip to content

Commit a007f8e

Browse files
committed
test(coverage): achieve 100% class/method/line coverage — ARFA 1.3 Spec V4.0
Coverage: 47/47 classes · 118/118 methods · 450/450 lines (100%) - Create 22 individual test files (one CoversClass per rule) so pcov correctly attributes coverage to each class independently - Remove duplicate test methods from consolidated test files to avoid coverage-attribution conflicts - Add missing branch tests: ctype_digit, empty-string, non-string passthrough - Fix PluckRuleTest: assert [null,'Bob'] (rule uses $item[$field] ?? null) - Fix GroupByRuleTest: testEmptyFieldReturnsValue, testItemMissingField - Add devkit.php: exclude tests/ from cs-fixer to prevent @PHP84Migration from breaking pcov coverage attribution
1 parent 179bc15 commit a007f8e

30 files changed

Lines changed: 1005 additions & 84 deletions

devkit.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* KaririCode Devkit project-level overrides.
7+
*
8+
* Exclude test files from cs-fixer to prevent @PHP84Migration from transforming
9+
* `(new Foo())->method()` → `new Foo()->method()` — that PHP 8.4 syntax prevents
10+
* pcov from correctly attributing code-coverage to the tested class.
11+
*/
12+
return [
13+
'cs_fixer_finder_exclude_dirs' => ['tests'],
14+
];

tests/Unit/Rule/Brazilian/BrazilianRulesTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public function testGetName(): void
7373
{
7474
$this->assertIsString(new CpfToDigitsRule()->getName());
7575
$this->assertIsString(new CnpjToDigitsRule()->getName());
76-
$this->assertIsString(new \KaririCode\Transformer\Rule\Brazilian\CepToDigitsRule()->getName());
77-
$this->assertIsString(new \KaririCode\Transformer\Rule\Brazilian\PhoneFormatRule()->getName());
76+
$this->assertIsString((new \KaririCode\Transformer\Rule\Brazilian\CepToDigitsRule())->getName());
77+
$this->assertIsString((new \KaririCode\Transformer\Rule\Brazilian\PhoneFormatRule())->getName());
7878
}
7979
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Transformer\Tests\Unit\Rule\Brazilian;
6+
7+
use KaririCode\Transformer\Contract\TransformationContext;
8+
use KaririCode\Transformer\Core\TransformationContextImpl;
9+
use KaririCode\Transformer\Rule\Brazilian\CepToDigitsRule;
10+
use PHPUnit\Framework\Attributes\CoversClass;
11+
use PHPUnit\Framework\Attributes\Test;
12+
use PHPUnit\Framework\TestCase;
13+
14+
#[CoversClass(CepToDigitsRule::class)]
15+
final class CepToDigitsRuleTest extends TestCase
16+
{
17+
private function ctx(): TransformationContext
18+
{
19+
return TransformationContextImpl::create([])->withField('test');
20+
}
21+
22+
#[Test]
23+
public function testTransformCepToDigits(): void
24+
{
25+
$this->assertSame('63100000', new CepToDigitsRule()->transform('63100-000', $this->ctx()));
26+
$this->assertSame('63100000', new CepToDigitsRule()->transform('63100000', $this->ctx()));
27+
}
28+
29+
#[Test]
30+
public function testNonStringPassthrough(): void
31+
{
32+
$this->assertSame(42, new CepToDigitsRule()->transform(42, $this->ctx()));
33+
}
34+
35+
#[Test]
36+
public function testGetName(): void
37+
{
38+
$this->assertSame('brazilian.cep_to_digits', new CepToDigitsRule()->getName());
39+
}
40+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Transformer\Tests\Unit\Rule\Brazilian;
6+
7+
use KaririCode\Transformer\Contract\TransformationContext;
8+
use KaririCode\Transformer\Core\TransformationContextImpl;
9+
use KaririCode\Transformer\Rule\Brazilian\PhoneFormatRule;
10+
use PHPUnit\Framework\Attributes\CoversClass;
11+
use PHPUnit\Framework\Attributes\Test;
12+
use PHPUnit\Framework\TestCase;
13+
14+
#[CoversClass(PhoneFormatRule::class)]
15+
final class PhoneFormatRuleTest extends TestCase
16+
{
17+
private function ctx(): TransformationContext
18+
{
19+
return TransformationContextImpl::create([])->withField('test');
20+
}
21+
22+
#[Test]
23+
public function testTransformPhoneFormatMobile(): void
24+
{
25+
$this->assertSame('(85) 99999-1234', new PhoneFormatRule()->transform('85999991234', $this->ctx()));
26+
}
27+
28+
#[Test]
29+
public function testTransformPhoneFormatLandline(): void
30+
{
31+
$this->assertSame('(85) 3333-1234', new PhoneFormatRule()->transform('8533331234', $this->ctx()));
32+
}
33+
34+
#[Test]
35+
public function testTransformPhoneFormatInvalid(): void
36+
{
37+
$this->assertSame('123', new PhoneFormatRule()->transform('123', $this->ctx()));
38+
}
39+
40+
#[Test]
41+
public function testNonStringPassthrough(): void
42+
{
43+
$this->assertSame(42, new PhoneFormatRule()->transform(42, $this->ctx()));
44+
}
45+
46+
#[Test]
47+
public function testGetName(): void
48+
{
49+
$this->assertSame('brazilian.phone_format', new PhoneFormatRule()->getName());
50+
}
51+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Transformer\Tests\Unit\Rule\Data;
6+
7+
use KaririCode\Transformer\Contract\TransformationContext;
8+
use KaririCode\Transformer\Core\TransformationContextImpl;
9+
use KaririCode\Transformer\Rule\Data\ArrayToKeyValueRule;
10+
use PHPUnit\Framework\Attributes\CoversClass;
11+
use PHPUnit\Framework\Attributes\Test;
12+
use PHPUnit\Framework\TestCase;
13+
14+
#[CoversClass(ArrayToKeyValueRule::class)]
15+
final class ArrayToKeyValueRuleTest extends TestCase
16+
{
17+
private function ctx(array $params = []): TransformationContext
18+
{
19+
return TransformationContextImpl::create([])->withField('test')->withParameters($params);
20+
}
21+
22+
#[Test]
23+
public function testTransformArrayToKeyValue(): void
24+
{
25+
$data = [['id' => 1, 'name' => 'Alice'], ['id' => 2, 'name' => 'Bob']];
26+
$result = new ArrayToKeyValueRule()->transform($data, $this->ctx(['key' => 'id', 'value' => 'name']));
27+
$this->assertSame([1 => 'Alice', 2 => 'Bob'], $result);
28+
}
29+
30+
#[Test]
31+
public function testNonArrayPassthrough(): void
32+
{
33+
$this->assertSame('str', new ArrayToKeyValueRule()->transform('str', $this->ctx()));
34+
}
35+
36+
#[Test]
37+
public function testItemMissingKeySkipped(): void
38+
{
39+
// Item without 'id' key is skipped — key not int or string
40+
$data = [['name' => 'Alice']];
41+
$result = new ArrayToKeyValueRule()->transform($data, $this->ctx(['key' => 'id', 'value' => 'name']));
42+
$this->assertSame([], $result);
43+
}
44+
45+
#[Test]
46+
public function testGetName(): void
47+
{
48+
$this->assertSame('data.array_to_key_value', new ArrayToKeyValueRule()->getName());
49+
}
50+
}

tests/Unit/Rule/Data/DataRulesTest.php

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@
66

77
use KaririCode\Transformer\Contract\TransformationContext;
88
use KaririCode\Transformer\Core\TransformationContextImpl;
9-
use KaririCode\Transformer\Rule\Data\{ArrayToKeyValueRule, CsvToArrayRule, ImplodeRule, JsonDecodeRule, JsonEncodeRule};
9+
use KaririCode\Transformer\Rule\Data\{CsvToArrayRule, ImplodeRule, JsonEncodeRule};
1010
use PHPUnit\Framework\Attributes\CoversClass;
1111
use PHPUnit\Framework\Attributes\Test;
1212
use PHPUnit\Framework\TestCase;
1313

1414
#[CoversClass(JsonEncodeRule::class)]
15-
#[CoversClass(JsonDecodeRule::class)]
1615
#[CoversClass(CsvToArrayRule::class)]
17-
#[CoversClass(ArrayToKeyValueRule::class)]
1816
#[CoversClass(ImplodeRule::class)]
1917
final class DataRulesTest extends TestCase
2018
{
@@ -29,13 +27,6 @@ public function testJsonEncode(): void
2927
$this->assertSame('{"a":1}', new JsonEncodeRule()->transform(['a' => 1], $this->ctx()));
3028
}
3129

32-
#[Test]
33-
public function testJsonDecode(): void
34-
{
35-
$this->assertSame(['a' => 1], new JsonDecodeRule()->transform('{"a":1}', $this->ctx()));
36-
$this->assertSame('invalid', new JsonDecodeRule()->transform('invalid', $this->ctx()));
37-
}
38-
3930
#[Test]
4031
public function testCsvToArrayWithHeader(): void
4132
{
@@ -75,14 +66,6 @@ public function testCsvToArrayGetName(): void
7566
$this->assertSame('data.csv_to_array', new CsvToArrayRule()->getName());
7667
}
7768

78-
#[Test]
79-
public function testArrayToKeyValue(): void
80-
{
81-
$data = [['id' => 1, 'name' => 'Alice'], ['id' => 2, 'name' => 'Bob']];
82-
$result = new ArrayToKeyValueRule()->transform($data, $this->ctx(['key' => 'id', 'value' => 'name']));
83-
$this->assertSame([1 => 'Alice', 2 => 'Bob'], $result);
84-
}
85-
8669
#[Test]
8770
public function testImplode(): void
8871
{
@@ -94,10 +77,8 @@ public function testImplode(): void
9477
#[Test]
9578
public function testGetName(): void
9679
{
97-
$this->assertIsString(new \KaririCode\Transformer\Rule\Data\CsvToArrayRule()->getName());
98-
$this->assertIsString(new \KaririCode\Transformer\Rule\Data\JsonEncodeRule()->getName());
99-
$this->assertIsString(new \KaririCode\Transformer\Rule\Data\JsonDecodeRule()->getName());
100-
$this->assertIsString(new \KaririCode\Transformer\Rule\Data\ImplodeRule()->getName());
101-
$this->assertIsString(new \KaririCode\Transformer\Rule\Data\ArrayToKeyValueRule()->getName());
80+
$this->assertIsString((new \KaririCode\Transformer\Rule\Data\CsvToArrayRule())->getName());
81+
$this->assertIsString((new \KaririCode\Transformer\Rule\Data\JsonEncodeRule())->getName());
82+
$this->assertIsString((new \KaririCode\Transformer\Rule\Data\ImplodeRule())->getName());
10283
}
10384
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Transformer\Tests\Unit\Rule\Data;
6+
7+
use KaririCode\Transformer\Contract\TransformationContext;
8+
use KaririCode\Transformer\Core\TransformationContextImpl;
9+
use KaririCode\Transformer\Rule\Data\JsonDecodeRule;
10+
use PHPUnit\Framework\Attributes\CoversClass;
11+
use PHPUnit\Framework\Attributes\Test;
12+
use PHPUnit\Framework\TestCase;
13+
14+
#[CoversClass(JsonDecodeRule::class)]
15+
final class JsonDecodeRuleTest extends TestCase
16+
{
17+
private function ctx(): TransformationContext
18+
{
19+
return TransformationContextImpl::create([])->withField('test');
20+
}
21+
22+
#[Test]
23+
public function testTransformJsonDecode(): void
24+
{
25+
$rule = new JsonDecodeRule();
26+
$this->assertSame(['a' => 1], $rule->transform('{"a":1}', $this->ctx()));
27+
$this->assertSame('invalid', $rule->transform('invalid', $this->ctx()));
28+
}
29+
30+
#[Test]
31+
public function testNonStringPassthrough(): void
32+
{
33+
$this->assertSame(42, new JsonDecodeRule()->transform(42, $this->ctx()));
34+
}
35+
36+
#[Test]
37+
public function testGetName(): void
38+
{
39+
$this->assertSame('data.json_decode', new JsonDecodeRule()->getName());
40+
}
41+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Transformer\Tests\Unit\Rule\Date;
6+
7+
use KaririCode\Transformer\Contract\TransformationContext;
8+
use KaririCode\Transformer\Core\TransformationContextImpl;
9+
use KaririCode\Transformer\Rule\Date\AgeRule;
10+
use PHPUnit\Framework\Attributes\CoversClass;
11+
use PHPUnit\Framework\Attributes\Test;
12+
use PHPUnit\Framework\TestCase;
13+
14+
#[CoversClass(AgeRule::class)]
15+
final class AgeRuleTest extends TestCase
16+
{
17+
private function ctx(array $params = []): TransformationContext
18+
{
19+
return TransformationContextImpl::create([])->withField('test')->withParameters($params);
20+
}
21+
22+
#[Test]
23+
public function testTransformAge(): void
24+
{
25+
$rule = new AgeRule();
26+
$result = $rule->transform('2000-01-15', $this->ctx(['from' => 'Y-m-d']));
27+
$this->assertIsInt($result);
28+
$this->assertGreaterThanOrEqual(25, $result);
29+
}
30+
31+
#[Test]
32+
public function testInvalidDatePassthrough(): void
33+
{
34+
$this->assertSame('invalid', new AgeRule()->transform('invalid', $this->ctx()));
35+
}
36+
37+
#[Test]
38+
public function testEmptyStringPassthrough(): void
39+
{
40+
$this->assertSame('', new AgeRule()->transform('', $this->ctx()));
41+
}
42+
43+
#[Test]
44+
public function testGetName(): void
45+
{
46+
$this->assertSame('date.age', new AgeRule()->getName());
47+
}
48+
}

tests/Unit/Rule/Date/DateRulesTest.php

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,20 @@
66

77
use KaririCode\Transformer\Contract\TransformationContext;
88
use KaririCode\Transformer\Core\TransformationContextImpl;
9-
use KaririCode\Transformer\Rule\Date\{AgeRule, DateToIso8601Rule, DateToTimestampRule, RelativeDateRule};
9+
use KaririCode\Transformer\Rule\Date\{DateToIso8601Rule, RelativeDateRule};
1010
use PHPUnit\Framework\Attributes\CoversClass;
1111
use PHPUnit\Framework\Attributes\Test;
1212
use PHPUnit\Framework\TestCase;
1313

1414
#[CoversClass(DateToIso8601Rule::class)]
15-
#[CoversClass(DateToTimestampRule::class)]
1615
#[CoversClass(RelativeDateRule::class)]
17-
#[CoversClass(AgeRule::class)]
1816
final class DateRulesTest extends TestCase
1917
{
2018
private function ctx(array $params = []): TransformationContext
2119
{
2220
return TransformationContextImpl::create([])->withField('test')->withParameters($params);
2321
}
2422

25-
#[Test]
26-
public function testDateToTimestamp(): void
27-
{
28-
$result = new DateToTimestampRule()->transform('2025-02-28', $this->ctx(['format' => 'Y-m-d']));
29-
$this->assertIsInt($result);
30-
$date = new \DateTimeImmutable('@' . $result)->format('Y-m-d');
31-
$this->assertSame('2025-02-28', $date);
32-
}
33-
34-
#[Test]
35-
public function testDateToTimestampInvalid(): void
36-
{
37-
$this->assertSame('invalid', new DateToTimestampRule()->transform('invalid', $this->ctx()));
38-
}
39-
4023
#[Test]
4124
public function testDateToIso8601(): void
4225
{
@@ -172,32 +155,15 @@ public function testRelativeDateGetName(): void
172155
public function testRelativeDateUsesDefaultNow(): void
173156
{
174157
// No 'now' param provided — uses PHP's current time
175-
$recent = new \DateTimeImmutable()->modify('-2 minutes')->format('Y-m-d H:i:s');
158+
$recent = (new \DateTimeImmutable())->modify('-2 minutes')->format('Y-m-d H:i:s');
176159
$result = new RelativeDateRule()->transform($recent, $this->ctx());
177160
$this->assertStringContainsString('minute', $result);
178161
}
179162

180-
#[Test]
181-
public function testAge(): void
182-
{
183-
// Someone born 2000-01-15 should be 25 on 2025-02-28
184-
$result = new AgeRule()->transform('2000-01-15', $this->ctx(['from' => 'Y-m-d']));
185-
$this->assertIsInt($result);
186-
$this->assertGreaterThanOrEqual(25, $result);
187-
}
188-
189-
#[Test]
190-
public function testAgeInvalid(): void
191-
{
192-
$this->assertSame('invalid', new AgeRule()->transform('invalid', $this->ctx()));
193-
}
194-
195163
#[Test]
196164
public function testGetName(): void
197165
{
198-
$this->assertIsString(new \KaririCode\Transformer\Rule\Date\DateToIso8601Rule()->getName());
199-
$this->assertIsString(new \KaririCode\Transformer\Rule\Date\DateToTimestampRule()->getName());
200-
$this->assertIsString(new \KaririCode\Transformer\Rule\Date\RelativeDateRule()->getName());
201-
$this->assertIsString(new AgeRule()->getName());
166+
$this->assertIsString((new \KaririCode\Transformer\Rule\Date\DateToIso8601Rule())->getName());
167+
$this->assertIsString((new \KaririCode\Transformer\Rule\Date\RelativeDateRule())->getName());
202168
}
203169
}

0 commit comments

Comments
 (0)