packages/deriver/tests/Unit/Deriver/Providers/DomainProviderTest.php

1<?php
2
3declare (strict_types=1);
4
5namespace Tests\Unit\Deriver\Providers;
6
7use Deriver\Deriver\Evaluator\Call\Provider\RefinementEvaluator;
8use Deriver\Deriver\Evaluator\Domain\AbstractDomain;
9use Deriver\Deriver\Evaluator\Domain\DomainFact;
10use Deriver\Deriver\Evaluator\Projection;
11use Deriver\Deriver\Evaluator\Term;
12use Deriver\Deriver\Evaluator\TermKind;
13use Deriver\Deriver\Providers\DomainProvider;
14use Override;
15use PHPUnit\Framework\Attributes\CoversClass;
16use PHPUnit\Framework\Attributes\Small;
17use PHPUnit\Framework\Attributes\UsesClass;
18use PHPUnit\Framework\TestCase;
19
20/**
21 * @covers \Deriver\Deriver\Providers\DomainProvider
22 */
23#[CoversClass(DomainProvider::class)]
24#[UsesClass(AbstractDomain::class)]
25#[Small]
26final class DomainProviderTest extends TestCase
27{
28    public function testDomainsContributesThePolicyLattice(): void
29    {
30        $provider = new class () implements DomainProvider, RefinementEvaluator {
31            /**
32             * @return string Stable provider identity
33             */
34            #[Override]
35            public function id(): string
36            {
37                return 'example.policy-provider';
38            }
39            /**
40             * @return string Semantic version
41             */
42            #[Override]
43            public function version(): string
44            {
45                return '1';
46            }
47            /**
48             * @return list<AbstractDomain> Captured domains
49             */
50            #[Override]
51            public function domains(): array
52            {
53                return [new class () implements AbstractDomain {
54                    /**
55                     * @return string Domain identity
56                     */
57                    #[Override]
58                    public function id(): string
59                    {
60                        return 'example.policy';
61                    }
62                    /**
63                     * @return string Semantic version
64                     */
65                    #[Override]
66                    public function version(): string
67                    {
68                        return '1';
69                    }
70                    /**
71                     * @return DomainFact Empty set
72                     */
73                    #[Override]
74                    public function bottom(): DomainFact
75                    {
76                        return new DomainFact($this->id(), new Term(TermKind::Bottom));
77                    }
78                    /**
79                     * @return DomainFact All policy records
80                     */
81                    #[Override]
82                    public function top(): DomainFact
83                    {
84                        return new DomainFact($this->id(), new Term(TermKind::Top));
85                    }
86                    /**
87                     * @param DomainFact $a Contained fact
88                     * @param DomainFact $b Containing fact
89                     * @return bool Flat-lattice inclusion
90                     */
91                    #[Override]
92                    public function lessOrEqual(DomainFact $a, DomainFact $b): bool
93                    {
94                        return $a->representation->kind === TermKind::Bottom || $b->representation->kind === TermKind::Top || $a->representation->isConcrete() && $b->representation->isConcrete() && $a->representation->native() === $b->representation->native();
95                    }
96                    /**
97                     * @param DomainFact $a First possibility
98                     * @param DomainFact $b Second possibility
99                     * @return DomainFact Inclusive alternative
100                     */
101                    #[Override]
102                    public function join(DomainFact $a, DomainFact $b): DomainFact
103                    {
104                        if ($this->lessOrEqual($a, $b)) {
105                            return $b;
106                        }
107                        return $this->lessOrEqual($b, $a) ? $a : $this->top();
108                    }
109                    /**
110                     * @param DomainFact $previous Previous approximation
111                     * @param DomainFact $next New approximation
112                     * @return DomainFact Finite widening
113                     */
114                    #[Override]
115                    public function widen(DomainFact $previous, DomainFact $next): DomainFact
116                    {
117                        return $this->join($previous, $next);
118                    }
119                    /**
120                     * @param DomainFact $value Policy record
121                     * @param Projection $projection Selected fields
122                     * @return DomainFact Projected fact
123                     */
124                    #[Override]
125                    public function project(DomainFact $value, Projection $projection): DomainFact
126                    {
127                        $term = $value->representation;
128                        foreach ($projection->path as $key) {
129                            $term = $term->operands[$key] ?? new Term(TermKind::Top);
130                        }
131                        return new DomainFact($this->id(), $term);
132                    }
133                }];
134            }
135            /**
136             * @param Term $predicate Branch predicate
137             * @param bool $truth Selected polarity
138             * @return Term Guaranteed implication
139             */
140            #[Override]
141            public function refine(Term $predicate, bool $truth): Term
142            {
143                return $truth ? $predicate : new Term(TermKind::Unary, '!', [$predicate]);
144            }
145        };
146        self::assertSame('example.policy', $provider->domains()[0]->id());
147    }
148}
149