Passed
Push — main ( 71fa0f...eaed76 )
by Chema
01:29
created

SuffixExtendsRuleTest::test_config_suffix_rule()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 12
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 18
rs 9.8666
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GacelaTest\Unit\PHPStan\Rules;
6
7
use Gacela\Framework\AbstractConfig;
8
use Gacela\Framework\AbstractFacade;
9
use Gacela\Framework\AbstractFactory;
10
use Gacela\Framework\AbstractProvider;
11
use Gacela\PHPStan\Rules\SuffixExtendsRule;
12
use PhpParser\Node\Stmt\Class_;
13
use PHPStan\Analyser\Scope;
14
use PHPStan\Reflection\ClassReflection;
15
use PHPUnit\Framework\TestCase;
16
17
final class SuffixExtendsRuleTest extends TestCase
18
{
19
    public function test_returns_empty_array_for_anonymous_class(): void
20
    {
21
        $rule = new SuffixExtendsRule('Facade', AbstractFacade::class);
22
        $node = self::createStub(Class_::class);
23
        $node->method('isAnonymous')->willReturn(true);
24
25
        $scope = self::createStub(Scope::class);
26
27
        $result = $rule->processNode($node, $scope);
28
29
        self::assertSame([], $result);
30
    }
31
32
    public function test_returns_empty_array_when_class_reflection_is_null(): void
33
    {
34
        $rule = new SuffixExtendsRule('Facade', AbstractFacade::class);
35
        $node = self::createStub(Class_::class);
36
        $node->method('isAnonymous')->willReturn(false);
37
38
        $scope = self::createStub(Scope::class);
39
        $scope->method('getClassReflection')->willReturn(null);
40
41
        $result = $rule->processNode($node, $scope);
42
43
        self::assertSame([], $result);
44
    }
45
46
    public function test_returns_empty_array_when_class_does_not_have_suffix(): void
47
    {
48
        $rule = new SuffixExtendsRule('Facade', AbstractFacade::class);
49
        $node = self::createStub(Class_::class);
50
        $node->method('isAnonymous')->willReturn(false);
51
52
        $classReflection = $this->createMock(ClassReflection::class);
53
        $classReflection->method('getName')->willReturn('App\Module\Service');
54
55
        $scope = self::createStub(Scope::class);
56
        $scope->method('getClassReflection')->willReturn($classReflection);
57
58
        $result = $rule->processNode($node, $scope);
59
60
        self::assertSame([], $result);
61
    }
62
63
    public function test_returns_empty_array_when_class_with_suffix_extends_expected_parent(): void
64
    {
65
        $rule = new SuffixExtendsRule('Facade', AbstractFacade::class);
66
        $node = self::createStub(Class_::class);
67
        $node->method('isAnonymous')->willReturn(false);
68
69
        $classReflection = $this->createMock(ClassReflection::class);
70
        $classReflection->method('getName')->willReturn('App\Module\UserFacade');
71
        $classReflection->method('isSubclassOf')->with(AbstractFacade::class)->willReturn(true);
72
73
        $scope = self::createStub(Scope::class);
74
        $scope->method('getClassReflection')->willReturn($classReflection);
75
76
        $result = $rule->processNode($node, $scope);
77
78
        self::assertSame([], $result);
79
    }
80
81
    public function test_returns_empty_array_when_class_is_the_expected_parent_itself(): void
82
    {
83
        $rule = new SuffixExtendsRule('Facade', AbstractFacade::class);
84
        $node = self::createStub(Class_::class);
85
        $node->method('isAnonymous')->willReturn(false);
86
87
        $classReflection = $this->createMock(ClassReflection::class);
88
        $classReflection->method('getName')->willReturn(AbstractFacade::class);
89
        $classReflection->method('isSubclassOf')->with(AbstractFacade::class)->willReturn(false);
90
91
        $scope = self::createStub(Scope::class);
92
        $scope->method('getClassReflection')->willReturn($classReflection);
93
94
        $result = $rule->processNode($node, $scope);
95
96
        self::assertSame([], $result);
97
    }
98
99
    public function test_returns_error_when_class_with_suffix_does_not_extend_expected_parent(): void
100
    {
101
        $rule = new SuffixExtendsRule('Facade', AbstractFacade::class);
102
        $node = self::createStub(Class_::class);
103
        $node->method('isAnonymous')->willReturn(false);
104
105
        $classReflection = $this->createMock(ClassReflection::class);
106
        $classReflection->method('getName')->willReturn('App\Module\UserFacade');
107
        $classReflection->method('isSubclassOf')->with(AbstractFacade::class)->willReturn(false);
108
109
        $scope = self::createStub(Scope::class);
110
        $scope->method('getClassReflection')->willReturn($classReflection);
111
112
        $result = $rule->processNode($node, $scope);
113
114
        self::assertCount(1, $result);
115
        self::assertSame(
116
            'Class App\Module\UserFacade should extend ' . AbstractFacade::class,
117
            $result[0],
118
        );
119
    }
120
121
    public function test_facade_suffix_rule(): void
122
    {
123
        $rule = new SuffixExtendsRule('Facade', AbstractFacade::class);
124
        $node = self::createStub(Class_::class);
125
        $node->method('isAnonymous')->willReturn(false);
126
127
        $classReflection = $this->createMock(ClassReflection::class);
128
        $classReflection->method('getName')->willReturn('InvalidFacade');
129
        $classReflection->method('isSubclassOf')->with(AbstractFacade::class)->willReturn(false);
130
131
        $scope = self::createStub(Scope::class);
132
        $scope->method('getClassReflection')->willReturn($classReflection);
133
134
        $result = $rule->processNode($node, $scope);
135
136
        self::assertCount(1, $result);
137
        self::assertStringContainsString('should extend', $result[0]);
138
    }
139
140
    public function test_factory_suffix_rule(): void
141
    {
142
        $rule = new SuffixExtendsRule('Factory', AbstractFactory::class);
143
        $node = self::createStub(Class_::class);
144
        $node->method('isAnonymous')->willReturn(false);
145
146
        $classReflection = $this->createMock(ClassReflection::class);
147
        $classReflection->method('getName')->willReturn('InvalidFactory');
148
        $classReflection->method('isSubclassOf')->with(AbstractFactory::class)->willReturn(false);
149
150
        $scope = self::createStub(Scope::class);
151
        $scope->method('getClassReflection')->willReturn($classReflection);
152
153
        $result = $rule->processNode($node, $scope);
154
155
        self::assertCount(1, $result);
156
        self::assertStringContainsString('InvalidFactory', $result[0]);
157
        self::assertStringContainsString(AbstractFactory::class, $result[0]);
158
    }
159
160
    public function test_provider_suffix_rule(): void
161
    {
162
        $rule = new SuffixExtendsRule('Provider', AbstractProvider::class);
163
        $node = self::createStub(Class_::class);
164
        $node->method('isAnonymous')->willReturn(false);
165
166
        $classReflection = $this->createMock(ClassReflection::class);
167
        $classReflection->method('getName')->willReturn('InvalidProvider');
168
        $classReflection->method('isSubclassOf')->with(AbstractProvider::class)->willReturn(false);
169
170
        $scope = self::createStub(Scope::class);
171
        $scope->method('getClassReflection')->willReturn($classReflection);
172
173
        $result = $rule->processNode($node, $scope);
174
175
        self::assertCount(1, $result);
176
        self::assertStringContainsString('InvalidProvider', $result[0]);
177
        self::assertStringContainsString(AbstractProvider::class, $result[0]);
178
    }
179
180
    public function test_config_suffix_rule(): void
181
    {
182
        $rule = new SuffixExtendsRule('Config', AbstractConfig::class);
183
        $node = self::createStub(Class_::class);
184
        $node->method('isAnonymous')->willReturn(false);
185
186
        $classReflection = $this->createMock(ClassReflection::class);
187
        $classReflection->method('getName')->willReturn('InvalidConfig');
188
        $classReflection->method('isSubclassOf')->with(AbstractConfig::class)->willReturn(false);
189
190
        $scope = self::createStub(Scope::class);
191
        $scope->method('getClassReflection')->willReturn($classReflection);
192
193
        $result = $rule->processNode($node, $scope);
194
195
        self::assertCount(1, $result);
196
        self::assertStringContainsString('InvalidConfig', $result[0]);
197
        self::assertStringContainsString(AbstractConfig::class, $result[0]);
198
    }
199
200
    public function test_handles_namespace_extraction_correctly(): void
201
    {
202
        $rule = new SuffixExtendsRule('Facade', AbstractFacade::class);
203
        $node = self::createStub(Class_::class);
204
        $node->method('isAnonymous')->willReturn(false);
205
206
        $classReflection = $this->createMock(ClassReflection::class);
207
        $classReflection->method('getName')->willReturn('Very\Long\Namespace\Path\UserFacade');
208
        $classReflection->method('isSubclassOf')->with(AbstractFacade::class)->willReturn(false);
209
210
        $scope = self::createStub(Scope::class);
211
        $scope->method('getClassReflection')->willReturn($classReflection);
212
213
        $result = $rule->processNode($node, $scope);
214
215
        self::assertCount(1, $result);
216
        self::assertStringContainsString('Very\Long\Namespace\Path\UserFacade', $result[0]);
217
    }
218
219
    public function test_get_node_type_returns_class(): void
220
    {
221
        $rule = new SuffixExtendsRule('Facade', AbstractFacade::class);
222
223
        self::assertSame(Class_::class, $rule->getNodeType());
224
    }
225
}
226