Skip to content

Commit

Permalink
fix: moved automatic download check up, fixed tests
Browse files Browse the repository at this point in the history
- automatic downloads setting is now checked centrally
- downloads should be done via the task service - SnykCliAuthenticationService now uses the task service, too
- fixed a potential NPE in SnykTaskQueueService
- removed superfluous test
- adjusted mocking
  • Loading branch information
bastiandoetsch committed Jan 31, 2024
1 parent af6f866 commit fef3ebc
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import com.intellij.util.PlatformIcons
import io.snyk.plugin.cli.ConsoleCommandRunner
import io.snyk.plugin.getCliFile
import io.snyk.plugin.getPluginPath
import io.snyk.plugin.getSnykCliDownloaderService
import io.snyk.plugin.getSnykTaskQueueService
import io.snyk.plugin.pluginSettings
import io.snyk.plugin.ui.SnykBalloonNotificationHelper
import io.snyk.plugin.ui.getReadOnlyClickableHtmlJEditorPane
Expand Down Expand Up @@ -55,7 +55,7 @@ class SnykCliAuthenticationService(val project: Project) {
val downloadCliTask: () -> Unit = {
if (!getCliFile().exists()) {
val currentIndicator = ProgressManager.getInstance().progressIndicator
getSnykCliDownloaderService().downloadLatestRelease(currentIndicator, project)
getSnykTaskQueueService(project)?.downloadLatestRelease(currentIndicator)
} else {
logger.debug("Skip CLI download, since it was already downloaded")
}
Expand Down
22 changes: 18 additions & 4 deletions src/main/kotlin/io/snyk/plugin/services/SnykTaskQueueService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import io.snyk.plugin.isSnykCodeRunning
import io.snyk.plugin.net.ClientException
import io.snyk.plugin.pluginSettings
import io.snyk.plugin.snykcode.core.RunUtils
import io.snyk.plugin.ui.SnykBalloonNotificationHelper
import io.snyk.plugin.ui.SnykBalloonNotifications
import org.jetbrains.annotations.TestOnly
import snyk.common.SnykError
Expand Down Expand Up @@ -86,6 +87,7 @@ class SnykTaskQueueService(val project: Project) {
fun scan() {
taskQueue.run(object : Task.Backgroundable(project, "Snyk: initializing...", true) {
override fun run(indicator: ProgressIndicator) {
// FIXME: this should be using content roots instead of basePath

Check warning

Code scanning / detekt

Flags a forbidden comment. Warning

Forbidden FIXME todo marker in comment, please fix the problem.
project.basePath?.let {
if (!confirmScanningAndSetWorkspaceTrustedStateIfNeeded(project, Paths.get(it))) return
}
Expand Down Expand Up @@ -115,7 +117,7 @@ class SnykTaskQueueService(val project: Project) {

fun waitUntilCliDownloadedIfNeeded(indicator: ProgressIndicator) {
indicator.text = "Snyk waits for CLI to be downloaded..."
downloadLatestRelease()
downloadLatestRelease(indicator)
do {
indicator.checkCanceled()
Thread.sleep(WAIT_FOR_DOWNLOAD_MILLIS)
Expand Down Expand Up @@ -228,7 +230,7 @@ class SnykTaskQueueService(val project: Project) {
if (ossResult.isSuccessful()) {
scanPublisher?.scanningOssFinished(ossResult)
} else {
scanPublisher?.scanningOssError(ossResult.getFirstError()!!)
ossResult.getFirstError()?.let { scanPublisher?.scanningOssError(it) }
}
}
DaemonCodeAnalyzer.getInstance(project).restart()
Expand Down Expand Up @@ -279,12 +281,24 @@ class SnykTaskQueueService(val project: Project) {
})
}

fun downloadLatestRelease() {
fun downloadLatestRelease(indicator: ProgressIndicator) {
// abort even before submitting a task
if (!pluginSettings().manageBinariesAutomatically) {
if (!isCliInstalled()) {
val msg =
"The plugin cannot scan without Snyk CLI, but automatic download is disabled. " +
"Please put a Snyk CLI executable in ${pluginSettings().cliPath} and retry."
SnykBalloonNotificationHelper.showError(msg, project)
}
indicator.cancel()
return
}
val cliDownloader = getSnykCliDownloaderService()

taskQueue.run(object : Task.Backgroundable(project, "Check Snyk CLI presence", true) {
override fun run(indicator: ProgressIndicator) {
cliDownloadPublisher.checkCliExistsStarted()
if (project.isDisposed) return
val cliDownloader = getSnykCliDownloaderService()

if (!isCliInstalled()) {
cliDownloader.downloadLatestRelease(indicator, project)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ import com.intellij.util.io.HttpRequests
import io.snyk.plugin.cli.Platform
import io.snyk.plugin.events.SnykCliDownloadListener
import io.snyk.plugin.getCliFile
import io.snyk.plugin.isCliInstalled
import io.snyk.plugin.pluginSettings
import io.snyk.plugin.services.download.HttpRequestHelper.createRequest
import io.snyk.plugin.tail
import io.snyk.plugin.ui.SnykBalloonNotificationHelper
import java.io.IOException
import java.time.LocalDate
import java.time.temporal.ChronoUnit
Expand Down Expand Up @@ -60,18 +58,9 @@ class SnykCliDownloaderService {
}

fun downloadLatestRelease(indicator: ProgressIndicator, project: Project) {
if (!pluginSettings().manageBinariesAutomatically) {
if (!isCliInstalled()) {
val msg =
"The plugin cannot scan without Snyk CLI, but automatic download is disabled. " +
"Please put a Snyk CLI executable in ${pluginSettings().cliPath} and retry."
SnykBalloonNotificationHelper.showError(msg, project)
}
return
}
currentProgressIndicator = indicator
cliDownloadPublisher.cliDownloadStarted()
indicator.isIndeterminate = true
currentProgressIndicator = indicator
var succeeded = false
val cliFile = getCliFile()
try {
Expand Down
4 changes: 4 additions & 0 deletions src/test/java/snyk/common/lsp/LanguageServerWrapperTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ class LanguageServerWrapperTest {

@Test
fun `sendReportAnalyticsCommand should send a reportAnalytics command to the language server`() {
cut.languageClient = mockk(relaxed = true)
val processMock = mockk<Process>(relaxed = true)
cut.process = processMock
every { processMock.info().startInstant().isPresent } returns true
every {
lsMock.workspaceService.executeCommand(any<ExecuteCommandParams>())
} returns CompletableFuture.completedFuture(null)
Expand Down
29 changes: 17 additions & 12 deletions src/test/kotlin/io/snyk/plugin/services/SnykTaskQueueServiceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package io.snyk.plugin.services

import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.service
import com.intellij.openapi.progress.EmptyProgressIndicator
import com.intellij.testFramework.LightPlatformTestCase
import com.intellij.testFramework.PlatformTestUtil
import com.intellij.testFramework.replaceService
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.mockkStatic
import io.mockk.spyk
import io.mockk.unmockkAll
Expand Down Expand Up @@ -47,21 +49,29 @@ class SnykTaskQueueServiceTest : LightPlatformTestCase() {
super.setUp()
unmockkAll()
resetSettings(project)

ossServiceMock = mockk(relaxed = true)
project.replaceService(OssService::class.java, ossServiceMock, project)

mockSnykApiServiceSastEnabled()
replaceSnykApiServiceMockInContainer()

mockkStatic("io.snyk.plugin.UtilsKt")
mockkStatic("snyk.trust.TrustedProjectsKt")

downloaderServiceMock = spyk(SnykCliDownloaderService())
every { downloaderServiceMock.requestLatestReleasesInformation() } returns LatestReleaseInfo(
"http://testUrl",
"testReleaseInfo",
"testTag"
)

every { getSnykCliDownloaderService() } returns downloaderServiceMock
every { downloaderServiceMock.isFourDaysPassedSinceLastCheck() } returns false
every { confirmScanningAndSetWorkspaceTrustedStateIfNeeded(any(), any()) } returns true

mockkObject(SnykTaskQueueService.Companion)
every { SnykTaskQueueService.ls } returns mockk(relaxed = true)
}

private fun mockSnykApiServiceSastEnabled() {
Expand Down Expand Up @@ -94,40 +104,35 @@ class SnykTaskQueueServiceTest : LightPlatformTestCase() {

assertTrue(snykTaskQueueService.getTaskQueue().isEmpty)

snykTaskQueueService.downloadLatestRelease()
snykTaskQueueService.downloadLatestRelease(EmptyProgressIndicator())
PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue()

assertTrue(snykTaskQueueService.getTaskQueue().isEmpty)
assertNull(snykTaskQueueService.ossScanProgressIndicator)
}

fun testCliDownloadBeforeScanIfNeeded() {
val cliFile = getCliFile()
val downloaderMock = setupMockForDownloadTest()
every { downloaderMock.expectedSha() } returns "test"
every { downloaderMock.downloadFile(any(), any(), any()) } returns cliFile
setupAppSettingsForDownloadTests()
every { isCliInstalled() } returns true

val snykTaskQueueService = project.service<SnykTaskQueueService>()
snykTaskQueueService.scan()
PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue()

PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue()
assertTrue(snykTaskQueueService.getTaskQueue().isEmpty)

verify { downloaderMock.downloadFile(any(), any(), any()) }
verify { isCliInstalled() }
}

fun testDontDownloadCLIIfUpdatesDisabled() {
val downloaderMock = setupMockForDownloadTest()
val settings = setupAppSettingsForDownloadTests()
settings.manageBinariesAutomatically = false

val snykTaskQueueService = project.service<SnykTaskQueueService>()

snykTaskQueueService.scan()
PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue()

PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue()
assertTrue(snykTaskQueueService.getTaskQueue().isEmpty)

verify(exactly = 0) { downloaderMock.downloadFile(any(), any(), any()) }
}

Expand Down Expand Up @@ -159,7 +164,7 @@ class SnykTaskQueueServiceTest : LightPlatformTestCase() {
setProject(null) // to avoid double disposing effort in tearDown

// the Task should roll out gracefully without any Exception or Error
snykTaskQueueService.downloadLatestRelease()
snykTaskQueueService.downloadLatestRelease(EmptyProgressIndicator())
}

fun testSastEnablementCheckInScan() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,12 @@ package io.snyk.plugin.services.download

import io.mockk.confirmVerified
import io.mockk.every
import io.mockk.justRun
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import io.mockk.unmockkObject
import io.mockk.verify
import io.snyk.plugin.isCliInstalled
import io.snyk.plugin.pluginSettings
import io.snyk.plugin.services.SnykApplicationSettingsStateService
import io.snyk.plugin.ui.SnykBalloonNotificationHelper
import org.junit.After
import org.junit.Assert.assertNull
import org.junit.Before
Expand Down Expand Up @@ -43,27 +38,4 @@ class SnykCliDownloaderServiceTest {
verify { settingsStateService.manageBinariesAutomatically }
confirmVerified(settingsStateService)
}

@Test
fun `downloadLatestRelease should return without doing anything if updates disabled`() {
val cut = SnykCliDownloaderService()
every { settingsStateService.manageBinariesAutomatically } returns false
every { settingsStateService.cliPath } returns "dummyPath"
every { isCliInstalled() } returns false
mockkObject(SnykBalloonNotificationHelper)
justRun { SnykBalloonNotificationHelper.showError(any(), any()) }

cut.downloadLatestRelease(mockk(), mockk())

verify { settingsStateService.manageBinariesAutomatically }
verify { settingsStateService.cliPath }
verify(exactly = 1) {
SnykBalloonNotificationHelper.showError(
any(), any()
)
}
confirmVerified(settingsStateService) // this makes sure, no publisher, no cli path, nothing is used
confirmVerified(SnykBalloonNotificationHelper)
unmockkObject(SnykBalloonNotificationHelper)
}
}

0 comments on commit fef3ebc

Please sign in to comment.