FOSSology  4.7.1
Open Source License Compliance by Open Source Software
CustomTextImportTest.php
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2026 Siemens AG
4 
5  SPDX-License-Identifier: GPL-2.0-only
6 */
7 
9 
15 use Mockery as M;
16 
21 class CustomTextImportTest extends \PHPUnit\Framework\TestCase
22 {
24  private $assertCountBefore;
25 
27  private $dbManager;
29  private $userDao;
31  private $licenseDao;
33  private $importer;
35  private $tmpFiles = [];
36 
37  protected function setUp(): void
38  {
39  $this->assertCountBefore = \Hamcrest\MatcherAssert::getCount();
40 
41  // Auth::getUserId() / getGroupId() read these globals.
42  $GLOBALS['SysConf']['auth']['UserId'] = 1;
43  $GLOBALS['SysConf']['auth']['GroupId'] = 1;
44 
45  $this->dbManager = M::mock(DbManager::class);
46  $this->dbManager->shouldReceive('begin')->andReturnNull();
47  $this->dbManager->shouldReceive('commit')->andReturnNull();
48  $this->dbManager->shouldReceive('rollback')->andReturnNull();
49  $this->userDao = M::mock(UserDao::class);
50  $this->licenseDao = M::mock(LicenseDao::class);
51 
52  $this->importer = new CustomTextImport(
53  $this->dbManager,
54  $this->userDao,
55  $this->licenseDao
56  );
57  }
58 
59  protected function tearDown(): void
60  {
61  $this->addToAssertionCount(
62  \Hamcrest\MatcherAssert::getCount() - $this->assertCountBefore
63  );
64  foreach ($this->tmpFiles as $f) {
65  if (file_exists($f)) {
66  unlink($f);
67  }
68  }
69  M::close();
70  }
71 
72  private function makeLicense(int $id): object
73  {
74  $lic = M::mock(License::class);
75  $lic->shouldReceive('getId')->andReturn($id);
76  return $lic;
77  }
78 
79  private function writeTempFile(string $content, string $ext): string
80  {
81  $path = tempnam(sys_get_temp_dir(), 'cti_test_') . '.' . $ext;
82  file_put_contents($path, $content);
83  $this->tmpFiles[] = $path;
84  return $path;
85  }
86 
88  private function markAsCsvSource(): void
89  {
90  $prop = new \ReflectionProperty(CustomTextImport::class, 'unescapeNewlines');
91  $prop->setAccessible(true);
92  $prop->setValue($this->importer, true);
93  }
94 
102  {
103  $row = [
104  'Text' => 'some scanning text',
105  'Is Active' => 'true',
106  'License Shortname' => 'MIT',
107  'Removing' => 'false',
108  'Comment' => 'a comment',
109  'License Text' => 'license text here',
110  'Acknowledgement' => 'ack text',
111  ];
112 
113  $mapped = Reflectory::invokeObjectsMethodnameWith(
114  $this->importer, 'mapHeaders', [$row]
115  );
116 
117  $this->assertSame('some scanning text', $mapped['text']);
118  $this->assertIsArray($mapped['licenses']);
119  $this->assertCount(1, $mapped['licenses']);
120  $this->assertSame('MIT', $mapped['licenses'][0]['shortname']);
121  $this->assertFalse($mapped['licenses'][0]['removing']);
122  $this->assertSame('license text here', $mapped['licenses'][0]['reportinfo']);
123  $this->assertSame('ack text', $mapped['licenses'][0]['acknowledgement']);
124  }
125 
134  {
135  $row = [
136  'Text' => 'phrase', 'License Shortname' => 'MIT', 'Removing' => '1',
137  ];
138 
139  $mapped = Reflectory::invokeObjectsMethodnameWith(
140  $this->importer, 'mapHeaders', [$row]
141  );
142 
143  $this->assertTrue($mapped['licenses'][0]['removing']);
144  }
145 
152  {
153  $licenses = [
154  ['shortname' => 'Apache-2.0', 'removing' => false,
155  'comment' => '', 'reportinfo' => '', 'acknowledgement' => ''],
156  ['shortname' => 'MIT', 'removing' => true,
157  'comment' => '', 'reportinfo' => '', 'acknowledgement' => ''],
158  ];
159  $row = ['text' => 'json phrase', 'licenses' => $licenses];
160 
161  $mapped = Reflectory::invokeObjectsMethodnameWith(
162  $this->importer, 'mapHeaders', [$row]
163  );
164 
165  $this->assertCount(2, $mapped['licenses']);
166  $this->assertSame('Apache-2.0', $mapped['licenses'][0]['shortname']);
167  $this->assertSame('MIT', $mapped['licenses'][1]['shortname']);
168  }
169 
177  {
178  $this->markAsCsvSource();
179  $row = ['text' => "line one\\nline two\\nline three"];
180 
181  $mapped = Reflectory::invokeObjectsMethodnameWith(
182  $this->importer, 'mapHeaders', [$row]
183  );
184 
185  $this->assertSame("line one\nline two\nline three", $mapped['text']);
186  }
187 
198  {
199  $row = ['text' => "printf(\"hi\\\\n\");"]; // literal: printf("hi\n");
200 
201  $mapped = Reflectory::invokeObjectsMethodnameWith(
202  $this->importer, 'mapHeaders', [$row]
203  );
204 
205  $this->assertSame("printf(\"hi\\\\n\");", $mapped['text']);
206  }
207 
215  {
216  $this->markAsCsvSource();
217  $row = [
218  'text' => 'phrase',
219  'licenses' => [[
220  'shortname' => 'MIT',
221  'removing' => false,
222  'comment' => "line1\\nline2",
223  'reportinfo' => "a\\nb",
224  'acknowledgement' => "x\\ny",
225  ]],
226  ];
227 
228  $mapped = Reflectory::invokeObjectsMethodnameWith(
229  $this->importer, 'mapHeaders', [$row]
230  );
231 
232  $this->assertSame("line1\nline2", $mapped['licenses'][0]['comment']);
233  $this->assertSame("a\nb", $mapped['licenses'][0]['reportinfo']);
234  $this->assertSame("x\ny", $mapped['licenses'][0]['acknowledgement']);
235  }
236 
244  public function testParseBoolean($input, bool $expected): void
245  {
246  $result = Reflectory::invokeObjectsMethodnameWith(
247  $this->importer, 'parseBoolean', [$input]
248  );
249  $this->assertSame($expected, $result);
250  }
251 
252  public static function parseBooleanProvider(): array
253  {
254  return [
255  ['true', true],
256  ['TRUE', true],
257  ['1', true],
258  ['yes', true],
259  ['on', true],
260  ['active', true],
261  ['false', false],
262  ['0', false],
263  ['no', false],
264  ['off', false],
265  ['', false],
266  [true, true],
267  [false, false],
268  ];
269  }
270 
279  {
280  $this->licenseDao->shouldReceive('getLicenseByShortName')
281  ->with('CAND-1.0', 7)
282  ->once()
283  ->andReturn($this->makeLicense(50));
284 
285  $this->dbManager->shouldReceive('getSingleRow')->andReturn(null);
286  $this->dbManager->shouldReceive('insertTableRow')->once();
287 
288  $result = Reflectory::invokeObjectsMethodnameWith(
289  $this->importer,
290  'associateLicensesWithMetadata',
291  [1, [['shortname' => 'CAND-1.0', 'removing' => false,
292  'comment' => '', 'reportinfo' => '', 'acknowledgement' => '']], 7]
293  );
294 
295  $this->assertSame(1, $result['inserted']);
296  }
297 
307  {
308  $seenNames = [];
309  $this->dbManager->shouldReceive('getSingleRow')
310  ->withArgs(function ($sql, $params, $name) use (&$seenNames) {
311  $seenNames[] = $name;
312  return true;
313  })
314  ->andReturn(null);
315  $this->dbManager->shouldReceive('insertTableRow')->twice();
316 
317  $this->licenseDao->shouldReceive('getLicenseByShortName')
318  ->with('MIT', null)->andReturn($this->makeLicense(1));
319  $this->licenseDao->shouldReceive('getLicenseByShortName')
320  ->with('Apache-2.0', null)->andReturn($this->makeLicense(2));
321 
322  Reflectory::invokeObjectsMethodnameWith(
323  $this->importer,
324  'associateLicensesWithMetadata',
325  [9, [
326  ['shortname' => 'MIT', 'removing' => false, 'comment' => '', 'reportinfo' => '', 'acknowledgement' => ''],
327  ['shortname' => 'Apache-2.0', 'removing' => false, 'comment' => '', 'reportinfo' => '', 'acknowledgement' => ''],
328  ]]
329  );
330 
331  $this->assertCount(1, array_unique($seenNames));
332  }
333 
342  {
343  $this->dbManager->shouldReceive('getSingleRow')->andReturn(['exists' => 1]);
344  $this->dbManager->shouldNotReceive('insertTableRow');
345 
346  $this->licenseDao->shouldReceive('getLicenseByShortName')
347  ->with('MIT', null)->andReturn($this->makeLicense(1));
348 
349  $result = Reflectory::invokeObjectsMethodnameWith(
350  $this->importer,
351  'associateLicensesWithMetadata',
352  [1, [['shortname' => 'MIT', 'removing' => false, 'comment' => '', 'reportinfo' => '', 'acknowledgement' => '']]]
353  );
354 
355  $this->assertSame(0, $result['inserted']);
356  $this->assertSame(1, $result['skipped']);
357  }
358 
365  {
366  $this->dbManager->shouldReceive('getSingleRow')->andReturn(null);
367  $this->dbManager->shouldReceive('insertTableRow')
368  ->once()
369  ->with('custom_phrase_license_map', M::on(function ($row) {
370  return $row['comment'] === null && $row['reportinfo'] === null && $row['acknowledgement'] === null;
371  }));
372 
373  $this->licenseDao->shouldReceive('getLicenseByShortName')
374  ->with('MIT', null)->andReturn($this->makeLicense(1));
375 
376  $result = Reflectory::invokeObjectsMethodnameWith(
377  $this->importer,
378  'associateLicensesWithMetadata',
379  [1, [['shortname' => 'MIT']]] // no removing/comment/reportinfo/acknowledgement keys
380  );
381 
382  $this->assertSame(1, $result['inserted']);
383  }
384 
390  {
391  $this->licenseDao->shouldReceive('getLicenseByShortName')
392  ->with('GPL-2.0-only', null)->once()->andReturn($this->makeLicense(7));
393  $this->licenseDao->shouldNotReceive('insertLicense');
394 
395  $this->dbManager->shouldReceive('getSingleRow')->andReturn(null);
396  $this->dbManager->shouldReceive('insertTableRow')->once();
397 
398  $result = Reflectory::invokeObjectsMethodnameWith(
399  $this->importer, 'associateLicenseNames', [42, 'GPL-2.0-only', false]
400  );
401 
402  $this->assertSame(1, $result['inserted']);
403  $this->assertEmpty($result['failed']);
404  }
405 
411  {
412  $this->licenseDao->shouldReceive('getLicenseByShortName')
413  ->with('MIT', null)->once()->andReturn($this->makeLicense(1));
414  $this->licenseDao->shouldReceive('getLicenseByShortName')
415  ->with('Apache-2.0', null)->once()->andReturn($this->makeLicense(2));
416 
417  $this->dbManager->shouldReceive('getSingleRow')->andReturn(null);
418  $this->dbManager->shouldReceive('insertTableRow')->twice();
419 
420  $result = Reflectory::invokeObjectsMethodnameWith(
421  $this->importer, 'associateLicenseNames', [46, 'MIT, Apache-2.0', false]
422  );
423 
424  $this->assertSame(2, $result['inserted']);
425  }
426 
432  {
433  $this->licenseDao->shouldReceive('getLicenseByShortName')
434  ->with('NoSuchLicense', null)->once()->andReturn(null);
435 
436  $result = Reflectory::invokeObjectsMethodnameWith(
437  $this->importer, 'associateLicenseNames', [1, 'NoSuchLicense', false]
438  );
439 
440  $this->assertSame(0, $result['inserted']);
441  $this->assertStringContainsString('unknown', $result['failed'][0]);
442  }
443 
450  {
451  $this->dbManager->shouldNotReceive('getSingleRow');
452  $this->dbManager->shouldNotReceive('insertPreparedAndReturn');
453 
454  $result = Reflectory::invokeObjectsMethodnameWith(
455  $this->importer, 'importSinglePhrase', [['text' => '']]
456  );
457 
458  $this->assertFalse($result['success']);
459  $this->assertStringContainsStringIgnoringCase('required', $result['message']);
460  }
461 
470  {
471  $this->dbManager->shouldNotReceive('getSingleRow');
472  $this->dbManager->shouldNotReceive('begin');
473 
474  $result = Reflectory::invokeObjectsMethodnameWith(
475  $this->importer, 'importSinglePhrase', [['text' => ['not', 'a', 'string']]]
476  );
477 
478  $this->assertFalse($result['success']);
479  $this->assertStringContainsStringIgnoringCase('required', $result['message']);
480  }
481 
489  {
490  $this->dbManager->shouldReceive('getSingleRow')
491  ->with(M::pattern('/INSERT INTO custom_phrase/'), M::any(), M::any())
492  ->once()->andReturn(['cp_pk' => 11]);
493  $this->dbManager->shouldNotReceive('insertTableRow');
494  $this->licenseDao->shouldNotReceive('getLicenseByShortName');
495 
496  $result = Reflectory::invokeObjectsMethodnameWith(
497  $this->importer,
498  'importSinglePhrase',
499  [[
500  'text' => 'phrase with a malformed license entry',
501  'licenses' => [['shortname' => ['nested', 'array'], 'removing' => false]]
502  ]]
503  );
504 
505  $this->assertTrue($result['success']);
506  }
507 
514  {
515  $this->dbManager->shouldReceive('getSingleRow')
516  ->with(M::pattern('/INSERT INTO custom_phrase/'), M::any(), M::any())
517  ->once()
518  ->withArgs(function ($sql, $params) {
519  return $params[4] === 'false';
520  })
521  ->andReturn(['cp_pk' => 12]);
522 
523  $result = Reflectory::invokeObjectsMethodnameWith(
524  $this->importer, 'importSinglePhrase', [['text' => 'malformed is_active', 'is_active' => ['x']]]
525  );
526 
527  $this->assertTrue($result['success']);
528  }
529 
537  {
538  $insertAttempt = 0;
539  $this->dbManager->shouldReceive('getSingleRow')
540  ->andReturnUsing(function ($sql) use (&$insertAttempt) {
541  if (strpos($sql, 'INSERT INTO custom_phrase') !== false) {
542  $insertAttempt++;
543  return ['cp_pk' => $insertAttempt];
544  }
545  return null;
546  });
547 
548  $data = [
549  ['text' => 'row one valid'],
550  ['text' => ['row', 'two', 'malformed']],
551  ['text' => 'row three valid'],
552  ];
553 
554  $msg = '';
555  $result = $this->importer->importJsonData($data, $msg);
556 
557  $this->assertStringContainsString('2 phrase(s) created', $result);
558  $this->assertStringContainsString('Row 2', $result);
559  }
560 
567  {
568  $this->dbManager->shouldReceive('getSingleRow')
569  ->with(M::pattern('/INSERT INTO custom_phrase/'), M::any(), M::any())
570  ->once()->andReturn(['cp_pk' => 10]);
571 
572  $result = Reflectory::invokeObjectsMethodnameWith(
573  $this->importer, 'importSinglePhrase', [['text' => 'brand new unique phrase']]
574  );
575 
576  $this->assertTrue($result['success']);
577  $this->assertEmpty($result['existing'] ?? '');
578  $this->assertEmpty($result['unchanged'] ?? '');
579  }
580 
588  {
589  $this->dbManager->shouldReceive('getSingleRow')
590  ->andReturnUsing(function ($sql) {
591  if (strpos($sql, 'INSERT INTO custom_phrase') !== false) {
592  return false; // ON CONFLICT: text already exists
593  }
594  if (strpos($sql, 'text_md5') !== false) {
595  return ['cp_pk' => 7]; // find-existing fallback
596  }
597  return null; // license map check - not yet associated
598  });
599  $this->dbManager->shouldReceive('insertTableRow')->once();
600  $this->licenseDao->shouldReceive('getLicenseByShortName')
601  ->with('Apache-2.0', 1)->once()->andReturn($this->makeLicense(20));
602 
603  $result = Reflectory::invokeObjectsMethodnameWith(
604  $this->importer,
605  'importSinglePhrase',
606  [[
607  'text' => 'existing phrase',
608  'licenses' => [['shortname' => 'Apache-2.0', 'removing' => false,
609  'comment' => '', 'reportinfo' => '', 'acknowledgement' => '']]
610  ]]
611  );
612 
613  $this->assertTrue($result['success']);
614  $this->assertTrue($result['existing']);
615  }
616 
625  {
626  $this->dbManager->shouldReceive('getSingleRow')
627  ->andReturnUsing(function ($sql) {
628  if (strpos($sql, 'INSERT INTO custom_phrase') !== false) {
629  return false; // ON CONFLICT: text already exists
630  }
631  if (strpos($sql, 'text_md5') !== false) {
632  return ['cp_pk' => 7]; // find-existing fallback
633  }
634  return ['exists' => 1]; // license already mapped
635  });
636  $this->dbManager->shouldNotReceive('insertTableRow');
637  $this->licenseDao->shouldReceive('getLicenseByShortName')
638  ->with('MIT', 1)->once()->andReturn($this->makeLicense(5));
639 
640  $result = Reflectory::invokeObjectsMethodnameWith(
641  $this->importer,
642  'importSinglePhrase',
643  [[
644  'text' => 'existing phrase',
645  'licenses' => [['shortname' => 'MIT', 'removing' => false,
646  'comment' => '', 'reportinfo' => '', 'acknowledgement' => '']]
647  ]]
648  );
649 
650  $this->assertTrue($result['success']);
651  $this->assertTrue($result['unchanged']);
652  $this->assertArrayNotHasKey('existing', $result);
653  }
654 
661  {
662  $this->dbManager->shouldReceive('getSingleRow')
663  ->andReturnUsing(function ($sql) {
664  if (strpos($sql, 'INSERT INTO custom_phrase') !== false) {
665  return false; // ON CONFLICT: text already exists
666  }
667  return ['cp_pk' => 3]; // find-existing fallback
668  });
669 
670  $result = Reflectory::invokeObjectsMethodnameWith(
671  $this->importer, 'importSinglePhrase', [['text' => 'existing phrase, no new license']]
672  );
673 
674  $this->assertTrue($result['success']);
675  $this->assertTrue($result['unchanged']);
676  }
677 
684  {
685  $this->dbManager->shouldReceive('getSingleRow')
686  ->andReturnUsing(function ($sql) {
687  if (strpos($sql, 'INSERT INTO custom_phrase') !== false) {
688  return false; // ON CONFLICT: text already exists
689  }
690  return ['cp_pk' => 3]; // find-existing fallback
691  });
692  $this->licenseDao->shouldReceive('getLicenseByShortName')
693  ->with('NoSuchLicense', 1)->once()->andReturn(null);
694 
695  $result = Reflectory::invokeObjectsMethodnameWith(
696  $this->importer,
697  'importSinglePhrase',
698  [[
699  'text' => 'existing phrase',
700  'licenses' => [['shortname' => 'NoSuchLicense', 'removing' => false,
701  'comment' => '', 'reportinfo' => '', 'acknowledgement' => '']]
702  ]]
703  );
704 
705  $this->assertFalse($result['success']);
706  $this->assertStringContainsString('NoSuchLicense', $result['message']);
707  }
708 
717  {
718  $insertAttempt = 0;
719  $this->dbManager->shouldReceive('getSingleRow')
720  ->andReturnUsing(function ($sql) use (&$insertAttempt) {
721  if (strpos($sql, 'INSERT INTO custom_phrase') !== false) {
722  $insertAttempt++;
723  return $insertAttempt === 1 ? ['cp_pk' => 1] : false;
724  }
725  if (strpos($sql, 'text_md5') !== false) {
726  return ['cp_pk' => 1]; // find-existing fallback for row 2
727  }
728  return null; // license map checks - nothing mapped yet
729  });
730  $this->dbManager->shouldReceive('insertTableRow')->twice();
731 
732  $this->licenseDao->shouldReceive('getLicenseByShortName')
733  ->with('MIT', 1)->andReturn($this->makeLicense(5));
734  $this->licenseDao->shouldReceive('getLicenseByShortName')
735  ->with('Apache-2.0', 1)->andReturn($this->makeLicense(6));
736 
737  $data = [
738  ['text' => 'shared phrase', 'licenses' =>
739  [['shortname' => 'MIT', 'removing' => false,
740  'comment' => '', 'reportinfo' => '', 'acknowledgement' => '']]],
741  ['text' => 'shared phrase', 'licenses' =>
742  [['shortname' => 'Apache-2.0', 'removing' => false,
743  'comment' => '', 'reportinfo' => '', 'acknowledgement' => '']]],
744  ];
745 
746  $msg = '';
747  $result = $this->importer->importJsonData($data, $msg);
748 
749  $this->assertStringContainsString('1 phrase(s) created', $result);
750  $this->assertStringContainsString('1 license(s) added to existing phrase(s)', $result);
751  }
752 
761  {
762  $this->dbManager->shouldReceive('getSingleRow')
763  ->andReturnUsing(function ($sql) {
764  if (strpos($sql, 'INSERT INTO custom_phrase') !== false) {
765  return false; // ON CONFLICT: text already exists
766  }
767  return ['cp_pk' => 5]; // find-existing fallback
768  });
769 
770  $data = [['text' => 'already exists, no license']];
771  $msg = '';
772  $result = $this->importer->importJsonData($data, $msg);
773 
774  $this->assertStringContainsString('1 row(s) already up to date', $result);
775  $this->assertStringNotContainsString('Errors:', $result);
776  }
777 
784  {
785  $msg = '';
786  $result = $this->importer->importJsonData([], $msg);
787 
788  $this->assertStringContainsString('nothing new to import', $result);
789  }
790 
797  {
798  $this->dbManager->shouldReceive('getSingleRow')
799  ->with(M::pattern('/INSERT INTO custom_phrase/'), M::any(), M::any())
800  ->once()->andReturn(['cp_pk' => 2]);
801 
802  $data = [
803  ['text' => 'valid phrase'],
804  ['text' => ''],
805  ];
806 
807  $msg = '';
808  $result = $this->importer->importJsonData($data, $msg);
809 
810  $this->assertStringContainsString('1 phrase(s) created', $result);
811  $this->assertStringContainsString('Row 2', $result);
812  }
813 
821  {
822  // First import: phrase does not exist yet.
823  $this->dbManager->shouldReceive('getSingleRow')
824  ->with(M::pattern('/INSERT INTO custom_phrase/'), M::any(), M::any())
825  ->once()
826  ->andReturn(['cp_pk' => 1]);
827  $this->dbManager->shouldReceive('getSingleRow')
828  ->with(M::pattern('/custom_phrase_license_map/'), M::any(), M::any())
829  ->once()
830  ->andReturn(null);
831  $this->dbManager->shouldReceive('insertTableRow')->once();
832  $this->licenseDao->shouldReceive('getLicenseByShortName')
833  ->with('MIT', 1)->twice()->andReturn($this->makeLicense(1));
834 
835  $data = [['text' => 'idempotent phrase', 'licenses' =>
836  [['shortname' => 'MIT', 'removing' => false, 'comment' => '', 'reportinfo' => '', 'acknowledgement' => '']]]];
837 
838  $msg1 = '';
839  $result1 = $this->importer->importJsonData($data, $msg1);
840  $this->assertStringContainsString('1 phrase(s) created', $result1);
841 
842  // Second import: phrase exists now, and MIT is already mapped.
843  $this->dbManager->shouldReceive('getSingleRow')
844  ->with(M::pattern('/INSERT INTO custom_phrase/'), M::any(), M::any())
845  ->once()
846  ->andReturn(false);
847  $this->dbManager->shouldReceive('getSingleRow')
848  ->with(M::pattern('/^SELECT cp_pk FROM custom_phrase WHERE text_md5/'), M::any(), M::any())
849  ->once()
850  ->andReturn(['cp_pk' => 1]);
851  $this->dbManager->shouldReceive('getSingleRow')
852  ->with(M::pattern('/custom_phrase_license_map/'), M::any(), M::any())
853  ->once()
854  ->andReturn(['exists' => 1]);
855 
856  $msg2 = '';
857  $result2 = $this->importer->importJsonData($data, $msg2);
858 
859  $this->assertStringContainsString('1 row(s) already up to date', $result2);
860  $this->assertStringNotContainsString('phrase(s) created', $result2);
861  $this->assertStringNotContainsString('license(s) added', $result2);
862  }
863 
869  public function testHandleCsvFileCreatesSinglePhrase(): void
870  {
871  $bom = chr(0xEF) . chr(0xBB) . chr(0xBF);
872  $csv = $bom . "Text,Is Active,License Shortname,Removing,Comment,License Text,Acknowledgement\n";
873  $csv .= '"simple scanning phrase",true,MIT,false,,,';
874 
875  $path = $this->writeTempFile($csv, 'csv');
876 
877  $this->dbManager->shouldReceive('getSingleRow')
878  ->with(M::pattern('/INSERT INTO custom_phrase/'), M::any(), M::any())
879  ->once()->andReturn(['cp_pk' => 1]);
880  $this->dbManager->shouldReceive('getSingleRow')
881  ->with(M::pattern('/custom_phrase_license_map/'), M::any(), M::any())
882  ->once()->andReturn(null);
883  $this->dbManager->shouldReceive('insertTableRow')->once();
884 
885  $this->licenseDao->shouldReceive('getLicenseByShortName')
886  ->with('MIT', 1)->once()->andReturn($this->makeLicense(3));
887 
888  $result = $this->importer->handleFile($path, 'csv');
889 
890  $this->assertStringContainsString('1 phrase(s) created', $result);
891  }
892 
902  {
903  $text = "Licensed under any of the following licenses.";
904  $csv = "Text,Is Active,License Shortname,Removing,Comment,License Text,Acknowledgement\n";
905  $csv .= "\"$text\",true,GPL-2.0-or-later,false,,,\n";
906  $csv .= "\"$text\",true,LGPL-2.1-or-later,true,,,\n";
907  $csv .= "\"$text\",true,MPL-1.1+,true,,,\n";
908 
909  $path = $this->writeTempFile($csv, 'csv');
910 
911  $insertAttempt = 0;
912  $this->dbManager->shouldReceive('getSingleRow')
913  ->andReturnUsing(function ($sql) use (&$insertAttempt) {
914  if (strpos($sql, 'INSERT INTO custom_phrase') !== false) {
915  $insertAttempt++;
916  return $insertAttempt === 1 ? ['cp_pk' => 99] : false;
917  }
918  if (strpos($sql, 'text_md5') !== false) {
919  return ['cp_pk' => 99]; // find-existing fallback for rows 2 and 3
920  }
921  return null; // license map checks - nothing mapped yet
922  });
923  $this->dbManager->shouldReceive('insertTableRow')->times(3);
924 
925  $this->licenseDao->shouldReceive('getLicenseByShortName')
926  ->with('GPL-2.0-or-later', 1)->andReturn($this->makeLicense(10));
927  $this->licenseDao->shouldReceive('getLicenseByShortName')
928  ->with('LGPL-2.1-or-later', 1)->andReturn($this->makeLicense(11));
929  $this->licenseDao->shouldReceive('getLicenseByShortName')
930  ->with('MPL-1.1+', 1)->andReturn($this->makeLicense(12));
931 
932  $result = $this->importer->handleFile($path, 'csv');
933 
934  $this->assertStringContainsString('1 phrase(s) created', $result);
935  $this->assertStringContainsString('2 license(s) added to existing phrase(s)', $result);
936  }
937 
944  {
945  $csv = "Text,Is Active,License Shortname\n";
946  $csv .= "only two columns,true\n";
947 
948  $path = $this->writeTempFile($csv, 'csv');
949 
950  $this->dbManager->shouldNotReceive('insertPreparedAndReturn');
951 
952  $result = $this->importer->handleFile($path, 'csv');
953 
954  $this->assertStringContainsStringIgnoringCase('column', $result);
955  }
956 
965  {
966  $csv = "Text,Is Active,License Shortname,Removing,Comment,License Text,Acknowledgement\n";
967  $csv .= "\"line one\\nline two\",true,MIT,false,,,\n";
968 
969  $path = $this->writeTempFile($csv, 'csv');
970 
971  $capturedText = null;
972  $this->dbManager->shouldReceive('getSingleRow')
973  ->andReturnUsing(function ($sql, $params) use (&$capturedText) {
974  if (strpos($sql, 'INSERT INTO custom_phrase') !== false) {
975  $capturedText = $params[0];
976  return ['cp_pk' => 1];
977  }
978  return null; // license map check - not yet associated
979  });
980  $this->dbManager->shouldReceive('insertTableRow')->once();
981  $this->licenseDao->shouldReceive('getLicenseByShortName')
982  ->with('MIT', 1)->once()->andReturn($this->makeLicense(1));
983 
984  $this->importer->handleFile($path, 'csv');
985 
986  $this->assertSame("line one\nline two", $capturedText);
987  }
988 
994  public function testHandleJsonFileCreatesTwoPhrases(): void
995  {
996  $data = [
997  ['text' => 'first phrase', 'is_active' => true, 'licenses' =>
998  [['shortname' => 'MIT', 'removing' => false,
999  'comment' => '', 'reportinfo' => '', 'acknowledgement' => '']]],
1000  ['text' => 'second phrase', 'is_active' => true, 'licenses' => []],
1001  ];
1002 
1003  $path = $this->writeTempFile(json_encode($data), 'json');
1004 
1005  $insertAttempt = 0;
1006  $this->dbManager->shouldReceive('getSingleRow')
1007  ->andReturnUsing(function ($sql) use (&$insertAttempt) {
1008  if (strpos($sql, 'INSERT INTO custom_phrase') !== false) {
1009  $insertAttempt++;
1010  return ['cp_pk' => $insertAttempt];
1011  }
1012  return null; // license map check - not yet associated
1013  });
1014  $this->dbManager->shouldReceive('insertTableRow')->once();
1015 
1016  $this->licenseDao->shouldReceive('getLicenseByShortName')
1017  ->with('MIT', 1)->once()->andReturn($this->makeLicense(7));
1018 
1019  $result = $this->importer->handleFile($path, 'json');
1020 
1021  $this->assertStringContainsString('2 phrase(s) created', $result);
1022  }
1023 
1030  {
1031  $path = $this->writeTempFile('{not valid json[', 'json');
1032 
1033  $result = $this->importer->handleFile($path, 'json');
1034 
1035  $this->assertStringContainsStringIgnoringCase('invalid json', $result);
1036  }
1037 
1044  {
1045  $path = $this->writeTempFile('"just a string"', 'json');
1046 
1047  $result = $this->importer->handleFile($path, 'json');
1048 
1049  $this->assertStringContainsStringIgnoringCase('array', $result);
1050  }
1051 
1059  {
1060  $data = [
1061  ['text' => 'repeat phrase', 'licenses' =>
1062  [['shortname' => 'MIT', 'removing' => false,
1063  'comment' => '', 'reportinfo' => '', 'acknowledgement' => '']]],
1064  ['text' => 'repeat phrase', 'licenses' =>
1065  [['shortname' => 'Apache-2.0', 'removing' => false,
1066  'comment' => '', 'reportinfo' => '', 'acknowledgement' => '']]],
1067  ];
1068 
1069  $path = $this->writeTempFile(json_encode($data), 'json');
1070 
1071  $insertAttempt = 0;
1072  $this->dbManager->shouldReceive('getSingleRow')
1073  ->andReturnUsing(function ($sql) use (&$insertAttempt) {
1074  if (strpos($sql, 'INSERT INTO custom_phrase') !== false) {
1075  $insertAttempt++;
1076  return $insertAttempt === 1 ? ['cp_pk' => 20] : false;
1077  }
1078  if (strpos($sql, 'text_md5') !== false) {
1079  return ['cp_pk' => 20]; // find-existing fallback for row 2
1080  }
1081  return null;
1082  });
1083  $this->dbManager->shouldReceive('insertTableRow')->twice();
1084 
1085  $this->licenseDao->shouldReceive('getLicenseByShortName')
1086  ->with('MIT', 1)->andReturn($this->makeLicense(1));
1087  $this->licenseDao->shouldReceive('getLicenseByShortName')
1088  ->with('Apache-2.0', 1)->andReturn($this->makeLicense(2));
1089 
1090  $result = $this->importer->handleFile($path, 'json');
1091 
1092  $this->assertStringContainsString('1 phrase(s) created', $result);
1093  $this->assertStringContainsString('1 license(s) added to existing phrase(s)', $result);
1094  }
1095 }
Import custom text phrases from CSV/JSON.
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:16
Utility functions for specific applications.