1
|
|
|
<?php |
2
|
|
|
declare(strict_types=1); |
3
|
|
|
/** |
4
|
|
|
* Created by PhpStorm. |
5
|
|
|
* User: benedikt |
6
|
|
|
* Date: 1/3/18 |
7
|
|
|
* Time: 3:53 PM |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
namespace Tfboe\FmLib\Tests\Unit\Service; |
11
|
|
|
|
12
|
|
|
use DateTime; |
13
|
|
|
use Doctrine\ORM\EntityManagerInterface; |
14
|
|
|
use Illuminate\Contracts\Container\BindingResolutionException; |
15
|
|
|
use PHPUnit\Framework\Error\Error; |
16
|
|
|
use PHPUnit\Framework\MockObject\MockObject; |
17
|
|
|
use ReflectionException; |
18
|
|
|
use Tfboe\FmLib\Entity\LastRecalculationInterface; |
19
|
|
|
use Tfboe\FmLib\Service\DynamicServiceLoadingService; |
20
|
|
|
use Tfboe\FmLib\Service\DynamicServiceLoadingServiceInterface; |
21
|
|
|
use Tfboe\FmLib\Service\RankingSystem\RankingSystemInterface; |
22
|
|
|
use Tfboe\FmLib\Service\RankingSystemService; |
23
|
|
|
use Tfboe\FmLib\Tests\Entity\Competition; |
24
|
|
|
use Tfboe\FmLib\Tests\Entity\Game; |
25
|
|
|
use Tfboe\FmLib\Tests\Entity\Match; |
26
|
|
|
use Tfboe\FmLib\Tests\Entity\Phase; |
27
|
|
|
use Tfboe\FmLib\Tests\Entity\RankingSystem; |
28
|
|
|
use Tfboe\FmLib\Tests\Entity\Tournament; |
29
|
|
|
use Tfboe\FmLib\Tests\Helpers\UnitTestCase; |
30
|
|
|
|
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Class EloRankingTest |
34
|
|
|
* @packageTfboe\FmLib\Tests\Unit\Service |
35
|
|
|
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) |
36
|
|
|
*/ |
37
|
|
|
class RankingSystemServiceTest extends UnitTestCase |
38
|
|
|
{ |
39
|
|
|
//<editor-fold desc="Public Methods"> |
40
|
|
|
/** |
41
|
|
|
* @covers \Tfboe\FmLib\Service\RankingSystemService::adaptOpenSyncFromValues |
42
|
|
|
* @throws ReflectionException |
43
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Tournament |
44
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Tournament |
45
|
|
|
* @uses \Tfboe\FmLib\Service\RankingSystemService::getRankingSystems |
46
|
|
|
* @uses \Tfboe\FmLib\Service\RankingSystemService::getRankingSystemsEarliestInfluences |
47
|
|
|
* @uses \Tfboe\FmLib\Service\RankingSystem\RankingSystemService::__construct |
48
|
|
|
* @uses \Tfboe\FmLib\Entity\Helpers\TournamentHierarchyEntity::__construct |
49
|
|
|
* @uses \Tfboe\FmLib\Entity\Helpers\TournamentHierarchyEntity::getRankingSystems |
50
|
|
|
* @uses \Tfboe\FmLib\Service\RankingSystemService::__construct |
51
|
|
|
*/ |
52
|
|
|
public function testAdaptOpenSyncFromValues() |
53
|
|
|
{ |
54
|
|
|
$serviceLoader = $this->createMock(DynamicServiceLoadingService::class); |
55
|
|
|
$serviceLoader->expects(self::exactly(2)) |
56
|
|
|
->method("loadRankingSystemService") |
57
|
|
View Code Duplication |
->willReturnCallback(function ($earliestInfluence) { |
|
|
|
|
58
|
|
|
$mock = $this->createMock(\Tfboe\FmLib\Service\RankingSystem\RankingSystemService::class); |
59
|
|
|
$mock->method("getEarliestInfluence")->willReturn(new DateTime($earliestInfluence)); |
60
|
|
|
return $mock; |
61
|
|
|
}); |
62
|
|
|
/** @var DynamicServiceLoadingService $serviceLoader */ |
63
|
|
|
/** @noinspection PhpParamsInspection */ |
64
|
|
|
$service = new RankingSystemService($serviceLoader, |
|
|
|
|
65
|
|
|
$this->getMockForAbstractClass(EntityManagerInterface::class)); |
|
|
|
|
66
|
|
|
|
67
|
|
|
|
68
|
|
|
$tournament = new Tournament(); |
69
|
|
|
$ranking = $this->createStubWithId(RankingSystem::class, 'r1'); |
70
|
|
|
$ranking->method('getServiceName')->willReturn("2017-01-01"); |
71
|
|
|
$ranking->method('getOpenSyncFrom')->willReturn(new DateTime("2017-01-01 15:00:00")); |
72
|
|
|
$ranking->expects(self::once())->method('setOpenSyncFrom')->with(new DateTime("2017-01-01")); |
73
|
|
|
/** @var \Tfboe\FmLib\Entity\RankingSystemInterface $ranking */ |
74
|
|
|
$tournament->getRankingSystems()->set($ranking->getId(), $ranking); |
75
|
|
|
|
76
|
|
|
$ranking2 = $this->createStubWithId(RankingSystem::class, 'r2'); |
77
|
|
|
$ranking2->method('getServiceName')->willReturn("2017-02-01"); |
78
|
|
|
$ranking2->method('getOpenSyncFrom')->willReturn(new DateTime("2017-01-30 15:00:00")); |
79
|
|
|
$ranking2->expects(self::once())->method('setOpenSyncFrom')->with(new DateTime("2017-01-30")); |
80
|
|
|
/** @var \Tfboe\FmLib\Entity\RankingSystemInterface $ranking2 */ |
81
|
|
|
$tournament->getRankingSystems()->set($ranking2->getId(), $ranking2); |
82
|
|
|
|
83
|
|
|
$ranking3 = $this->createStubWithId(RankingSystem::class, 'r3'); |
84
|
|
|
$ranking3->method('getOpenSyncFrom')->willReturn(null); |
85
|
|
|
$ranking3->expects(self::once())->method('setOpenSyncFrom')->with(new DateTime("2017-03-01")); |
86
|
|
|
$ranking4 = $this->createStubWithId(RankingSystem::class, 'r4'); |
87
|
|
|
$ranking4->method('getOpenSyncFrom')->willReturn(new DateTime("2017-04-01")); |
88
|
|
|
$ranking4->expects(self::never())->method('setOpenSyncFrom'); |
89
|
|
|
|
90
|
|
|
$service->adaptOpenSyncFromValues($tournament, [ |
91
|
|
|
'r1' => ["rankingSystem" => $ranking, "earliestInfluence" => new DateTime("2017-01-02")], |
92
|
|
|
'r2' => ["rankingSystem" => $ranking2, "earliestInfluence" => new DateTime("2017-01-30")], |
93
|
|
|
'r3' => ["rankingSystem" => $ranking3, "earliestInfluence" => new DateTime("2017-03-01")], |
94
|
|
|
'r4' => ["rankingSystem" => $ranking4, "earliestInfluence" => new DateTime("2017-06-01")], |
95
|
|
|
]); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @covers \Tfboe\FmLib\Service\RankingSystemService::applyRankingSystems |
100
|
|
|
* @covers \Tfboe\FmLib\Service\RankingSystemService::getRankingSystems |
101
|
|
|
* @throws BindingResolutionException |
102
|
|
|
* @throws ReflectionException |
103
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Tournament |
104
|
|
|
* @uses \Tfboe\FmLib\Service\RankingSystemService::__construct |
105
|
|
|
* @uses \Tfboe\FmLib\Service\RankingSystem\RankingSystemService::__construct |
106
|
|
|
* @uses \Tfboe\FmLib\Entity\Helpers\TournamentHierarchyEntity::__construct |
107
|
|
|
* @uses \Tfboe\FmLib\Entity\Helpers\TournamentHierarchyEntity::getRankingSystems |
108
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Tournament |
109
|
|
|
*/ |
110
|
|
|
public function testApplyRankingSystems() |
111
|
|
|
{ |
112
|
|
|
$tournament = new Tournament(); |
113
|
|
|
/** @var \Tfboe\FmLib\Entity\RankingSystemInterface $ranking2 */ |
114
|
|
|
$ranking2 = $this->createStubWithId(RankingSystem::class, 's2'); |
115
|
|
|
$tournament->getRankingSystems()->set($ranking2->getId(), $ranking2); |
116
|
|
|
/** @var \Tfboe\FmLib\Entity\RankingSystemInterface $ranking3 */ |
117
|
|
|
$ranking3 = $this->createStubWithId(RankingSystem::class, 's3'); |
118
|
|
|
|
119
|
|
|
$tournament->getRankingSystems()->set($ranking3->getId(), $ranking3); |
120
|
|
|
|
121
|
|
|
/** @var \Tfboe\FmLib\Entity\RankingSystemInterface $ranking4 */ |
122
|
|
|
$ranking4 = $this->createStubWithId(RankingSystem::class, 's4'); |
123
|
|
|
|
124
|
|
|
$oldInfluences = [ |
125
|
|
|
$ranking2->getId() => ["rankingSystem" => $ranking2, "earliestInfluence" => new DateTime("2017-02-01")], |
126
|
|
|
$ranking4->getId() => ["rankingSystem" => $ranking4, "earliestInfluence" => new DateTime("2017-04-01")] |
127
|
|
|
]; |
128
|
|
|
|
129
|
|
|
$serviceLoader = $this->createMock(DynamicServiceLoadingService::class); |
130
|
|
|
$mock = $this->createMock(\Tfboe\FmLib\Service\RankingSystem\RankingSystemService::class); |
131
|
|
|
$mock->expects(self::exactly(3))->method("updateRankingForTournament")->withConsecutive( |
132
|
|
|
[$ranking2, $tournament, self::equalTo(new DateTime("2017-02-01"))], |
133
|
|
|
[$ranking4, $tournament, self::equalTo(new DateTime("2017-04-01"))], |
134
|
|
|
[$ranking3, $tournament, null] |
135
|
|
|
); |
136
|
|
|
$serviceLoader->expects(self::exactly(3)) |
137
|
|
|
->method("loadRankingSystemService") |
138
|
|
|
->willReturn($mock); |
139
|
|
|
|
140
|
|
|
/** @var DynamicServiceLoadingService $serviceLoader */ |
141
|
|
|
/** @noinspection PhpParamsInspection */ |
142
|
|
|
$service = new RankingSystemService($serviceLoader, |
|
|
|
|
143
|
|
|
$this->getMockForAbstractClass(EntityManagerInterface::class)); |
|
|
|
|
144
|
|
|
$service->applyRankingSystems($tournament, $oldInfluences); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* @covers \Tfboe\FmLib\Service\RankingSystemService::__construct |
149
|
|
|
* @throws ReflectionException |
150
|
|
|
* @throws ReflectionException |
151
|
|
|
*/ |
152
|
|
|
public function testConstruct() |
153
|
|
|
{ |
154
|
|
|
$dsls = $this->getMockForAbstractClass(DynamicServiceLoadingServiceInterface::class); |
155
|
|
|
$entityManager = $this->getMockForAbstractClass(EntityManagerInterface::class); |
156
|
|
|
/** @var DynamicServiceLoadingServiceInterface $dsls */ |
157
|
|
|
/** @var EntityManagerInterface $entityManager */ |
158
|
|
|
$system = new RankingSystemService($dsls, $entityManager); |
|
|
|
|
159
|
|
|
self::assertInstanceOf(RankingSystemService::class, $system); |
160
|
|
|
/** @noinspection PhpUnhandledExceptionInspection */ |
161
|
|
|
self::assertEquals($entityManager, self::getProperty(get_class($system), 'entityManager')->getValue($system)); |
162
|
|
|
/** @noinspection PhpUnhandledExceptionInspection */ |
163
|
|
|
self::assertEquals($dsls, self::getProperty(get_class($system), 'dsls')->getValue($system)); |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* @covers \Tfboe\FmLib\Service\RankingSystemService::getRankingSystemsEarliestInfluences |
168
|
|
|
* @covers \Tfboe\FmLib\Service\RankingSystemService::getRankingSystems |
169
|
|
|
* @throws ReflectionException |
170
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Competition |
171
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Competition |
172
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Game |
173
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Game |
174
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Match |
175
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Match |
176
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Phase |
177
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Phase |
178
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Tournament |
179
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Tournament |
180
|
|
|
* @uses \Tfboe\FmLib\Entity\Helpers\NameEntity |
181
|
|
|
* @uses \Tfboe\FmLib\Service\RankingSystemService::__construct |
182
|
|
|
* @uses \Tfboe\FmLib\Service\RankingSystem\RankingSystemService::__construct |
183
|
|
|
* @uses \Tfboe\FmLib\Entity\Helpers\TournamentHierarchyEntity::__construct |
184
|
|
|
* @uses \Tfboe\FmLib\Entity\Helpers\TournamentHierarchyEntity::getRankingSystems |
185
|
|
|
*/ |
186
|
|
|
public function testGetRankingSystemsEarliestInfluences() |
187
|
|
|
{ |
188
|
|
|
$serviceLoader = $this->createMock(DynamicServiceLoadingService::class); |
189
|
|
|
$serviceLoader->expects(self::exactly(3)) |
190
|
|
|
->method("loadRankingSystemService") |
191
|
|
View Code Duplication |
->willReturnCallback(function ($earliestInfluence) { |
|
|
|
|
192
|
|
|
$mock = $this->createMock(\Tfboe\FmLib\Service\RankingSystem\RankingSystemService::class); |
193
|
|
|
$mock->method("getEarliestInfluence")->willReturn(new DateTime($earliestInfluence)); |
194
|
|
|
return $mock; |
195
|
|
|
}); |
196
|
|
|
/** @var DynamicServiceLoadingService $serviceLoader */ |
197
|
|
|
/** @noinspection PhpParamsInspection */ |
198
|
|
|
$service = new RankingSystemService($serviceLoader, |
|
|
|
|
199
|
|
|
$this->getMockForAbstractClass(EntityManagerInterface::class)); |
|
|
|
|
200
|
|
|
$tournament = new Tournament(); |
201
|
|
|
$ranking2 = $this->createStubWithId(RankingSystem::class, 'r2'); |
202
|
|
|
$ranking2->method('getServiceName')->willReturn("2017-04-01"); |
203
|
|
|
/** @var \Tfboe\FmLib\Entity\RankingSystemInterface $ranking2 */ |
204
|
|
|
$tournament->getRankingSystems()->set($ranking2->getId(), $ranking2); |
205
|
|
|
|
206
|
|
|
$competition = new Competition(); |
207
|
|
|
$competition->setName("TestCompetition")->setTournament($tournament); |
|
|
|
|
208
|
|
|
$phase = new Phase(); |
209
|
|
|
$phase->setPhaseNumber(1); |
210
|
|
|
$phase->setCompetition($competition); |
211
|
|
|
$ranking3 = $this->createStubWithId(RankingSystem::class, 'r3'); |
212
|
|
|
$ranking3->method('getServiceName')->willReturn("2017-02-01"); |
213
|
|
|
/** @var \Tfboe\FmLib\Entity\RankingSystemInterface $ranking3 */ |
214
|
|
|
|
215
|
|
|
$phase->getRankingSystems()->set($ranking3->getId(), $ranking3); |
216
|
|
|
|
217
|
|
|
$match = new Match(); |
218
|
|
|
$match->setMatchNumber(1); |
219
|
|
|
$match->setPhase($phase); |
220
|
|
|
$game = new Game(); |
221
|
|
|
$game->setGameNumber(1); |
222
|
|
|
$game->setMatch($match); |
223
|
|
|
$ranking4 = $this->createStubWithId(RankingSystem::class, 'r4'); |
224
|
|
|
$ranking4->method('getServiceName')->willReturn("2017-03-01"); |
225
|
|
|
/** @var \Tfboe\FmLib\Entity\RankingSystemInterface $ranking4 */ |
226
|
|
|
$game->getRankingSystems()->set($ranking4->getId(), $ranking4); |
227
|
|
|
|
228
|
|
|
|
229
|
|
|
self::assertEquals( |
230
|
|
|
[ |
231
|
|
|
$ranking2->getId() => ["rankingSystem" => $ranking2, "earliestInfluence" => new DateTime("2017-04-01")], |
232
|
|
|
$ranking3->getId() => ["rankingSystem" => $ranking3, "earliestInfluence" => new DateTime("2017-02-01")], |
233
|
|
|
$ranking4->getId() => ["rankingSystem" => $ranking4, "earliestInfluence" => new DateTime("2017-03-01")], |
234
|
|
|
], |
235
|
|
|
$service->getRankingSystemsEarliestInfluences($tournament)); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* @covers \Tfboe\FmLib\Service\RankingSystemService::getRankingSystemsEarliestInfluences |
240
|
|
|
* @throws ReflectionException |
241
|
|
|
* @uses \Tfboe\FmLib\Entity\Helpers\TournamentHierarchyEntity::getRankingSystems |
242
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Tournament::getChildren |
243
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Tournament::getCompetitions |
244
|
|
|
* @uses \Tfboe\FmLib\Entity\Traits\Tournament::init |
245
|
|
|
* @uses \Tfboe\FmLib\Exceptions\Internal::error |
246
|
|
|
* @uses \Tfboe\FmLib\Service\RankingSystemService::__construct |
247
|
|
|
* @uses \Tfboe\FmLib\Service\RankingSystemService::getRankingSystems |
248
|
|
|
* @uses \Tfboe\FmLib\Entity\Helpers\TournamentHierarchyEntity::__construct |
249
|
|
|
*/ |
250
|
|
|
public function testGetRankingSystemsEarliestInfluencesWithInvalidId() |
251
|
|
|
{ |
252
|
|
|
/** @var DynamicServiceLoadingService|MockObject $serviceLoader */ |
253
|
|
|
$serviceLoader = $this->createMock(DynamicServiceLoadingService::class); |
254
|
|
|
$serviceLoader->expects(self::once()) |
|
|
|
|
255
|
|
|
->method("loadRankingSystemService") |
256
|
|
|
->with('invalid') |
257
|
|
|
->willThrowException(new BindingResolutionException()); |
258
|
|
|
|
259
|
|
|
/** @var EntityManagerInterface $entityManager */ |
260
|
|
|
$entityManager = $this->getMockForAbstractClass(EntityManagerInterface::class); |
261
|
|
|
$service = new RankingSystemService($serviceLoader, $entityManager); |
|
|
|
|
262
|
|
|
$tournament = new Tournament(); |
263
|
|
|
$ranking2 = $this->createStubWithId(RankingSystem::class, 'r2'); |
264
|
|
|
$ranking2->method('getServiceName')->willReturn("invalid"); |
265
|
|
|
/** @var \Tfboe\FmLib\Entity\RankingSystemInterface $ranking2 */ |
266
|
|
|
$tournament->getRankingSystems()->set($ranking2->getId(), $ranking2); |
267
|
|
|
|
268
|
|
|
$this->expectException(Error::class); |
269
|
|
|
$this->expectExceptionMessage("The ranking system r2 has an invalid service name!"); |
270
|
|
|
|
271
|
|
|
$service->getRankingSystemsEarliestInfluences($tournament); |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* @covers \Tfboe\FmLib\Service\RankingSystemService::recalculateRankingSystems |
276
|
|
|
* @throws ReflectionException |
277
|
|
|
* @throws ReflectionException |
278
|
|
|
* @throws ReflectionException |
279
|
|
|
* @uses \Tfboe\FmLib\Helpers\Logging::log |
280
|
|
|
* @uses \Tfboe\FmLib\Service\RankingSystemService::__construct |
281
|
|
|
*/ |
282
|
|
|
public function testRecalculateRankingSystems() |
283
|
|
|
{ |
284
|
|
|
$rs1 = $this->createStubWithId(\Tfboe\FmLib\Entity\RankingSystemInterface::class, "rs1"); |
285
|
|
|
$rs1->expects(self::once())->method('getServiceName')->willReturn('service'); |
286
|
|
|
$rs1->expects(self::exactly(2))->method('getOpenSyncFrom')->willReturn(new DateTime("2017-02-01")); |
287
|
|
|
$rs1->expects(self::once())->method('setOpenSyncFrom')->with(null); |
288
|
|
|
$rs2 = $this->createStubWithId(\Tfboe\FmLib\Entity\RankingSystemInterface::class, "rs2"); |
289
|
|
|
$rs2->expects(self::once())->method('getServiceName')->willReturn('service'); |
290
|
|
|
$rs2->expects(self::exactly(2))->method('getOpenSyncFrom')->willReturn(new DateTime("2017-05-01")); |
291
|
|
|
$rs2->expects(self::once())->method('setOpenSyncFrom')->with(null); |
292
|
|
|
$slash = '\\'; |
293
|
|
|
$first = 'SELECT s'; |
294
|
|
|
$second = ' FROM Tfboe'; |
295
|
|
|
$third = 'FmLib'; |
296
|
|
|
$rest = 'RankingSystemInterface s WHERE s.openSyncFrom IS NOT NULL'; |
297
|
|
|
$entityManager = $this->getEntityManagerMockForQuery([$rs1, $rs2], |
298
|
|
|
$first . $second . $slash . $third . $slash . 'Entity' . $slash . $rest, ['flush', 'clear', 'transactional', |
299
|
|
|
'find']); |
300
|
|
|
$entityManager->method('transactional')->willReturnCallback(function ($f) use ($entityManager) { |
301
|
|
|
return $f($entityManager); |
302
|
|
|
}); |
303
|
|
|
$lastRecalculation = $this->createMock(LastRecalculationInterface::class); |
304
|
|
|
$entityManager->method('find')->willReturn($lastRecalculation); |
305
|
|
|
$dsls = $this->getMockForAbstractClass(DynamicServiceLoadingServiceInterface::class); |
306
|
|
|
$service = $this->getMockForAbstractClass(RankingSystemInterface::class); |
307
|
|
|
$service->expects(self::exactly(2))->method('updateRankingFrom') |
308
|
|
|
->withConsecutive([$rs1, new DateTime("2017-02-01")], [$rs2, new DateTime("2017-05-01")]); |
309
|
|
|
$dsls->expects(self::exactly(2))->method('loadRankingSystemService')->with('service')->willReturn($service); |
310
|
|
|
/** @var DynamicServiceLoadingServiceInterface $dsls */ |
311
|
|
|
/** @var EntityManagerInterface $entityManager */ |
312
|
|
|
$system = new RankingSystemService($dsls, $entityManager); |
|
|
|
|
313
|
|
|
$system->recalculateRankingSystems(); |
314
|
|
|
} |
315
|
|
|
//</editor-fold desc="Public Methods"> |
316
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.