@@ -22,6 +22,7 @@ package org.ossreviewtoolkit.plugins.scanners.fossid
2222import io.kotest.core.spec.style.WordSpec
2323import io.kotest.inspectors.forAtLeastOne
2424import io.kotest.matchers.collections.beEmpty
25+ import io.kotest.matchers.collections.shouldBeSingleton
2526import io.kotest.matchers.collections.shouldHaveSize
2627import io.kotest.matchers.should
2728import io.kotest.matchers.shouldBe
@@ -678,6 +679,48 @@ class FossIdSnippetChoiceTest : WordSpec({
678679 summary.snippetFindings should beEmpty()
679680 }
680681
682+ " add the license of already marked file with a snippet choice to the license findings (scan in archive mode)" {
683+ val projectCode = PROJECT
684+ val scanCode = scanCode(PROJECT , null)
685+ val config = createConfig(deltaScans = false, fetchSnippetMatchedLines = true, isArchiveMode = true)
686+ val vcsInfo = createVcsInfo()
687+ val scan = createScan(vcsInfo.url, "${vcsInfo.revision}_other ", scanCode)
688+ val pkgId = createIdentifier(index = 42)
689+
690+ val choiceLocation = TextLocation (FILE_1 , 10, 20)
691+ val payload = OrtCommentPayload (mapOf("MIT " to listOf(choiceLocation)), 1, 0)
692+ val comment = jsonMapper.writeValueAsString(mapOf(ORT_NAME to payload))
693+ val markedAsIdentifiedFile = createMarkAsIdentifiedFile("MIT ", FILE_1_ARCHIVE_MODE , comment)
694+ FossIdRestService .create(config.serverUrl)
695+ .expectProjectRequest(projectCode)
696+ .expectListScans(projectCode, listOf(scan))
697+ .expectCheckScanStatus(scanCode, ScanStatus .FINISHED )
698+ .expectCreateScan(projectCode, scanCode, vcsInfo, "", isArchiveMode = true)
699+ .expectRemoveUploadedContent(scanCode)
700+ .expectUploadFile(scanCode)
701+ .expectExtractArchives(scanCode)
702+ .mockFiles(
703+ scanCode,
704+ markedFiles = listOf(markedAsIdentifiedFile)
705+ )
706+
707+ val snippetChoices = createSnippetChoices(
708+ vcsInfo.url,
709+ createSnippetChoice(choiceLocation, PURL_1 , "")
710+ )
711+ val fossId = createFossId(config)
712+
713+ val summary = fossId.scan(createPackage(pkgId, vcsInfo), snippetChoices = snippetChoices).summary
714+
715+ summary.licenseFindings.shouldBeSingleton {
716+ it.license shouldBe " MIT" .toSpdx()
717+ it.location shouldBe choiceLocation
718+ }
719+
720+ summary.issues.filter { it.severity > Severity .HINT } should beEmpty()
721+ summary.snippetFindings should beEmpty()
722+ }
723+
681724 " add the license of marked as identified files that have been manually marked in the UI (legacy behavior)" {
682725 val projectCode = PROJECT
683726 val scanCode = scanCode(PROJECT , null)
@@ -775,6 +818,72 @@ class FossIdSnippetChoiceTest : WordSpec({
775818 }
776819 }
777820
821+ " put a marked as identified file back to pending if it has no snippet choice (scan in archive mode)" {
822+ val projectCode = PROJECT
823+ val scanCode = scanCode(PROJECT , null)
824+ val config = createConfig(deltaScans = false, fetchSnippetMatchedLines = true, isArchiveMode = true)
825+ val vcsInfo = createVcsInfo()
826+ val scan = createScan(vcsInfo.url, "${vcsInfo.revision}_other ", scanCode)
827+ val pkgId = createIdentifier(index = 42)
828+
829+ val choiceLocation = TextLocation (FILE_1 , 10, 20)
830+ val payload = OrtCommentPayload (mapOf("MIT " to listOf(choiceLocation)), 1, 0)
831+ val comment = jsonMapper.writeValueAsString(mapOf(ORT_NAME to payload))
832+ val markedAsIdentifiedFile = createMarkAsIdentifiedFile("MIT ", FILE_1_ARCHIVE_MODE , comment)
833+ val service = FossIdRestService .create(config.serverUrl)
834+ .expectProjectRequest(projectCode)
835+ .expectListScans(projectCode, listOf(scan))
836+ .expectCheckScanStatus(scanCode, ScanStatus .FINISHED )
837+ .expectCreateScan(projectCode, scanCode, vcsInfo, "", isArchiveMode = true)
838+ .expectRemoveUploadedContent(scanCode)
839+ .expectUploadFile(scanCode)
840+ .expectExtractArchives(scanCode)
841+ .mockFiles(
842+ scanCode,
843+ markedFiles = listOf(markedAsIdentifiedFile),
844+ snippets = listOf(
845+ createSnippet(0, FILE_1 , PURL_1 ),
846+ createSnippet(1, FILE_1 , PURL_2 ),
847+ createSnippet(2, FILE_1 , PURL_3 )
848+ ),
849+ matchedLines = mapOf(
850+ 0 to MatchedLines .create((10..20).toList(), (10..20).toList()),
851+ 1 to MatchedLines .create((10..20).toList(), (10..20).toList()),
852+ 2 to MatchedLines .create((20..30).toList(), (20..30).toList())
853+ )
854+ )
855+ // The unmark as identified call is made on the real snippet path.
856+ .expectUnmarkAsIdentified(scanCode, FILE_1_ARCHIVE_MODE )
857+
858+ val fossId = createFossId(config)
859+
860+ val summary = fossId.scan(createPackage(pkgId, vcsInfo), snippetChoices = emptyList()).summary
861+
862+ summary.issues.forAtLeastOne {
863+ it.message shouldBe " This scan has 1 file(s) pending identification in FossID. " +
864+ " Please review and resolve them at: https://www.example.org/fossid/index.html?action=scanview&sid=1"
865+ }
866+
867+ summary.issues.filter { it.severity > Severity .HINT } should beEmpty()
868+ summary.snippetFindings shouldHaveSize 2
869+ summary.snippetFindings.first().apply {
870+ sourceLocation.path shouldBe FILE_1
871+ snippets.map { it.purl } shouldBe listOf(PURL_1 , PURL_2 )
872+ }
873+
874+ summary.snippetFindings.last().apply {
875+ sourceLocation.path shouldBe FILE_1
876+ snippets.map { it.purl } shouldBe listOf(PURL_3 )
877+ }
878+
879+ coVerify {
880+ service.removeUploadedContent(USER , API_KEY , scanCode)
881+ service.uploadFile(USER , API_KEY , scanCode, any())
882+ service.extractArchives(USER , API_KEY , scanCode, any())
883+ service.unmarkAsIdentified(USER , API_KEY , scanCode, FILE_1_ARCHIVE_MODE , any())
884+ }
885+ }
886+
778887 " put a marked as identified file back to pending if some of its snippet choices have been deleted" {
779888 val projectCode = PROJECT
780889 val scanCode = scanCode(PROJECT , null)
0 commit comments