packages/deriver/tests/Unit/Deriver/Evaluator/Call/Provider/DispatchProviderTest.php
1<?php
2
3declare (strict_types=1);
4
5namespace Tests\Unit\Deriver\Evaluator\Call\Provider;
6
7use Deriver\Deriver\Evaluator\Call\Provider\DispatchDecision;
8use Deriver\Deriver\Evaluator\Call\Provider\DispatchProvider;
9use Deriver\Deriver\Evaluator\Call\Provider\DispatchRequest;
10use Deriver\Deriver\Evaluator\Call\Provider\DispatchTarget;
11use Deriver\Deriver\Evaluator\Call\Provider\Provider;
12use Deriver\Deriver\Evaluator\Request\EntryPoint;
13use Deriver\Deriver\Evaluator\TargetProfile;
14use Deriver\Deriver\Evaluator\Term;
15use Deriver\Deriver\Evaluator\TermKind;
16use Deriver\Deriver\Providers\DeclarationProvider;
17use Deriver\Deriver\Providers\EntryPointProvider;
18use Deriver\Deriver\Providers\EnvironmentProvider;
19use Deriver\Deriver\Sources\Source;
20use Deriver\Deriver\Sources\SourceRef;
21use Deriver\Deriver\Sources\Sources;
22use Override;
23use PHPUnit\Framework\Attributes\CoversClass;
24use PHPUnit\Framework\Attributes\Small;
25use PHPUnit\Framework\Attributes\UsesClass;
26use PHPUnit\Framework\TestCase;
27
28/**
29 * @covers \Deriver\Deriver\Evaluator\Call\Provider\DispatchProvider
30 */
31#[CoversClass(DispatchProvider::class)]
32#[UsesClass(DeclarationProvider::class)]
33#[UsesClass(DispatchDecision::class)]
34#[UsesClass(DispatchRequest::class)]
35#[UsesClass(DispatchTarget::class)]
36#[UsesClass(EntryPointProvider::class)]
37#[UsesClass(EnvironmentProvider::class)]
38#[UsesClass(Provider::class)]
39#[UsesClass(TargetProfile::class)]
40#[UsesClass(SourceRef::class)]
41#[UsesClass(Term::class)]
42#[Small]
43final class DispatchProviderTest extends TestCase
44{
45 public function testResolveSeparatesExhaustiveBindingsFromUnmatchedCalls(): void
46 {
47 $source = new SourceRef('s', 'f.php', 0, 1);
48 $receiver = Term::parameter('service', 'Contract');
49 $request = new DispatchRequest($receiver, 'label', false, $source, new TargetProfile());
50 $decision = (new class () implements DeclarationProvider, DispatchProvider, EnvironmentProvider, EntryPointProvider {
51 /**
52 * @return string Provider identity
53 */
54 #[Override]
55 public function id(): string
56 {
57 return 'example.container';
58 }
59 /**
60 * @return string Captured export version
61 */
62 #[Override]
63 public function version(): string
64 {
65 return '1';
66 }
67 /**
68 * @return Sources Explicit generated declaration and semantics
69 */
70 #[Override]
71 public function declarations(): Sources
72 {
73 return new Sources([new Source('generated/service.php', '<?php class GeneratedService{public function label(){return "service";}}')]);
74 }
75 /**
76 * @param DispatchRequest $request Evaluated call metadata
77 * @return DispatchDecision Exported binding, with explicit completeness
78 */
79 #[Override]
80 public function resolve(DispatchRequest $request): DispatchDecision
81 {
82 if ($request->method !== 'label' || ($request->receiver->attributes['type'] ?? '') !== 'Contract') {
83 return new DispatchDecision();
84 }
85 return new DispatchDecision([new DispatchTarget('GeneratedService::label', new Term(TermKind::Object, 'container:shared-service', attributes: ['class' => 'GeneratedService']))], true, ['container:export-complete']);
86 }
87 /**
88 * @return array<string, Term> Captured settings
89 */
90 #[Override]
91 public function environment(): array
92 {
93 return ['APP_NAME' => Term::constant('captured')];
94 }
95 /**
96 * @return list<EntryPoint> Declared lifecycle entries
97 */
98 #[Override]
99 public function entries(): array
100 {
101 return [new EntryPoint('entry')];
102 }
103 })->resolve($request);
104 self::assertTrue($decision->exhaustive);
105 self::assertSame('GeneratedService::label', $decision->targets[0]->symbol);
106 }
107}
108