SyncTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-License-Identifier: AGPL-3.0-or-later
  5. */
  6. namespace OCA\User_LDAP\Tests\Jobs;
  7. use OCA\User_LDAP\Access;
  8. use OCA\User_LDAP\AccessFactory;
  9. use OCA\User_LDAP\Connection;
  10. use OCA\User_LDAP\ConnectionFactory;
  11. use OCA\User_LDAP\Helper;
  12. use OCA\User_LDAP\Jobs\Sync;
  13. use OCA\User_LDAP\LDAP;
  14. use OCA\User_LDAP\Mapping\UserMapping;
  15. use OCA\User_LDAP\User\Manager;
  16. use OCP\AppFramework\Utility\ITimeFactory;
  17. use OCP\IAvatarManager;
  18. use OCP\IConfig;
  19. use OCP\IDBConnection;
  20. use OCP\IUserManager;
  21. use OCP\Notification\IManager;
  22. use Test\TestCase;
  23. class SyncTest extends TestCase {
  24. /** @var array */
  25. protected $arguments;
  26. /** @var Helper|\PHPUnit\Framework\MockObject\MockObject */
  27. protected $helper;
  28. /** @var LDAP|\PHPUnit\Framework\MockObject\MockObject */
  29. protected $ldapWrapper;
  30. /** @var Manager|\PHPUnit\Framework\MockObject\MockObject */
  31. protected $userManager;
  32. /** @var UserMapping|\PHPUnit\Framework\MockObject\MockObject */
  33. protected $mapper;
  34. /** @var Sync */
  35. protected $sync;
  36. /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
  37. protected $config;
  38. /** @var IAvatarManager|\PHPUnit\Framework\MockObject\MockObject */
  39. protected $avatarManager;
  40. /** @var IDBConnection|\PHPUnit\Framework\MockObject\MockObject */
  41. protected $dbc;
  42. /** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */
  43. protected $ncUserManager;
  44. /** @var IManager|\PHPUnit\Framework\MockObject\MockObject */
  45. protected $notificationManager;
  46. /** @var ConnectionFactory|\PHPUnit\Framework\MockObject\MockObject */
  47. protected $connectionFactory;
  48. /** @var AccessFactory|\PHPUnit\Framework\MockObject\MockObject */
  49. protected $accessFactory;
  50. protected function setUp(): void {
  51. parent::setUp();
  52. $this->helper = $this->createMock(Helper::class);
  53. $this->ldapWrapper = $this->createMock(LDAP::class);
  54. $this->userManager = $this->createMock(Manager::class);
  55. $this->mapper = $this->createMock(UserMapping::class);
  56. $this->config = $this->createMock(IConfig::class);
  57. $this->avatarManager = $this->createMock(IAvatarManager::class);
  58. $this->dbc = $this->createMock(IDBConnection::class);
  59. $this->ncUserManager = $this->createMock(IUserManager::class);
  60. $this->notificationManager = $this->createMock(IManager::class);
  61. $this->connectionFactory = $this->createMock(ConnectionFactory::class);
  62. $this->accessFactory = $this->createMock(AccessFactory::class);
  63. $this->arguments = [
  64. 'helper' => $this->helper,
  65. 'ldapWrapper' => $this->ldapWrapper,
  66. 'mapper' => $this->mapper,
  67. 'config' => $this->config,
  68. 'avatarManager' => $this->avatarManager,
  69. 'dbc' => $this->dbc,
  70. 'ncUserManager' => $this->ncUserManager,
  71. 'notificationManager' => $this->notificationManager,
  72. 'connectionFactory' => $this->connectionFactory,
  73. 'accessFactory' => $this->accessFactory,
  74. ];
  75. $this->sync = new Sync($this->createMock(ITimeFactory::class));
  76. }
  77. public function intervalDataProvider() {
  78. return [
  79. [
  80. 0, 1000, 750
  81. ],
  82. [
  83. 22, 0, 50
  84. ],
  85. [
  86. 500, 500, 500
  87. ],
  88. [
  89. 1357, 0, 0
  90. ],
  91. [
  92. 421337, 2000, 3000
  93. ]
  94. ];
  95. }
  96. /**
  97. * @dataProvider intervalDataProvider
  98. */
  99. public function testUpdateInterval($userCount, $pagingSize1, $pagingSize2) {
  100. $this->config->expects($this->once())
  101. ->method('setAppValue')
  102. ->with('user_ldap', 'background_sync_interval', $this->anything())
  103. ->willReturnCallback(function ($a, $k, $interval) {
  104. $this->assertTrue($interval >= SYNC::MIN_INTERVAL);
  105. $this->assertTrue($interval <= SYNC::MAX_INTERVAL);
  106. return true;
  107. });
  108. $this->config->expects($this->atLeastOnce())
  109. ->method('getAppKeys')
  110. ->willReturn([
  111. 'blabla',
  112. 'ldap_paging_size',
  113. 's07blabla',
  114. 'installed',
  115. 's07ldap_paging_size'
  116. ]);
  117. $this->config->expects($this->exactly(2))
  118. ->method('getAppValue')
  119. ->willReturnOnConsecutiveCalls($pagingSize1, $pagingSize2);
  120. $this->mapper->expects($this->atLeastOnce())
  121. ->method('count')
  122. ->willReturn($userCount);
  123. $this->sync->setArgument($this->arguments);
  124. $this->sync->updateInterval();
  125. }
  126. public function moreResultsProvider() {
  127. return [
  128. [ 3, 3, true ],
  129. [ 3, 5, true ],
  130. [ 3, 2, false],
  131. [ 0, 4, false],
  132. [ null, 4, false]
  133. ];
  134. }
  135. /**
  136. * @dataProvider moreResultsProvider
  137. */
  138. public function testMoreResults($pagingSize, $results, $expected) {
  139. $connection = $this->createMock(Connection::class);
  140. $this->connectionFactory->expects($this->any())
  141. ->method('get')
  142. ->willReturn($connection);
  143. $connection->expects($this->any())
  144. ->method('__get')
  145. ->willReturnCallback(function ($key) use ($pagingSize) {
  146. if ($key === 'ldapPagingSize') {
  147. return $pagingSize;
  148. }
  149. return null;
  150. });
  151. /** @var Access|\PHPUnit\Framework\MockObject\MockObject $access */
  152. $access = $this->createMock(Access::class);
  153. $this->accessFactory->expects($this->any())
  154. ->method('get')
  155. ->with($connection)
  156. ->willReturn($access);
  157. $this->userManager->expects($this->any())
  158. ->method('getAttributes')
  159. ->willReturn(['dn', 'uid', 'mail', 'displayname']);
  160. $access->expects($this->once())
  161. ->method('fetchListOfUsers')
  162. ->willReturn(array_pad([], $results, 'someUser'));
  163. $access->expects($this->any())
  164. ->method('combineFilterWithAnd')
  165. ->willReturn('pseudo=filter');
  166. $access->connection = $connection;
  167. $access->userManager = $this->userManager;
  168. $this->sync->setArgument($this->arguments);
  169. $hasMoreResults = $this->sync->runCycle(['prefix' => 's01', 'offset' => 100]);
  170. $this->assertSame($expected, $hasMoreResults);
  171. }
  172. public function cycleDataProvider() {
  173. $lastCycle = ['prefix' => 's01', 'offset' => 1000];
  174. $lastCycle2 = ['prefix' => '', 'offset' => 1000];
  175. return [
  176. [ null, ['s01'], ['prefix' => 's01', 'offset' => 0] ],
  177. [ null, [''], ['prefix' => '', 'offset' => 0] ],
  178. [ $lastCycle, ['s01', 's02'], ['prefix' => 's02', 'offset' => 0] ],
  179. [ $lastCycle, [''], ['prefix' => '', 'offset' => 0] ],
  180. [ $lastCycle2, ['', 's01'], ['prefix' => 's01', 'offset' => 0] ],
  181. [ $lastCycle, [], null ],
  182. ];
  183. }
  184. /**
  185. * @dataProvider cycleDataProvider
  186. */
  187. public function testDetermineNextCycle($cycleData, $prefixes, $expectedCycle) {
  188. $this->helper->expects($this->any())
  189. ->method('getServerConfigurationPrefixes')
  190. ->with(true)
  191. ->willReturn($prefixes);
  192. if (is_array($expectedCycle)) {
  193. $this->config->expects($this->exactly(2))
  194. ->method('setAppValue')
  195. ->withConsecutive(
  196. ['user_ldap', 'background_sync_prefix', $expectedCycle['prefix']],
  197. ['user_ldap', 'background_sync_offset', $expectedCycle['offset']]
  198. );
  199. } else {
  200. $this->config->expects($this->never())
  201. ->method('setAppValue');
  202. }
  203. $this->sync->setArgument($this->arguments);
  204. $nextCycle = $this->sync->determineNextCycle($cycleData);
  205. if ($expectedCycle === null) {
  206. $this->assertNull($nextCycle);
  207. } else {
  208. $this->assertSame($expectedCycle['prefix'], $nextCycle['prefix']);
  209. $this->assertSame($expectedCycle['offset'], $nextCycle['offset']);
  210. }
  211. }
  212. public function testQualifiesToRun() {
  213. $cycleData = ['prefix' => 's01'];
  214. $this->config->expects($this->exactly(2))
  215. ->method('getAppValue')
  216. ->willReturnOnConsecutiveCalls(time() - 60 * 40, time() - 60 * 20);
  217. $this->sync->setArgument($this->arguments);
  218. $this->assertTrue($this->sync->qualifiesToRun($cycleData));
  219. $this->assertFalse($this->sync->qualifiesToRun($cycleData));
  220. }
  221. public function runDataProvider() {
  222. return [
  223. #0 - one LDAP server, reset
  224. [[
  225. 'prefixes' => [''],
  226. 'scheduledCycle' => ['prefix' => '', 'offset' => '4500'],
  227. 'pagingSize' => 500,
  228. 'usersThisCycle' => 0,
  229. 'expectedNextCycle' => ['prefix' => '', 'offset' => '0'],
  230. 'mappedUsers' => 123,
  231. ]],
  232. #1 - 2 LDAP servers, next prefix
  233. [[
  234. 'prefixes' => ['', 's01'],
  235. 'scheduledCycle' => ['prefix' => '', 'offset' => '4500'],
  236. 'pagingSize' => 500,
  237. 'usersThisCycle' => 0,
  238. 'expectedNextCycle' => ['prefix' => 's01', 'offset' => '0'],
  239. 'mappedUsers' => 123,
  240. ]],
  241. #2 - 2 LDAP servers, rotate prefix
  242. [[
  243. 'prefixes' => ['', 's01'],
  244. 'scheduledCycle' => ['prefix' => 's01', 'offset' => '4500'],
  245. 'pagingSize' => 500,
  246. 'usersThisCycle' => 0,
  247. 'expectedNextCycle' => ['prefix' => '', 'offset' => '0'],
  248. 'mappedUsers' => 123,
  249. ]],
  250. ];
  251. }
  252. /**
  253. * @dataProvider runDataProvider
  254. */
  255. public function testRun($runData) {
  256. $this->config->expects($this->any())
  257. ->method('getAppValue')
  258. ->willReturnCallback(function ($app, $key, $default) use ($runData) {
  259. if ($app === 'core' && $key === 'backgroundjobs_mode') {
  260. return 'cron';
  261. }
  262. if ($app = 'user_ldap') {
  263. // for getCycle()
  264. if ($key === 'background_sync_prefix') {
  265. return $runData['scheduledCycle']['prefix'];
  266. }
  267. if ($key === 'background_sync_offset') {
  268. return $runData['scheduledCycle']['offset'];
  269. }
  270. // for qualifiesToRun()
  271. if ($key === $runData['scheduledCycle']['prefix'] . '_lastChange') {
  272. return time() - 60 * 40;
  273. }
  274. // for getMinPagingSize
  275. if ($key === $runData['scheduledCycle']['prefix'] . 'ldap_paging_size') {
  276. return $runData['pagingSize'];
  277. }
  278. }
  279. return $default;
  280. });
  281. $this->config->expects($this->exactly(3))
  282. ->method('setAppValue')
  283. ->withConsecutive(
  284. ['user_ldap', 'background_sync_prefix', $runData['expectedNextCycle']['prefix']],
  285. ['user_ldap', 'background_sync_offset', $runData['expectedNextCycle']['offset']],
  286. ['user_ldap', 'background_sync_interval', $this->anything()]
  287. );
  288. $this->config->expects($this->any())
  289. ->method('getAppKeys')
  290. ->with('user_ldap')
  291. ->willReturn([$runData['scheduledCycle']['prefix'] . 'ldap_paging_size']);
  292. $this->helper->expects($this->any())
  293. ->method('getServerConfigurationPrefixes')
  294. ->with(true)
  295. ->willReturn($runData['prefixes']);
  296. $connection = $this->createMock(Connection::class);
  297. $this->connectionFactory->expects($this->any())
  298. ->method('get')
  299. ->willReturn($connection);
  300. $connection->expects($this->any())
  301. ->method('__get')
  302. ->willReturnCallback(function ($key) use ($runData) {
  303. if ($key === 'ldapPagingSize') {
  304. return $runData['pagingSize'];
  305. }
  306. return null;
  307. });
  308. /** @var Access|\PHPUnit\Framework\MockObject\MockObject $access */
  309. $access = $this->createMock(Access::class);
  310. $this->accessFactory->expects($this->any())
  311. ->method('get')
  312. ->with($connection)
  313. ->willReturn($access);
  314. $this->userManager->expects($this->any())
  315. ->method('getAttributes')
  316. ->willReturn(['dn', 'uid', 'mail', 'displayname']);
  317. $access->expects($this->once())
  318. ->method('fetchListOfUsers')
  319. ->willReturn(array_pad([], $runData['usersThisCycle'], 'someUser'));
  320. $access->expects($this->any())
  321. ->method('combineFilterWithAnd')
  322. ->willReturn('pseudo=filter');
  323. $access->connection = $connection;
  324. $access->userManager = $this->userManager;
  325. $this->mapper->expects($this->any())
  326. ->method('count')
  327. ->willReturn($runData['mappedUsers']);
  328. $this->sync->run($this->arguments);
  329. }
  330. }