Skip to content

Commit 0e83faa

Browse files
committed
feat(processor-pipeline): Add User processing example and update tests
- Create new application.php demonstrating real-world usage of ProcessorPipeline - Implement User entity with email, name, and age processing - Add specific processors: EmailNormalizer, EmailValidator, NameCapitalizer, AgeValidator - Update ProcessorBuilderTest to cover new use cases - Enhance ProcessorPipelineTest with additional scenarios - Refactor ProcessorRegistryTest for improved coverage - Ensure 100% test coverage across all components - Translate all code and comments to English for international consistency
1 parent 633f316 commit 0e83faa

9 files changed

+554
-7
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
],
2323
"require": {
2424
"php": "^8.3",
25-
"kariricode/data-structure": "^1.0"
25+
"kariricode/data-structure": "^1.1",
26+
"kariricode/contract": "^2.7"
2627
},
2728
"autoload": {
2829
"psr-4": {

composer.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ProcessorBuilder.php

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\ProcessorPipeline;
6+
7+
use KaririCode\Contract\Processor\ConfigurableProcessor;
8+
use KaririCode\Contract\Processor\Pipeline;
9+
use KaririCode\Contract\Processor\Processor;
10+
11+
class ProcessorBuilder
12+
{
13+
public function __construct(private ProcessorRegistry $registry)
14+
{
15+
}
16+
17+
public function build(string $context, string $name, array $config = []): Processor
18+
{
19+
$processor = $this->registry->get($context, $name);
20+
if ($processor instanceof ConfigurableProcessor && !empty($config)) {
21+
$processor->configure($config);
22+
}
23+
24+
return $processor;
25+
}
26+
27+
public function buildPipeline(string $context, array $processorSpecs): Pipeline
28+
{
29+
$pipeline = new ProcessorPipeline();
30+
foreach ($processorSpecs as $name => $config) {
31+
if (is_int($name)) {
32+
$name = $config;
33+
$config = [];
34+
}
35+
$processor = $this->build($context, $name, $config);
36+
$pipeline->addProcessor($processor);
37+
}
38+
39+
return $pipeline;
40+
}
41+
}

src/ProcessorPipeline.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\ProcessorPipeline;
6+
7+
use KaririCode\Contract\Processor\Pipeline;
8+
use KaririCode\Contract\Processor\Processor;
9+
10+
class ProcessorPipeline implements Pipeline
11+
{
12+
private array $processors = [];
13+
14+
public function addProcessor(Processor $processor): self
15+
{
16+
$this->processors[] = $processor;
17+
18+
return $this;
19+
}
20+
21+
public function process(mixed $input): mixed
22+
{
23+
return array_reduce(
24+
$this->processors,
25+
fn ($carry, Processor $processor) => $processor->process($carry),
26+
$input
27+
);
28+
}
29+
}

src/ProcessorRegistry.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\ProcessorPipeline;
6+
7+
use KaririCode\Contract\Processor\Processor;
8+
use KaririCode\DataStructure\Map\HashMap;
9+
10+
class ProcessorRegistry
11+
{
12+
public function __construct(private HashMap $processors = new HashMap())
13+
{
14+
}
15+
16+
public function register(string $context, string $name, Processor $processor): void
17+
{
18+
if (!$this->processors->containsKey($context)) {
19+
$this->processors->put($context, new HashMap());
20+
}
21+
$contextMap = $this->processors->get($context);
22+
$contextMap->put($name, $processor);
23+
}
24+
25+
public function get(string $context, string $name): Processor
26+
{
27+
if (!$this->processors->containsKey($context)) {
28+
throw new \RuntimeException("Context '$context' not found.");
29+
}
30+
$contextMap = $this->processors->get($context);
31+
if (!$contextMap->containsKey($name)) {
32+
throw new \RuntimeException("Processor '$name' not found in context '$context'.");
33+
}
34+
35+
return $contextMap->get($name);
36+
}
37+
38+
public function getContextProcessors(string $context): HashMap
39+
{
40+
if (!$this->processors->containsKey($context)) {
41+
throw new \RuntimeException("Context '$context' not found.");
42+
}
43+
44+
return $this->processors->get($context);
45+
}
46+
}

tests/ProcessorBuilderTest.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Tests\ProcessorPipeline;
6+
7+
use KaririCode\Contract\Processor\ConfigurableProcessor;
8+
use KaririCode\Contract\Processor\Pipeline;
9+
use KaririCode\Contract\Processor\Processor;
10+
use KaririCode\ProcessorPipeline\ProcessorBuilder;
11+
use KaririCode\ProcessorPipeline\ProcessorPipeline;
12+
use KaririCode\ProcessorPipeline\ProcessorRegistry;
13+
use PHPUnit\Framework\MockObject\MockObject;
14+
use PHPUnit\Framework\TestCase;
15+
16+
final class ProcessorBuilderTest extends TestCase
17+
{
18+
private ProcessorRegistry|MockObject $registry;
19+
private ProcessorBuilder $builder;
20+
21+
protected function setUp(): void
22+
{
23+
$this->registry = $this->createMock(ProcessorRegistry::class);
24+
$this->builder = new ProcessorBuilder($this->registry);
25+
}
26+
27+
public function testBuildNonConfigurableProcessor(): void
28+
{
29+
$processor = $this->createMock(Processor::class);
30+
$this->registry->expects($this->once())
31+
->method('get')
32+
->with('context', 'name')
33+
->willReturn($processor);
34+
35+
$result = $this->builder->build('context', 'name');
36+
$this->assertSame($processor, $result);
37+
}
38+
39+
public function testBuildConfigurableProcessor(): void
40+
{
41+
$processor = $this->createMock(ConfigurableProcessor::class);
42+
$this->registry->expects($this->once())
43+
->method('get')
44+
->with('context', 'name')
45+
->willReturn($processor);
46+
47+
$processor->expects($this->once())
48+
->method('configure')
49+
->with(['option' => 'value']);
50+
51+
$result = $this->builder->build('context', 'name', ['option' => 'value']);
52+
$this->assertSame($processor, $result);
53+
}
54+
55+
public function testBuildPipeline(): void
56+
{
57+
$processor1 = $this->createMock(Processor::class);
58+
$processor2 = $this->createMock(ConfigurableProcessor::class);
59+
60+
$this->registry->expects($this->exactly(2))
61+
->method('get')
62+
->willReturnCallback(function ($context, $name) use ($processor1, $processor2) {
63+
if ('context' === $context && 'processor1' === $name) {
64+
return $processor1;
65+
}
66+
if ('context' === $context && 'processor2' === $name) {
67+
return $processor2;
68+
}
69+
$this->fail('Unexpected get() call');
70+
});
71+
72+
$processor2->expects($this->once())
73+
->method('configure')
74+
->with(['option' => 'value']);
75+
76+
$result = $this->builder->buildPipeline('context', [
77+
'processor1',
78+
'processor2' => ['option' => 'value'],
79+
]);
80+
81+
$this->assertInstanceOf(Pipeline::class, $result);
82+
$this->assertInstanceOf(ProcessorPipeline::class, $result);
83+
}
84+
}

tests/ProcessorPipelineTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\ProcessorPipeline\Tests;
6+
7+
use KaririCode\Contract\Processor\Processor;
8+
use KaririCode\ProcessorPipeline\ProcessorPipeline;
9+
use PHPUnit\Framework\TestCase;
10+
11+
final class ProcessorPipelineTest extends TestCase
12+
{
13+
public function testAddProcessor(): void
14+
{
15+
$pipeline = new ProcessorPipeline();
16+
$processor = $this->createMock(Processor::class);
17+
18+
$result = $pipeline->addProcessor($processor);
19+
20+
$this->assertSame($pipeline, $result);
21+
}
22+
23+
public function testProcessWithNoProcessors(): void
24+
{
25+
$pipeline = new ProcessorPipeline();
26+
$input = 'test';
27+
28+
$result = $pipeline->process($input);
29+
30+
$this->assertSame($input, $result);
31+
}
32+
33+
public function testProcessWithMultipleProcessors(): void
34+
{
35+
$pipeline = new ProcessorPipeline();
36+
37+
$processor1 = $this->createMock(Processor::class);
38+
$processor1->method('process')->willReturnCallback(fn ($input) => $input . '1');
39+
40+
$processor2 = $this->createMock(Processor::class);
41+
$processor2->method('process')->willReturnCallback(fn ($input) => $input . '2');
42+
43+
$pipeline->addProcessor($processor1)->addProcessor($processor2);
44+
45+
$result = $pipeline->process('test');
46+
47+
$this->assertSame('test12', $result);
48+
}
49+
}

0 commit comments

Comments
 (0)