diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorkerTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorkerTest.kt deleted file mode 100644 index 33e0ef13ed9ee0a6009621b4ed3dc3a16548398e..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorkerTest.kt +++ /dev/null @@ -1,205 +0,0 @@ -package de.rki.coronawarnapp.worker - -import android.content.Context -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.work.WorkInfo -import androidx.work.WorkManager -import androidx.work.WorkRequest -import androidx.work.testing.TestDriver -import androidx.work.testing.WorkManagerTestInitHelper -import de.rki.coronawarnapp.playbook.Playbook -import de.rki.coronawarnapp.service.submission.SubmissionService -import de.rki.coronawarnapp.storage.LocalData -import de.rki.coronawarnapp.util.TimeAndDateExtensions.daysToMilliseconds -import de.rki.coronawarnapp.util.formatter.TestResult -import io.mockk.coEvery -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.mockk -import io.mockk.mockkObject -import io.mockk.slot -import io.mockk.unmockkAll -import org.hamcrest.CoreMatchers.`is` -import org.hamcrest.CoreMatchers.notNullValue -import org.hamcrest.MatcherAssert.assertThat -import org.junit.After -import org.junit.Before -import org.junit.Ignore -import org.junit.Test -import org.junit.runner.RunWith -import java.util.Date - -/** - * DiagnosisTestResultRetrievalPeriodicWorker test. - */ -@Ignore("FixMe:DiagnosisTestResultRetrievalPeriodicWorkerTest") -@RunWith(AndroidJUnit4::class) -class DiagnosisTestResultRetrievalPeriodicWorkerTest { - private lateinit var context: Context - private lateinit var workManager: WorkManager - private lateinit var request: WorkRequest - @MockK lateinit var playbook: Playbook - private val submissionService = SubmissionService(playbook) - // small delay because WorkManager does not run work instantly when delay is off - private val delay = 500L - - @Before - fun setUp() { - LocalData.registrationToken("test token") - LocalData.isTestResultAvailableNotificationSent(false) - mockkObject(LocalData) - mockkObject(BackgroundWorkScheduler) - - // do not init Test WorkManager instance again between tests - // leads to all tests instead of first one to fail - context = ApplicationProvider.getApplicationContext() - if (WorkManager.getInstance(context) !is TestDriver) { - WorkManagerTestInitHelper.initializeTestWorkManager(context) - } - workManager = WorkManager.getInstance(context) - - every { BackgroundWorkScheduler["buildDiagnosisTestResultRetrievalPeriodicWork"]() } answers { - request = this.callOriginal() as WorkRequest - request - } - - val slot = slot<String>() - every { BackgroundWorkScheduler["isWorkActive"](capture(slot)) } answers { - !(slot.isCaptured && slot.captured == - BackgroundWorkScheduler.WorkTag.DIAGNOSIS_TEST_RESULT_RETRIEVAL_PERIODIC_WORKER.tag) - } - } - - /** - * Test worker for running more than a set amount of days. - * - * @see [BackgroundConstants.POLLING_VALIDITY_MAX_DAYS] - */ - @Test - fun testDiagnosisTestResultRetrievalPeriodicWorkerCancel() { - val past = System.currentTimeMillis() - - (BackgroundConstants.POLLING_VALIDITY_MAX_DAYS.toLong() + 1).daysToMilliseconds() - testDiagnosisTestResultRetrievalPeriodicWorkerForResult(mockk(), past, true) - } - - /** - * Test worker for running less than a set amount of days, [TestResult.PENDING]. - * - * @see [BackgroundConstants.POLLING_VALIDITY_MAX_DAYS] - */ - @Test - fun testDiagnosisTestResultRetrievalPeriodicWorkerPending() { - val past = Date().time - (BackgroundConstants.POLLING_VALIDITY_MAX_DAYS.toLong() - 1).daysToMilliseconds() - testDiagnosisTestResultRetrievalPeriodicWorkerForResult(TestResult.PENDING, past) - } - - /** - * Test worker for running less than a set amount of days, [TestResult.NEGATIVE] . - * - * @see [BackgroundConstants.POLLING_VALIDITY_MAX_DAYS] - */ - @Test - fun testDiagnosisTestResultRetrievalPeriodicWorkerSuccessNegative() { - val past = Date().time - (BackgroundConstants.POLLING_VALIDITY_MAX_DAYS.toLong() - 1).daysToMilliseconds() - testDiagnosisTestResultRetrievalPeriodicWorkerForResult(TestResult.NEGATIVE, past) - } - - /** - * Test worker for running less than a set amount of days, [TestResult.POSITIVE] . - * - * @see [BackgroundConstants.POLLING_VALIDITY_MAX_DAYS] - */ - @Test - fun testDiagnosisTestResultRetrievalPeriodicWorkerSuccessPositive() { - val past = Date().time - (BackgroundConstants.POLLING_VALIDITY_MAX_DAYS.toLong() - 1).daysToMilliseconds() - testDiagnosisTestResultRetrievalPeriodicWorkerForResult(TestResult.POSITIVE, past) - } - - /** - * Test worker for retries and fail. - */ - @Test - fun testDiagnosisTestResultRetrievalPeriodicWorkerRetryAndFail() { - val past = Date().time - (BackgroundConstants.POLLING_VALIDITY_MAX_DAYS.toLong() - 1).daysToMilliseconds() - every { LocalData.initialPollingForTestResultTimeStamp() } returns past - - BackgroundWorkScheduler.startWorkScheduler() - - assertThat(request, notNullValue()) - var workInfo = workManager.getWorkInfoById(request.id).get() - assertThat(workInfo, notNullValue()) - assertThat(workInfo.state, `is`((WorkInfo.State.ENQUEUED))) - - for (i in 1..BackgroundConstants.WORKER_RETRY_COUNT_THRESHOLD + 2) { - // run job i times - runPeriodicJobInitialDelayMet() - - // get job run #i result - when (i) { - BackgroundConstants.WORKER_RETRY_COUNT_THRESHOLD + 2 -> { - assertThat(request, notNullValue()) - workInfo = workManager.getWorkInfoById(request.id).get() - assertThat(workInfo.runAttemptCount, `is`(0)) - } - else -> { - assertThat(request, notNullValue()) - workInfo = workManager.getWorkInfoById(request.id).get() - assertThat(workInfo.runAttemptCount, `is`(i)) - } - } - } - } - - @After - fun cleanUp() { - workManager.cancelAllWork() - unmockkAll() - } - - private fun testDiagnosisTestResultRetrievalPeriodicWorkerForResult( - result: TestResult, - past: Long, - isCancelTest: Boolean = false - ) { - coEvery { submissionService.asyncRequestTestResult(any()) } returns result - every { LocalData.initialPollingForTestResultTimeStamp() } returns past - - BackgroundWorkScheduler.startWorkScheduler() - - assertThat(request, notNullValue()) - - val workInfo = workManager.getWorkInfoById(request.id).get() - assertThat(workInfo, notNullValue()) - assertThat(workInfo.state, `is`((WorkInfo.State.ENQUEUED))) - runPeriodicJobInitialDelayMet() - verifyTestResult(result, isCancelTest) - } - - private fun verifyTestResult(result: TestResult, isCancelTest: Boolean) { - assertThat(request, notNullValue()) - val workInfo = workManager.getWorkInfoById(request.id).get() - if (isCancelTest) { - assertThat(workInfo.state, `is`((WorkInfo.State.CANCELLED))) - assertThat(LocalData.isTestResultAvailableNotificationSent(), `is`(false)) - } else { - when (result) { - TestResult.POSITIVE, TestResult.NEGATIVE, TestResult.INVALID -> { - assertThat(workInfo.state, `is`((WorkInfo.State.CANCELLED))) - assertThat(LocalData.isTestResultAvailableNotificationSent(), `is`(true)) - } - TestResult.PENDING -> { - assertThat(workInfo.runAttemptCount, `is`(0)) - assertThat(workInfo.state, `is`((WorkInfo.State.ENQUEUED))) - } - } - } - } - - private fun runPeriodicJobInitialDelayMet() { - val testDriver = WorkManagerTestInitHelper.getTestDriver(context) - testDriver?.setAllConstraintsMet(request.id) - testDriver?.setInitialDelayMet(request.id) - Thread.sleep(delay) - } -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/task/TaskControllerExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/task/TaskControllerExtensions.kt index 2166ace118dc16417307a1f2ec6775de7656e69e..73a26b99f22272f74973d91baf0ba23628a9d895 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/task/TaskControllerExtensions.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/task/TaskControllerExtensions.kt @@ -1,6 +1,5 @@ package de.rki.coronawarnapp.task -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapMerge @@ -17,14 +16,3 @@ suspend fun TaskController.submitBlocking(ourRequest: TaskRequest) = }.also { Timber.v("submitBlocking(request=%s) continuing with result %s", ourRequest, it) } - -suspend fun TaskController.submitAndListen(ourRequest: TaskRequest): Flow<Task.Progress> { - submit(ourRequest) - Timber.v("submitAndListen(request=%s) waiting for progress flow...", ourRequest) - - return tasks.flatMapMerge { it.asFlow() }.first { - it.taskState.request.id == ourRequest.id - }.progress.also { - Timber.v("submitAndListen(request=%s) continuing with flow %s", ourRequest, it) - } -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt index 47b8b15f2257abeefe1e5f3648be753f65a82073..bada3ae70a4bded1eeb277260d7e8f73ac04c32b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt @@ -14,13 +14,14 @@ import de.rki.coronawarnapp.service.submission.SubmissionService import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.submission.SubmissionSettings import de.rki.coronawarnapp.util.TimeAndDateExtensions +import de.rki.coronawarnapp.util.TimeStamper import de.rki.coronawarnapp.util.formatter.TestResult import de.rki.coronawarnapp.util.worker.InjectedWorkerFactory import de.rki.coronawarnapp.worker.BackgroundWorkScheduler.stop import timber.log.Timber /** - * Diagnosis Test Result Periodic retrieval + * Diagnosis test result retrieval by periodic polling * * @see BackgroundWorkScheduler */ @@ -30,16 +31,10 @@ class DiagnosisTestResultRetrievalPeriodicWorker @AssistedInject constructor( private val testResultAvailableNotificationService: TestResultAvailableNotificationService, private val notificationHelper: NotificationHelper, private val submissionSettings: SubmissionSettings, - private val submissionService: SubmissionService + private val submissionService: SubmissionService, + private val timeStamper: TimeStamper ) : CoroutineWorker(context, workerParams) { - /** - * If background job is running for less than 21 days, testResult is checked. - * If the job is running for more than 21 days, the job will be stopped - * - * @see LocalData.isTestResultAvailableNotificationSent - * @see LocalData.initialPollingForTestResultTimeStamp - */ override suspend fun doWork(): Result { Timber.tag(TAG).d("$id: doWork() started. Run attempt: $runAttemptCount") @@ -54,7 +49,7 @@ class DiagnosisTestResultRetrievalPeriodicWorker @AssistedInject constructor( var result = Result.success() try { - if (abortConditionsMet()) { + if (abortConditionsMet(timeStamper.nowUTC.millis)) { Timber.tag(TAG).d(" $id Stopping worker.") stopWorker() } else { @@ -75,7 +70,7 @@ class DiagnosisTestResultRetrievalPeriodicWorker @AssistedInject constructor( stopWorker() } } - } catch (e: Exception) { + } catch (ex: Exception) { result = Result.retry() } @@ -84,7 +79,7 @@ class DiagnosisTestResultRetrievalPeriodicWorker @AssistedInject constructor( return result } - private fun abortConditionsMet(): Boolean { + private fun abortConditionsMet(currentMillis: Long): Boolean { if (LocalData.isTestResultAvailableNotificationSent()) { Timber.tag(TAG).d("$id: Notification already sent.") return true @@ -96,7 +91,7 @@ class DiagnosisTestResultRetrievalPeriodicWorker @AssistedInject constructor( if (TimeAndDateExtensions.calculateDays( LocalData.initialPollingForTestResultTimeStamp(), - System.currentTimeMillis() + currentMillis ) >= BackgroundConstants.POLLING_VALIDITY_MAX_DAYS ) { Timber.tag(TAG) @@ -118,12 +113,6 @@ class DiagnosisTestResultRetrievalPeriodicWorker @AssistedInject constructor( ) } - /** - * Stops the Background Polling worker - * - * @see LocalData.initialPollingForTestResultTimeStamp - * @see BackgroundWorkScheduler.stop - */ private fun stopWorker() { LocalData.initialPollingForTestResultTimeStamp(0L) BackgroundWorkScheduler.WorkType.DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER.stop() diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorkerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorkerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..9de1965602d2351a0fc09046ce922b777f8d790a --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorkerTest.kt @@ -0,0 +1,241 @@ +package de.rki.coronawarnapp.worker + +import android.content.Context +import androidx.work.ListenableWorker +import androidx.work.Operation +import androidx.work.WorkRequest +import androidx.work.WorkerParameters +import de.rki.coronawarnapp.notification.NotificationConstants +import de.rki.coronawarnapp.notification.NotificationHelper +import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService +import de.rki.coronawarnapp.service.submission.SubmissionService +import de.rki.coronawarnapp.storage.LocalData +import de.rki.coronawarnapp.submission.SubmissionSettings +import de.rki.coronawarnapp.util.TimeAndDateExtensions.daysToMilliseconds +import de.rki.coronawarnapp.util.TimeStamper +import de.rki.coronawarnapp.util.di.AppInjector +import de.rki.coronawarnapp.util.di.ApplicationComponent +import de.rki.coronawarnapp.util.formatter.TestResult +import de.rki.coronawarnapp.util.security.EncryptedPreferencesFactory +import de.rki.coronawarnapp.util.security.EncryptionErrorResetTool +import de.rki.coronawarnapp.worker.BackgroundWorkScheduler.stop +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.Runs +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.impl.annotations.RelaxedMockK +import io.mockk.just +import io.mockk.mockkObject +import io.mockk.verify +import kotlinx.coroutines.test.runBlockingTest +import org.joda.time.Instant +import org.junit.Before +import org.junit.Test +import testhelpers.BaseTest + +class DiagnosisTestResultRetrievalPeriodicWorkerTest : BaseTest() { + @MockK lateinit var context: Context + @MockK lateinit var request: WorkRequest + @MockK lateinit var submissionSettings: SubmissionSettings + @MockK lateinit var submissionService: SubmissionService + @MockK lateinit var testResultAvailableNotificationService: TestResultAvailableNotificationService + @MockK lateinit var notificationHelper: NotificationHelper + @MockK lateinit var appComponent: ApplicationComponent + @MockK lateinit var encryptedPreferencesFactory: EncryptedPreferencesFactory + @MockK lateinit var encryptionErrorResetTool: EncryptionErrorResetTool + @MockK lateinit var operation: Operation + @MockK lateinit var timeStamper: TimeStamper + @RelaxedMockK lateinit var workerParams: WorkerParameters + private val currentInstant = Instant.ofEpochSecond(1611764225) + private val registrationToken = "test token" + + @Before + fun setUp() { + MockKAnnotations.init(this) + every { submissionSettings.hasViewedTestResult.value } returns false + every { timeStamper.nowUTC } returns currentInstant + + mockkObject(AppInjector) + every { AppInjector.component } returns appComponent + every { appComponent.encryptedPreferencesFactory } returns encryptedPreferencesFactory + every { appComponent.errorResetTool } returns encryptionErrorResetTool + + mockkObject(LocalData) + every { LocalData.registrationToken() } returns registrationToken + every { LocalData.isTestResultAvailableNotificationSent() } returns false + every { LocalData.initialPollingForTestResultTimeStamp() } returns currentInstant.millis + every { LocalData.initialPollingForTestResultTimeStamp(any()) } just Runs + every { LocalData.isTestResultAvailableNotificationSent(any()) } just Runs + + mockkObject(BackgroundWorkScheduler) + every { BackgroundWorkScheduler.WorkType.DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER.stop() } returns operation + } + + @Test + fun testStopWorkerWhenResultHasBeenViewed() { + runBlockingTest { + every { submissionSettings.hasViewedTestResult.value } returns true + val worker = createWorker() + val result = worker.doWork() + coVerify(exactly = 0) { submissionService.asyncRequestTestResult(any()) } + verify(exactly = 1) { BackgroundWorkScheduler.WorkType.DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER.stop() } + assert(result is ListenableWorker.Result.Success) + } + } + + @Test + fun testStopWorkerWhenNotificationSent() { + runBlockingTest { + every { LocalData.isTestResultAvailableNotificationSent() } returns true + val worker = createWorker() + val result = worker.doWork() + coVerify(exactly = 0) { submissionService.asyncRequestTestResult(any()) } + verify(exactly = 1) { BackgroundWorkScheduler.WorkType.DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER.stop() } + assert(result is ListenableWorker.Result.Success) + } + } + + @Test + fun testStopWorkerWhenMaxDaysExceeded() { + runBlockingTest { + val past = + currentInstant - (BackgroundConstants.POLLING_VALIDITY_MAX_DAYS.toLong() + 1).daysToMilliseconds() + every { LocalData.initialPollingForTestResultTimeStamp() } returns past.millis + val worker = createWorker() + val result = worker.doWork() + coVerify(exactly = 0) { submissionService.asyncRequestTestResult(any()) } + verify(exactly = 1) { BackgroundWorkScheduler.WorkType.DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER.stop() } + result shouldBe ListenableWorker.Result.success() + } + } + + @Test + fun testSendNotificationWhenPositive() { + runBlockingTest { + val testResult = TestResult.POSITIVE + coEvery { submissionService.asyncRequestTestResult(registrationToken) } returns testResult + coEvery { testResultAvailableNotificationService.showTestResultAvailableNotification(testResult) } just Runs + coEvery { + notificationHelper.cancelCurrentNotification( + NotificationConstants.NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID + ) + } just Runs + val worker = createWorker() + val result = worker.doWork() + coVerify { submissionService.asyncRequestTestResult(registrationToken) } + coVerify { LocalData.isTestResultAvailableNotificationSent(true) } + coVerify { testResultAvailableNotificationService.showTestResultAvailableNotification(testResult) } + coVerify { + notificationHelper.cancelCurrentNotification( + NotificationConstants.NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID + ) + } + assert(result is ListenableWorker.Result.Success) + } + } + + @Test + fun testSendNotificationWhenNegative() { + runBlockingTest { + val testResult = TestResult.NEGATIVE + coEvery { submissionService.asyncRequestTestResult(registrationToken) } returns testResult + coEvery { testResultAvailableNotificationService.showTestResultAvailableNotification(testResult) } just Runs + coEvery { + notificationHelper.cancelCurrentNotification( + NotificationConstants.NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID + ) + } just Runs + val worker = createWorker() + val result = worker.doWork() + coVerify { submissionService.asyncRequestTestResult(registrationToken) } + coVerify { LocalData.isTestResultAvailableNotificationSent(true) } + coVerify { testResultAvailableNotificationService.showTestResultAvailableNotification(testResult) } + coVerify { + notificationHelper.cancelCurrentNotification( + NotificationConstants.NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID + ) + } + assert(result is ListenableWorker.Result.Success) + } + } + + @Test + fun testSendNotificationWhenInvalid() { + runBlockingTest { + val testResult = TestResult.INVALID + coEvery { submissionService.asyncRequestTestResult(registrationToken) } returns testResult + coEvery { testResultAvailableNotificationService.showTestResultAvailableNotification(testResult) } just Runs + coEvery { + notificationHelper.cancelCurrentNotification( + NotificationConstants.NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID + ) + } just Runs + val worker = createWorker() + val result = worker.doWork() + coVerify { submissionService.asyncRequestTestResult(registrationToken) } + coVerify { LocalData.isTestResultAvailableNotificationSent(true) } + coVerify { testResultAvailableNotificationService.showTestResultAvailableNotification(testResult) } + coVerify { + notificationHelper.cancelCurrentNotification( + NotificationConstants.NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID + ) + } + assert(result is ListenableWorker.Result.Success) + } + } + + @Test + fun testSendNoNotificationWhenPending() { + runBlockingTest { + val testResult = TestResult.PENDING + coEvery { submissionService.asyncRequestTestResult(registrationToken) } returns testResult + coEvery { testResultAvailableNotificationService.showTestResultAvailableNotification(testResult) } just Runs + coEvery { + notificationHelper.cancelCurrentNotification( + NotificationConstants.NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID + ) + } just Runs + val worker = createWorker() + val result = worker.doWork() + coVerify { submissionService.asyncRequestTestResult(registrationToken) } + coVerify(exactly = 0) { LocalData.isTestResultAvailableNotificationSent(true) } + coVerify(exactly = 0) { + testResultAvailableNotificationService.showTestResultAvailableNotification( + testResult + ) + } + coVerify(exactly = 0) { + notificationHelper.cancelCurrentNotification( + NotificationConstants.NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID + ) + } + coVerify(exactly = 0) { BackgroundWorkScheduler.WorkType.DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER.stop() } + assert(result is ListenableWorker.Result.Success) + } + } + + @Test + fun testRetryWhenExceptionIsThrown() { + runBlockingTest { + coEvery { submissionService.asyncRequestTestResult(registrationToken) } throws Exception() + val worker = createWorker() + val result = worker.doWork() + coVerify(exactly = 1) { submissionService.asyncRequestTestResult(any()) } + coVerify(exactly = 0) { BackgroundWorkScheduler.WorkType.DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER.stop() } + result shouldBe ListenableWorker.Result.retry() + } + } + + private fun createWorker() = DiagnosisTestResultRetrievalPeriodicWorker( + context, + workerParams, + testResultAvailableNotificationService, + notificationHelper, + submissionSettings, + submissionService, + timeStamper + ) +}