FOSSology  4.7.1
Open Source License Compliance by Open Source Software
schedulerTest.php
Go to the documentation of this file.
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2014-2015, 2019 Siemens AG
4 
5  SPDX-License-Identifier: GPL-2.0-only
6 */
17 namespace Fossology\Reuser\Test;
18 
38 use Monolog\Logger;
39 
40 include_once(__DIR__.'/../../../lib/php/Test/Agent/AgentTestMockHelper.php');
41 include_once(__DIR__.'/SchedulerTestRunnerCli.php');
42 
47 class SchedulerTest extends \PHPUnit\Framework\TestCase
48 {
52  private $groupId = 3;
56  private $userId = 2;
60  private $testDb;
64  private $dbManager;
68  private $testInstaller;
72  private $licenseDao;
76  private $clearingDao;
80  private $copyrightDao;
88  private $uploadDao;
92  private $uploadPermDao;
96  private $highlightDao;
100  private $treeDao;
101 
105  private $runnerCli;
106 
110  protected function setUp() : void
111  {
112  $this->testDb = new TestPgDb("reuserSched");
113  $this->dbManager = $this->testDb->getDbManager();
114 
115  $this->licenseDao = new LicenseDao($this->dbManager);
116  $logger = new Logger("ReuserSchedulerTest");
117  $this->uploadPermDao = \Mockery::mock(UploadPermissionDao::class);
118  $this->uploadDao = new UploadDao($this->dbManager, $logger, $this->uploadPermDao);
119  $this->highlightDao = new HighlightDao($this->dbManager);
120  $this->clearingDecisionFilter = new ClearingDecisionFilter();
121  $this->clearingDao = new ClearingDao($this->dbManager, $this->uploadDao);
122  $this->copyrightDao = new CopyrightDao($this->dbManager, $this->uploadDao);
123  $this->treeDao = \Mockery::mock(TreeDao::class);
124 
125  $this->runnerCli = new SchedulerTestRunnerCli($this->testDb);
126  }
127 
131  protected function tearDown() : void
132  {
133  $this->testDb->fullDestruct();
134  $this->testDb = null;
135  $this->dbManager = null;
136  $this->licenseDao = null;
137  $this->highlightDao = null;
138  $this->clearingDao = null;
139  $this->copyrightDao = null;
140  }
141 
145  private function setUpRepo()
146  {
147  $sysConf = $this->testDb->getFossSysConf();
148  $this->testInstaller = new TestInstaller($sysConf);
149  $this->testInstaller->init();
150  $this->testInstaller->cpRepo();
151  }
152 
156  private function rmRepo()
157  {
158  $this->testInstaller->rmRepo();
159  $this->testInstaller->clear();
160  }
161 
165  private function setUpTables()
166  {
167  $this->testDb->createPlainTables(array('upload','upload_reuse','uploadtree',
168  'uploadtree_a','license_ref','license_ref_bulk','clearing_decision',
169  'clearing_decision_event','clearing_event','license_file','highlight',
170  'highlight_bulk','agent','pfile','ars_master','users','group_user_member',
171  'upload_clearing_license','report_info'),false);
172  $this->testDb->createSequences(array('agent_agent_pk_seq','pfile_pfile_pk_seq',
173  'upload_upload_pk_seq','nomos_ars_ars_pk_seq','license_file_fl_pk_seq',
174  'license_ref_rf_pk_seq','license_ref_bulk_lrb_pk_seq',
175  'clearing_decision_clearing_decision_pk_seq',
176  'clearing_event_clearing_event_pk_seq','report_info_pk_seq'),false);
177  $this->testDb->createViews(array('license_file_ref'),false);
178  $this->testDb->createConstraints(array('agent_pkey','pfile_pkey',
179  'upload_pkey_idx','FileLicense_pkey','clearing_event_pkey'),false);
180  $this->testDb->alterTables(array('agent','pfile','upload','ars_master',
181  'license_ref_bulk','license_ref','clearing_event','clearing_decision','license_file','highlight'),false);
182  $this->testDb->createInheritedTables();
183  $this->testDb->createInheritedArsTables(array('monk'));
184 
185  $this->testDb->insertData(array('pfile','upload','uploadtree_a','users',
186  'group_user_member','agent','license_file','monk_ars','report_info'),
187  false);
188  $this->testDb->insertData_license_ref(80);
189 
190  $this->testDb->resetSequenceAsMaxOf('agent_agent_pk_seq', 'agent', 'agent_pk');
191 
192  $this->testDb->setupSysconfig();
193  }
194 
198  private function getHeartCount($output)
199  {
200  $matches = array();
201  if (preg_match("/.*HEART: ([0-9]*).*/", $output, $matches)) {
202  return intval($matches[1]);
203  } else {
204  return -1;
205  }
206  }
207 
214  private function getFilteredClearings($uploadId, $groupId)
215  {
216  $bounds = $this->uploadDao->getParentItemBounds($uploadId);
217  return $this->clearingDao->getFileClearingsFolder($bounds, $groupId);
218  }
219 
228  {
230  }
231 
237  {
238  $this->setUpTables();
239  $this->setUpRepo();
240 
241  list($success, $output,$retCode) = $runner->run($uploadId=1, $this->userId);
242 
243  $this->assertTrue($success, 'cannot run runner');
244  $this->assertEquals($retCode, 0, 'reuser failed: '.$output);
245 
246  assertThat($this->getHeartCount($output), equalTo(0));
247 
248  $bounds = $this->uploadDao->getParentItemBounds($uploadId);
249  assertThat($this->clearingDao->getFileClearingsFolder($bounds, $groupId=5), is(emptyArray()));
250 
251  $this->rmRepo();
252  }
253 
260  protected function insertDecisionFromTwoEvents($scope=DecisionScopes::ITEM,$originallyClearedItemId=23)
261  {
262  $licenseRef1 = $this->licenseDao->getLicenseByShortName("SPL-1.0")->getRef();
263  $licenseRef2 = $this->licenseDao->getLicenseByShortName("Glide")->getRef();
264 
265  $addedLicenses = array($licenseRef1, $licenseRef2);
266  assertThat($addedLicenses, not(arrayContaining(null)));
267 
268  $clearingLicense1 = new ClearingLicense($licenseRef1, false, ClearingEventTypes::USER, "42", "44");
269  $clearingLicense2 = new ClearingLicense($licenseRef2, true, ClearingEventTypes::USER, "-42", "-44");
270 
271  $eventId1 = $this->clearingDao->insertClearingEvent($originallyClearedItemId, $this->userId, $this->groupId,
272  $licenseRef1->getId(), $clearingLicense1->isRemoved(),
273  $clearingLicense1->getType(), $clearingLicense1->getReportinfo(), $clearingLicense1->getComment());
274  $eventId2 = $this->clearingDao->insertClearingEvent($originallyClearedItemId, 5, $this->groupId,
275  $licenseRef2->getId(), $clearingLicense2->isRemoved(),
276  $clearingLicense2->getType(), $clearingLicense2->getReportinfo(), $clearingLicense2->getComment());
277 
278  $addedEventIds = array($eventId1, $eventId2);
279 
280  $this->clearingDao->createDecisionFromEvents($originallyClearedItemId, $this->userId,
281  $this->groupId, DecisionTypes::IDENTIFIED, $scope, $addedEventIds);
282 
283  return array($clearingLicense1, $clearingLicense2, $addedEventIds);
284  }
285 
293  {
295  }
296 
302  {
303  $this->setUpTables();
304  $this->setUpRepo();
305 
307 
308  list($success,$output,$retCode) = $runner->run($uploadId=3);
309 
310  $this->assertTrue($success, 'cannot run runner');
311  $this->assertEquals($retCode, 0, 'reuser failed: '.$output);
312 
313  assertThat($this->getHeartCount($output), equalTo(0));
314 
315  $decisions = $this->getFilteredClearings($uploadId, $this->groupId);
316  assertThat($decisions, is(emptyArray()));
317 
318  $this->rmRepo();
319  }
320 
332  {
333  $this->runnerReuserScanWithALocalClearing($this->runnerCli,1);
334  }
335 
341  private function runnerReuserScanWithALocalClearing($runner, $heartBeat=0)
342  {
343  $this->setUpTables();
344  $this->setUpRepo();
345 
346  $this->uploadDao->addReusedUpload($uploadId=3,$reusedUpload=2,$this->groupId,$this->groupId);
347 
348  list($clearingLicense1, $clearingLicense2, $addedEventIds) = $this->insertDecisionFromTwoEvents();
349 
350  /* upload 3 in the test db is the same as upload 2
351  * items 13-24 in upload 2 correspond to 33-44 */
352  $reusingUploadItemShift = 20;
353 
354  list($success,$output,$retCode) = $runner->run($uploadId, $this->userId, $this->groupId);
355 
356  $this->assertTrue($success, 'cannot run runner');
357  $this->assertEquals($retCode, 0, 'reuser failed: '.$output);
358  assertThat($this->getHeartCount($output), equalTo($heartBeat));
359 
360  $newUploadClearings = $this->getFilteredClearings($uploadId, $this->groupId);
361  $potentiallyReusableClearings = $this->getFilteredClearings($reusedUpload, $this->groupId);
362 
363  assertThat($newUploadClearings, is(arrayWithSize(1)));
364 
365  assertThat($potentiallyReusableClearings, is(arrayWithSize(1)));
367  $potentiallyReusableClearing = $potentiallyReusableClearings[0];
369  $newClearing = $newUploadClearings[0];
370 
371  assertThat($newClearing, not(equalTo($potentiallyReusableClearing)));
372  assertThat($newClearing->getClearingId(), not(equalTo($potentiallyReusableClearing->getClearingId())));
373 
374  assertThat($newClearing->getClearingLicenses(), arrayContainingInAnyOrder($clearingLicense1, $clearingLicense2));
375 
376  assertThat($newClearing->getType(), equalTo($potentiallyReusableClearing->getType()));
377  assertThat($newClearing->getScope(), equalTo($potentiallyReusableClearing->getScope()));
378 
379  assertThat($newClearing->getUploadTreeId(),
380  equalTo($potentiallyReusableClearing->getUploadTreeId() + $reusingUploadItemShift));
381 
382  $this->rmRepo();
383  }
384 
396  {
397  $this->runnerReuserScanWithARepoClearing($this->runnerCli);
398  }
399 
404  private function runnerReuserScanWithARepoClearing($runner)
405  {
406  $this->setUpTables();
407  $this->setUpRepo();
408 
409  $this->uploadDao->addReusedUpload($uploadId=3,$reusedUpload=2,$this->groupId,$this->groupId);
410 
411  list($clearingLicense1, $clearingLicense2, $addedEventIds) = $this->insertDecisionFromTwoEvents(
412  DecisionScopes::REPO,$originallyClearedItemId=23);
413  $clearingLicenses = array($clearingLicense1, $clearingLicense2);
414 
415  /* upload 3 in the test db is the same as upload 2
416  * items 13-24 in upload 2 correspond to 33-44 */
417  $reusingUploadItemShift = 20;
418 
419  list($success,$output,$retCode) = $runner->run($uploadId, $this->userId, $this->groupId);
420 
421  $this->assertTrue($success, 'cannot run runner');
422  $this->assertEquals($retCode, 0, 'reuser failed: '.$output);
423 
424  assertThat($this->getHeartCount($output), equalTo(0));
425 
426  $newUploadClearings = $this->getFilteredClearings($uploadId, $this->groupId);
427  $potentiallyReusableClearings = $this->getFilteredClearings($reusedUpload, $this->groupId);
428 
429  assertThat($newUploadClearings, is(arrayWithSize(1)));
430 
431  assertThat($potentiallyReusableClearings, is(arrayWithSize(1)));
433  $potentiallyReusableClearing = $potentiallyReusableClearings[0];
435  $newClearing = $newUploadClearings[0];
436 
437  /* they are actually the same ClearingDecision
438  * only sameFolder and sameUpload are different */
439  assertThat($newClearing, not(equalTo($potentiallyReusableClearing)));
440 
441  /* reuser should have not created a new clearing decision */
442  assertThat($newClearing->getClearingId(), equalTo($potentiallyReusableClearing->getClearingId()));
443 
444  assertThat($newClearing->getClearingLicenses(), arrayContainingInAnyOrder($clearingLicenses));
445 
446  assertThat($newClearing->getType(), equalTo($potentiallyReusableClearing->getType()));
447  assertThat($newClearing->getScope(), equalTo($potentiallyReusableClearing->getScope()));
448 
449  assertThat($newClearing->getUploadTreeId(),
450  equalTo($potentiallyReusableClearing->getUploadTreeId() + $reusingUploadItemShift));
451 
452  /* reuser should have not created a correct local event history */
453  $bounds = $this->uploadDao->getItemTreeBounds($originallyClearedItemId + $reusingUploadItemShift);
454  $newEvents = $this->clearingDao->getRelevantClearingEvents($bounds, $this->groupId);
455 
456  assertThat($newEvents, is(arrayWithSize(count($clearingLicenses))));
457 
459  foreach ($newEvents as $newEvent) {
460  assertThat($newEvent->getEventId(), anyOf($addedEventIds));
461  assertThat($newEvent->getClearingLicense(), anyOf($clearingLicenses));
462  }
463 
464  $this->rmRepo();
465  }
466 
471  private function runnerReuserScanWithARepoClearingEnhanced($runner)
472  {
473  $this->setUpTables();
474  $this->setUpRepo();
475 
476  $originallyClearedItemId = 23;
477  /* upload 3 in the test db is the same as upload 2 -> items 13-24 in upload 2 correspond to 33-44 */
478  $reusingUploadItemShift = 20;
479 
480  $this->uploadDao->addReusedUpload($uploadId=3,$reusedUpload=2,$this->groupId,$this->groupId,$reuseMode=2);
481 
482  $repoPath = $this->testDb->getFossSysConf().'/repo/files/';
483  $this->treeDao->shouldReceive('getRepoPathOfPfile')->with(4)->andReturn($repoPath
484  .'04621571bcbabce75c4dd1c6445b87dec0995734.59cacdfce5051cd8a1d8a1f2dcce40a5.12320');
485  $this->treeDao->shouldReceive('getRepoPathOfPfile')->with(351)->andReturn($repoPath
486  .'c518ce1658140b65fa0132ad1130cb91512416bf.8e913e594d24ff3aeabe350107d97815.35829');
487 
488  list($clearingLicense1, $clearingLicense2, $addedEventIds) = $this->insertDecisionFromTwoEvents(
489  DecisionScopes::REPO,$originallyClearedItemId);
490  $clearingLicenses = array($clearingLicense1, $clearingLicense2);
491 
492  list($success,$output,$retCode) = $runner->run($uploadId, $this->userId, $this->groupId);
493 
494  $this->assertTrue($success, 'cannot run runner');
495  $this->assertEquals($retCode, 0, 'reuser failed: '.$output);
496 
497  $newUploadClearings = $this->getFilteredClearings($uploadId, $this->groupId);
498  $potentiallyReusableClearings = $this->getFilteredClearings($reusedUpload, $this->groupId);
499 
500  assertThat($newUploadClearings, is(arrayWithSize(1)));
501 
502  assertThat($potentiallyReusableClearings, is(arrayWithSize(1)));
504  $potentiallyReusableClearing = $potentiallyReusableClearings[0];
506  $newClearing = $newUploadClearings[0];
507 
508  /* they are actually the same ClearingDecision
509  * only sameFolder and sameUpload are different */
510  assertThat($newClearing, not(equalTo($potentiallyReusableClearing)));
511 
512  assertThat($newClearing->getClearingLicenses(), arrayContainingInAnyOrder($clearingLicenses));
513 
514  assertThat($newClearing->getType(), equalTo($potentiallyReusableClearing->getType()));
515  assertThat($newClearing->getScope(), equalTo($potentiallyReusableClearing->getScope()));
516 
517  assertThat($newClearing->getUploadTreeId(),
518  equalTo($potentiallyReusableClearing->getUploadTreeId() + $reusingUploadItemShift));
519 
520  /* reuser should have not created a correct local event history */
521  $bounds = $this->uploadDao->getItemTreeBounds($originallyClearedItemId + $reusingUploadItemShift);
522  $newEvents = $this->clearingDao->getRelevantClearingEvents($bounds, $this->groupId);
523 
524  assertThat($newEvents, is(arrayWithSize(count($clearingLicenses))));
525 
527  foreach ($newEvents as $newEvent) {
528  assertThat($newEvent->getEventId(), anyOf($addedEventIds));
529  assertThat($newEvent->getClearingLicense(), anyOf($clearingLicenses));
530  }
531  /*reuse main license*/
532  $this->clearingDao->makeMainLicense($uploadId=2, $this->groupId, $mainLicenseId=402);
533  $mainLicenseIdForReuse = $this->clearingDao->getMainLicenseIds($reusedUploadId=2, $this->groupId);
534  $mainLicenseIdForReuseSingle = array_values($mainLicenseIdForReuse);
535  $this->clearingDao->makeMainLicense($uploadId=3, $this->groupId, $mainLicenseIdForReuseSingle[0]);
536  $mainLicense=$this->clearingDao->getMainLicenseIds($uploadId=3, $this->groupId);
537  $mainLicenseSingle = array_values($mainLicense);
538  $this->assertEquals($mainLicenseIdForReuseSingle, $mainLicenseSingle);
539  $this->rmRepo();
540  }
541 
549  {
550  // Test array format (multiple selections)
551  $reuseSelections = ['2,1', '4,1'];
552  // Simulate the validation logic from scheduleAgent
553  $createdLinks = 0;
554  foreach ($reuseSelections as $reuseSelection) {
555  if (empty($reuseSelection) || !is_string($reuseSelection)) {
556  $this->fail("Invalid reuse selection found - empty or non-string value");
557  }
558 
559  $reuseUploadPair = explode(',', $reuseSelection, 2);
560  if (count($reuseUploadPair) !== 2) {
561  $this->fail("Invalid reuse selection format: '$reuseSelection' (expected format: 'uploadId,groupId')");
562  }
563 
564  [$reuseUploadId, $reuseGroupId] = $reuseUploadPair;
565  $this->assertIsNumeric($reuseUploadId, "Upload ID should be numeric");
566  $this->assertIsNumeric($reuseGroupId, "Group ID should be numeric");
567  $createdLinks++;
568  }
569 
570  $this->assertEquals(2, $createdLinks, 'Should process 2 reuse selections');
571  }
572 
580  {
581  // Test scalar format (single selection)
582  $reuseSelections = '2,1';
583  // Simulate the validation logic from scheduleAgent
584  if (!is_array($reuseSelections)) {
585  $reuseSelections = [$reuseSelections];
586  }
587 
588  $createdLinks = 0;
589  foreach ($reuseSelections as $reuseSelection) {
590  if (empty($reuseSelection) || !is_string($reuseSelection)) {
591  $this->fail("Invalid reuse selection found - empty or non-string value");
592  }
593 
594  $reuseUploadPair = explode(',', $reuseSelection, 2);
595  if (count($reuseUploadPair) !== 2) {
596  $this->fail("Invalid reuse selection format: '$reuseSelection' (expected format: 'uploadId,groupId')");
597  }
598 
599  [$reuseUploadId, $reuseGroupId] = $reuseUploadPair;
600  $this->assertIsNumeric($reuseUploadId, "Upload ID should be numeric");
601  $this->assertIsNumeric($reuseGroupId, "Group ID should be numeric");
602  $createdLinks++;
603  }
604 
605  $this->assertEquals(1, $createdLinks, 'Should process 1 reuse selection');
606  }
607 
615  {
616  $invalidSelection = "invalid_format";
617  // Simulate the validation logic from scheduleAgent
618  $reuseUploadPair = explode(',', $invalidSelection, 2);
619  $this->expectException(\InvalidArgumentException::class);
620  $this->expectExceptionMessage("Reuser: Invalid reuse selection format: '$invalidSelection' (expected format: 'uploadId,groupId')");
621 
622  if (count($reuseUploadPair) !== 2) {
623  throw new \InvalidArgumentException("Reuser: Invalid reuse selection format: '$invalidSelection' (expected format: 'uploadId,groupId')");
624  }
625  }
626 
634  {
635  $emptySelection = "";
636  $this->expectException(\InvalidArgumentException::class);
637  $this->expectExceptionMessage("Reuser: Invalid reuse selection found - empty or non-string value");
638 
639  if (empty($emptySelection) || !is_string($emptySelection)) {
640  throw new \InvalidArgumentException("Reuser: Invalid reuse selection found - empty or non-string value");
641  }
642  }
643 
651  {
652  $nonStringSelection = 123;
653  $this->expectException(\InvalidArgumentException::class);
654  $this->expectExceptionMessage("Reuser: Invalid reuse selection found - empty or non-string value");
655 
656  if (empty($nonStringSelection) || !is_string($nonStringSelection)) {
657  throw new \InvalidArgumentException("Reuser: Invalid reuse selection found - empty or non-string value");
658  }
659  }
660 
668  {
669  // Test various malformed formats
670  $malformedCases = [
671  '123,', // Missing group ID
672  ',456', // Missing upload ID
673  '123,456,789', // Too many parts
674  '123' // Missing comma separator
675  ];
676 
677  foreach ($malformedCases as $malformedValue) {
678  $reuseUploadPair = explode(',', $malformedValue, 2);
679  if (count($reuseUploadPair) !== 2) {
680  $this->expectException(\InvalidArgumentException::class);
681  $this->expectExceptionMessage("Reuser: Invalid reuse selection format: '$malformedValue' (expected format: 'uploadId,groupId')");
682  throw new \InvalidArgumentException("Reuser: Invalid reuse selection format: '$malformedValue' (expected format: 'uploadId,groupId')");
683  }
684  }
685  }
686 }
Various utility functions to filter ClearingDecision.
UI element for reuser during Uploading new package.
Tests for Reuser agent and scheduler interaction.
testReuserMalformedReuseSelection()
Test malformed reuse selection strings.
testReuserNonStringReuseSelection()
Test non-string reuse selection throws exception.
insertDecisionFromTwoEvents($scope=DecisionScopes::ITEM, $originallyClearedItemId=23)
Creates two clearing decisions.
testReuserRealScanWithALocalClearing()
Call runnerReuserScanWithALocalClearing()
testReuserSingleReuseSelectionValidation()
Test single reuse selection validation logic (backward compatibility)
testReuserRealScanWithoutAnyUploadToCopyAndAClearing()
Call runnerReuserScanWithoutAnyUploadToCopyAndAClearing()
testReuserRealScanWithARepoClearing()
Call runnerReuserScanWithARepoClearing()
testReuserInvalidReuseSelectionFormat()
Test invalid reuse selection format throws exception.
getFilteredClearings($uploadId, $groupId)
Get clearings for a given upload id.
getHeartCount($output)
Get the heart count from agent.
testReuserEmptyReuseSelection()
Test empty reuse selection throws exception.
runnerReuserScanWithoutAnyUploadToCopyAndNoClearing(SchedulerTestRunner $runner)
Test on an upload with no clearing decisions.
setUpTables()
Setup tables required by the agent.
testReuserRealScanWithoutAnyUploadToCopyAndNoClearing()
Call runnerReuserScanWithoutAnyUploadToCopyAndNoClearing()
runnerReuserScanWithoutAnyUploadToCopyAndAClearing($runner)
Run reuser agent with no upload to copy decisions from.
testReuserMultipleReuseSelectionsValidation()
Test multiple reuse selections validation logic.
run($uploadId, $userId=2, $groupId=2, $jobId=1, array $args=[])
Function to run agent from scheduler.
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:16
Namespace to hold test cases for Reuser agent.