diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt index 48bc37e409f..889ab566942 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt @@ -6,7 +6,6 @@ import de.rki.coronawarnapp.CoronaWarnApplication import de.rki.coronawarnapp.R import de.rki.coronawarnapp.risk.RiskLevel import de.rki.coronawarnapp.util.security.SecurityHelper.globalEncryptedSharedPreferencesInstance -import org.joda.time.Instant import java.util.Date /** @@ -19,11 +18,6 @@ object LocalData { private val TAG: String? = LocalData::class.simpleName - private const val PREFERENCE_NEXT_TIME_RATE_LIMITING_UNLOCKS = - "preference_next_time_rate_limiting_unlocks" - private const val PREFERENCE_GOOGLE_API_PROVIDE_DIAGNOSIS_KEYS_CALL_COUNT = - "preference_google_api_provide_diagnosis_keys_call_count" - /**************************************************** * ONBOARDING DATA ****************************************************/ @@ -396,40 +390,6 @@ object LocalData { } } - var nextTimeRateLimitingUnlocks: Instant - get() { - return Instant.ofEpochMilli( - getSharedPreferenceInstance().getLong( - PREFERENCE_NEXT_TIME_RATE_LIMITING_UNLOCKS, - 0L - ) - ) - } - set(value) { - getSharedPreferenceInstance().edit(true) { - putLong( - PREFERENCE_NEXT_TIME_RATE_LIMITING_UNLOCKS, - value.millis - ) - } - } - - var googleAPIProvideDiagnosisKeysCallCount: Int - get() { - return getSharedPreferenceInstance().getInt( - PREFERENCE_GOOGLE_API_PROVIDE_DIAGNOSIS_KEYS_CALL_COUNT, - 0 - ) - } - set(value) { - getSharedPreferenceInstance().edit(true) { - putInt( - PREFERENCE_GOOGLE_API_PROVIDE_DIAGNOSIS_KEYS_CALL_COUNT, - value - ) - } - } - /** * Gets the last time of successful risk level calculation as long * from the EncryptedSharedPrefs diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RetrieveDiagnosisKeysTransaction.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RetrieveDiagnosisKeysTransaction.kt index 20f3a38eec5..247d9f74272 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RetrieveDiagnosisKeysTransaction.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RetrieveDiagnosisKeysTransaction.kt @@ -28,21 +28,16 @@ import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction.Retriev import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction.RetrieveDiagnosisKeysTransactionState.CLOSE import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction.RetrieveDiagnosisKeysTransactionState.FETCH_DATE_UPDATE import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction.RetrieveDiagnosisKeysTransactionState.FILES_FROM_WEB_REQUESTS -import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction.RetrieveDiagnosisKeysTransactionState.QUOTA_CALCULATION import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction.RetrieveDiagnosisKeysTransactionState.RETRIEVE_RISK_SCORE_PARAMS import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction.RetrieveDiagnosisKeysTransactionState.SETUP import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction.RetrieveDiagnosisKeysTransactionState.TOKEN import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction.rollback import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction.start import de.rki.coronawarnapp.util.CachedKeyFileHolder -import de.rki.coronawarnapp.util.GoogleQuotaCalculator -import de.rki.coronawarnapp.util.QuotaCalculator import de.rki.coronawarnapp.worker.BackgroundWorkHelper import org.joda.time.DateTime import org.joda.time.DateTimeZone -import org.joda.time.Duration import org.joda.time.Instant -import org.joda.time.chrono.GJChronology import timber.log.Timber import java.io.File import java.util.Date @@ -93,9 +88,6 @@ object RetrieveDiagnosisKeysTransaction : Transaction() { /** Initial Setup of the Transaction and Transaction ID Generation and Date Lock */ SETUP, - /** calculates the Quota so that the rate limiting is caught gracefully*/ - QUOTA_CALCULATION, - /** Initialisation of the identifying token used during the entire transaction */ TOKEN, @@ -124,25 +116,12 @@ object RetrieveDiagnosisKeysTransaction : Transaction() { /** atomic reference for the rollback value for created files during the transaction */ private val exportFilesForRollback = AtomicReference>() - private val progressTowardsQuotaForRollback = AtomicReference() - - private const val QUOTA_RESET_PERIOD_IN_HOURS = 24 - - private val quotaCalculator: QuotaCalculator = GoogleQuotaCalculator( - incrementByAmount = 14, - quotaLimit = 20, - quotaResetPeriod = Duration.standardHours(QUOTA_RESET_PERIOD_IN_HOURS.toLong()), - quotaTimeZone = DateTimeZone.UTC, - quotaChronology = GJChronology.getInstanceUTC() - ) - suspend fun startWithConstraints() { val currentDate = DateTime(Instant.now(), DateTimeZone.UTC) val lastFetch = DateTime( LocalData.lastTimeDiagnosisKeysFromServerFetch(), DateTimeZone.UTC ) - if (LocalData.lastTimeDiagnosisKeysFromServerFetch() == null || currentDate.withTimeAtStartOfDay() != lastFetch.withTimeAtStartOfDay() ) { @@ -171,18 +150,6 @@ object RetrieveDiagnosisKeysTransaction : Transaction() { ****************************************************/ val currentDate = executeSetup() - /**************************************************** - * CALCULATE QUOTA FOR PROVIDE DIAGNOSIS KEYS - ****************************************************/ - val hasExceededQuota = executeQuotaCalculation() - - // When we are above the Quote, cancel the execution entirely - if (hasExceededQuota) { - Timber.tag(TAG).w("above quota, skipping RetrieveDiagnosisKeys") - executeClose() - return@lockAndExecuteUnique - } - /**************************************************** * RETRIEVE TOKEN ****************************************************/ @@ -225,10 +192,6 @@ object RetrieveDiagnosisKeysTransaction : Transaction() { if (TOKEN.isInStateStack()) { rollbackToken() } - // we reset the quota only if the submission has not happened yet - if (QUOTA_CALCULATION.isInStateStack() && !API_SUBMISSION.isInStateStack()) { - rollbackProgressTowardsQuota() - } } catch (e: Exception) { // We handle every exception through a RollbackException to make sure that a single EntryPoint // is available for the caller. @@ -246,11 +209,6 @@ object RetrieveDiagnosisKeysTransaction : Transaction() { LocalData.googleApiToken(googleAPITokenForRollback.get()) } - private fun rollbackProgressTowardsQuota() { - Timber.v("rollback $QUOTA_CALCULATION") - quotaCalculator.resetProgressTowardsQuota(progressTowardsQuotaForRollback.get()) - } - /** * Executes the INIT Transaction State */ @@ -261,16 +219,6 @@ object RetrieveDiagnosisKeysTransaction : Transaction() { currentDate } - /** - * Executes the QUOTA_CALCULATION Transaction State - */ - private suspend fun executeQuotaCalculation() = executeState( - QUOTA_CALCULATION - ) { - progressTowardsQuotaForRollback.set(quotaCalculator.getProgressTowardsQuota()) - quotaCalculator.calculateQuota() - } - /** * Executes the TOKEN Transaction State */ diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/GoogleQuotaCalculator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/GoogleQuotaCalculator.kt deleted file mode 100644 index e686eec758b..00000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/GoogleQuotaCalculator.kt +++ /dev/null @@ -1,87 +0,0 @@ -package de.rki.coronawarnapp.util - -import de.rki.coronawarnapp.storage.LocalData -import org.joda.time.Chronology -import org.joda.time.DateTime -import org.joda.time.DateTimeZone -import org.joda.time.Duration -import org.joda.time.Instant -import timber.log.Timber - -/** - * This Calculator class takes multiple parameters to check if the Google API - * can be called or the Rate Limit has been reached. The Quota is expected to reset at - * the start of the day in the given timeZone and Chronology - * - * @property incrementByAmount The amount of Quota Calls to increment per Call - * @property quotaLimit The maximum amount of Quota Calls allowed before Rate Limiting - * @property quotaResetPeriod The Period after which the Quota Resets - * @property quotaTimeZone The Timezone to work in - * @property quotaChronology The Chronology to work in - */ -class GoogleQuotaCalculator( - val incrementByAmount: Int, - val quotaLimit: Int, - val quotaResetPeriod: Duration, - val quotaTimeZone: DateTimeZone, - val quotaChronology: Chronology -) : QuotaCalculator { - override var hasExceededQuota: Boolean = false - - override fun calculateQuota(): Boolean { - val oldQuota = LocalData.googleAPIProvideDiagnosisKeysCallCount - var currentQuota = oldQuota - - val now = Instant.now() - val nextUnlock = LocalData.nextTimeRateLimitingUnlocks - - Timber.v( - "calculateQuota() start! (currentQuota=%s, timeNow=%s, timeReset=%s)", - oldQuota, now, nextUnlock - ) - if (now.isAfter(nextUnlock)) { - LocalData.nextTimeRateLimitingUnlocks = DateTime - .now(quotaTimeZone) - .withChronology(quotaChronology) - .plus(quotaResetPeriod) - .withTimeAtStartOfDay() - .toInstant() - Timber.d("calculateQuota() quota reset to 0.") - currentQuota = 0 - } else { - Timber.v("calculateQuota() can't be reset yet.") - } - - if (currentQuota <= quotaLimit) { - currentQuota += incrementByAmount - } - - if (currentQuota != oldQuota) { - LocalData.googleAPIProvideDiagnosisKeysCallCount = currentQuota - } - - return (currentQuota > quotaLimit).also { - hasExceededQuota = it - Timber.v( - "calculateQuota() done! -> oldQuota=%d, currentQuotaHm=%d, quotaLimit=%d, EXCEEDED=%b", - oldQuota, currentQuota, quotaLimit, it - ) - } - } - - override fun resetProgressTowardsQuota(newProgress: Int) { - if (newProgress > quotaLimit) { - Timber.w("cannot reset progress to a value higher than the quota limit") - return - } - if (newProgress % incrementByAmount != 0) { - Timber.e("supplied progress is no multiple of $incrementByAmount") - return - } - LocalData.googleAPIProvideDiagnosisKeysCallCount = newProgress - hasExceededQuota = false - Timber.d("resetProgressTowardsQuota(newProgress=%d) done", newProgress) - } - - override fun getProgressTowardsQuota(): Int = LocalData.googleAPIProvideDiagnosisKeysCallCount -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/QuotaCalculator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/QuotaCalculator.kt deleted file mode 100644 index 682f4a6002b..00000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/QuotaCalculator.kt +++ /dev/null @@ -1,29 +0,0 @@ -package de.rki.coronawarnapp.util - -/** - * Class to check if a Quota has been reached based on the calculation done inside - * the Calculator - * - */ -interface QuotaCalculator { - val hasExceededQuota: Boolean - - /** - * This function is called to recalculate an old quota score - */ - fun calculateQuota(): Boolean - - /** - * Reset the quota progress - * - * @param newProgress new progress towards the quota - */ - fun resetProgressTowardsQuota(newProgress: T) - - /** - * Retrieve the current progress towards the quota - * - * @return current progress count - */ - fun getProgressTowardsQuota(): T -} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RetrieveDiagnosisKeysTransactionTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RetrieveDiagnosisKeysTransactionTest.kt index 929b0fcf82b..e8c59395e85 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RetrieveDiagnosisKeysTransactionTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RetrieveDiagnosisKeysTransactionTest.kt @@ -13,7 +13,6 @@ import io.mockk.mockk import io.mockk.mockkObject import io.mockk.unmockkAll import kotlinx.coroutines.runBlocking -import org.joda.time.Instant import org.junit.After import org.junit.Before import org.junit.Test @@ -47,10 +46,6 @@ class RetrieveDiagnosisKeysTransactionTest { every { LocalData.lastTimeDiagnosisKeysFromServerFetch() } returns Date() every { LocalData.lastTimeDiagnosisKeysFromServerFetch(any()) } just Runs every { LocalData.googleApiToken() } returns UUID.randomUUID().toString() - every { LocalData.googleAPIProvideDiagnosisKeysCallCount = any() } just Runs - every { LocalData.googleAPIProvideDiagnosisKeysCallCount } returns 0 - every { LocalData.nextTimeRateLimitingUnlocks = any() } just Runs - every { LocalData.nextTimeRateLimitingUnlocks } returns Instant.now() } @Test @@ -62,7 +57,6 @@ class RetrieveDiagnosisKeysTransactionTest { coVerifyOrder { RetrieveDiagnosisKeysTransaction["executeSetup"]() - RetrieveDiagnosisKeysTransaction["executeQuotaCalculation"]() RetrieveDiagnosisKeysTransaction["executeRetrieveRiskScoreParams"]() RetrieveDiagnosisKeysTransaction["executeFetchKeyFilesFromServer"](any()) RetrieveDiagnosisKeysTransaction["executeFetchDateUpdate"](any()) @@ -83,7 +77,6 @@ class RetrieveDiagnosisKeysTransactionTest { coVerifyOrder { RetrieveDiagnosisKeysTransaction["executeSetup"]() - RetrieveDiagnosisKeysTransaction["executeQuotaCalculation"]() RetrieveDiagnosisKeysTransaction["executeRetrieveRiskScoreParams"]() RetrieveDiagnosisKeysTransaction["executeFetchKeyFilesFromServer"](any()) RetrieveDiagnosisKeysTransaction["executeAPISubmission"]( diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/GoogleQuotaCalculatorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/GoogleQuotaCalculatorTest.kt deleted file mode 100644 index 1ab5fa9b74b..00000000000 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/GoogleQuotaCalculatorTest.kt +++ /dev/null @@ -1,287 +0,0 @@ -package de.rki.coronawarnapp.util - -import de.rki.coronawarnapp.storage.LocalData -import io.mockk.every -import io.mockk.mockkObject -import io.mockk.unmockkObject -import org.joda.time.DateTime -import org.joda.time.DateTimeUtils -import org.joda.time.DateTimeZone -import org.joda.time.Duration -import org.joda.time.Instant -import org.joda.time.chrono.GJChronology -import org.junit.After -import org.junit.Assert.assertEquals -import org.junit.Before -import org.junit.Test -import timber.log.Timber -import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.atomic.AtomicLong - -internal class GoogleQuotaCalculatorTest { - - private val timeInTest = DateTimeUtils.currentTimeMillis() - - private lateinit var classUnderTest: GoogleQuotaCalculator - private val nextTimeRateLimitingUnlocksInTesting = AtomicLong() - private val googleAPIProvideDiagnosisKeysCallCount = AtomicInteger() - - private val defaultIncrementByAmountInTest = 14 - private val defaultQuotaLimitInTest = 20 - - @Before - fun setUpClassUnderTest() { - classUnderTest = GoogleQuotaCalculator( - incrementByAmount = defaultIncrementByAmountInTest, - quotaLimit = defaultQuotaLimitInTest, - quotaResetPeriod = Duration.standardHours(24), - quotaTimeZone = DateTimeZone.UTC, - quotaChronology = GJChronology.getInstanceUTC() - ) - DateTimeUtils.setCurrentMillisFixed(timeInTest) - - // Since LocalData is simple to mock - mockkObject(LocalData) - every { LocalData.nextTimeRateLimitingUnlocks = any() } answers { - nextTimeRateLimitingUnlocksInTesting.set((this.arg(0) as Instant).millis) - } - every { LocalData.nextTimeRateLimitingUnlocks } answers { - Instant.ofEpochMilli(nextTimeRateLimitingUnlocksInTesting.get()) - } - every { LocalData.googleAPIProvideDiagnosisKeysCallCount = any() } answers { - googleAPIProvideDiagnosisKeysCallCount.set(this.arg(0)) - } - every { LocalData.googleAPIProvideDiagnosisKeysCallCount } answers { - googleAPIProvideDiagnosisKeysCallCount.get() - } - } - - @Test - fun `isAboveQuota false if called initially`() { - assertEquals(classUnderTest.hasExceededQuota, false) - } - - @Test - fun `isAboveQuota true if called above quota limit when calling with amount bigger than one`() { - for (callNumber in 1..5) { - classUnderTest.calculateQuota() - val aboveQuota = classUnderTest.hasExceededQuota - Timber.v("call number $callNumber above quota: $aboveQuota") - if (callNumber > 1) { - assertEquals(true, aboveQuota) - } else { - assertEquals(false, aboveQuota) - } - } - } - - @Test - fun `getProgressTowardsQuota increases with calls to isAboveQuota but is stopped once increased above the quota`() { - var latestCallNumberWithoutLimiting = 1 - for (callNumber in 1..5) { - classUnderTest.calculateQuota() - val aboveQuota = classUnderTest.hasExceededQuota - Timber.v("call number $callNumber above quota: $aboveQuota") - val expectedIncrement = callNumber * defaultIncrementByAmountInTest - if (expectedIncrement >= defaultQuotaLimitInTest) { - assertEquals( - (latestCallNumberWithoutLimiting + 1) * defaultIncrementByAmountInTest, - classUnderTest.getProgressTowardsQuota() - ) - } else { - assertEquals( - callNumber * defaultIncrementByAmountInTest, - classUnderTest.getProgressTowardsQuota() - ) - latestCallNumberWithoutLimiting = callNumber - } - } - } - - @Test - fun `getProgressTowardsQuota is reset and the quota is not recalculated but isAboveQuota should still be false`() { - var latestCallNumberWithoutLimiting = 1 - for (callNumber in 1..5) { - classUnderTest.calculateQuota() - val aboveQuota = classUnderTest.hasExceededQuota - Timber.v("call number $callNumber above quota: $aboveQuota") - val expectedIncrement = callNumber * defaultIncrementByAmountInTest - if (expectedIncrement >= defaultQuotaLimitInTest) { - assertEquals( - (latestCallNumberWithoutLimiting + 1) * defaultIncrementByAmountInTest, - classUnderTest.getProgressTowardsQuota() - ) - } else { - assertEquals( - callNumber * defaultIncrementByAmountInTest, - classUnderTest.getProgressTowardsQuota() - ) - latestCallNumberWithoutLimiting = callNumber - } - } - - classUnderTest.resetProgressTowardsQuota(0) - assertEquals(false, classUnderTest.hasExceededQuota) - } - - @Test - fun `getProgressTowardsQuota is reset but the reset value is no multiple of incrementByAmount`() { - var latestCallNumberWithoutLimiting = 1 - for (callNumber in 1..5) { - classUnderTest.calculateQuota() - val aboveQuota = classUnderTest.hasExceededQuota - Timber.v("call number $callNumber above quota: $aboveQuota") - val expectedIncrement = callNumber * defaultIncrementByAmountInTest - if (expectedIncrement >= defaultQuotaLimitInTest) { - assertEquals( - (latestCallNumberWithoutLimiting + 1) * defaultIncrementByAmountInTest, - classUnderTest.getProgressTowardsQuota() - ) - } else { - assertEquals( - callNumber * defaultIncrementByAmountInTest, - classUnderTest.getProgressTowardsQuota() - ) - latestCallNumberWithoutLimiting = callNumber - } - } - - classUnderTest.resetProgressTowardsQuota(defaultIncrementByAmountInTest + 1) - } - - @Test - fun `getProgressTowardsQuota is reset and the quota is not recalculated and the progress should update`() { - var latestCallNumberWithoutLimiting = 1 - for (callNumber in 1..5) { - classUnderTest.calculateQuota() - val aboveQuota = classUnderTest.hasExceededQuota - Timber.v("call number $callNumber above quota: $aboveQuota") - val expectedIncrement = callNumber * defaultIncrementByAmountInTest - if (expectedIncrement >= defaultQuotaLimitInTest) { - assertEquals( - (latestCallNumberWithoutLimiting + 1) * defaultIncrementByAmountInTest, - classUnderTest.getProgressTowardsQuota() - ) - } else { - assertEquals( - callNumber * defaultIncrementByAmountInTest, - classUnderTest.getProgressTowardsQuota() - ) - latestCallNumberWithoutLimiting = callNumber - } - } - - val newProgressAfterReset = 14 - classUnderTest.resetProgressTowardsQuota(newProgressAfterReset) - assertEquals(false, classUnderTest.hasExceededQuota) - assertEquals(newProgressAfterReset, classUnderTest.getProgressTowardsQuota()) - } - - @Test - fun `getProgressTowardsQuota is reset and the quota is not recalculated and the progress throws an error because of too high newProgress`() { - var latestCallNumberWithoutLimiting = 1 - var progressBeforeReset: Int? = null - for (callNumber in 1..5) { - classUnderTest.calculateQuota() - val aboveQuota = classUnderTest.hasExceededQuota - Timber.v("call number $callNumber above quota: $aboveQuota") - val expectedIncrement = callNumber * defaultIncrementByAmountInTest - if (expectedIncrement >= defaultQuotaLimitInTest) { - progressBeforeReset = - (latestCallNumberWithoutLimiting + 1) * defaultIncrementByAmountInTest - assertEquals( - (latestCallNumberWithoutLimiting + 1) * defaultIncrementByAmountInTest, - classUnderTest.getProgressTowardsQuota() - ) - } else { - assertEquals( - callNumber * defaultIncrementByAmountInTest, - classUnderTest.getProgressTowardsQuota() - ) - latestCallNumberWithoutLimiting = callNumber - } - } - - val newProgressAfterReset = defaultQuotaLimitInTest + 1 - classUnderTest.resetProgressTowardsQuota(newProgressAfterReset) - assertEquals(true, classUnderTest.hasExceededQuota) - assertEquals( - (progressBeforeReset - ?: throw IllegalStateException("progressBeforeReset was not set during test")), - classUnderTest.getProgressTowardsQuota() - ) - } - - @Test - fun `isAboveQuota true if called above quota limit when calling with amount one`() { - classUnderTest = GoogleQuotaCalculator( - incrementByAmount = 1, - quotaLimit = 3, - quotaResetPeriod = Duration.standardHours(24), - quotaTimeZone = DateTimeZone.UTC, - quotaChronology = GJChronology.getInstanceUTC() - ) - for (callNumber in 1..15) { - classUnderTest.calculateQuota() - val aboveQuota = classUnderTest.hasExceededQuota - Timber.v("call number $callNumber above quota: $aboveQuota") - if (callNumber > 3) { - assertEquals(true, aboveQuota) - } else { - assertEquals(false, aboveQuota) - } - } - } - - @Test - fun `isAboveQuota false if called above quota limit but next day resets quota`() { - for (callNumber in 1..5) { - classUnderTest.calculateQuota() - val aboveQuota = classUnderTest.hasExceededQuota - Timber.v("call number $callNumber above quota: $aboveQuota") - if (callNumber > 1) { - assertEquals(true, aboveQuota) - } else { - assertEquals(false, aboveQuota) - } - } - - // Day Change - val timeInTestAdvancedByADay = timeInTest + Duration.standardDays(1).millis - DateTimeUtils.setCurrentMillisFixed(timeInTestAdvancedByADay) - classUnderTest.calculateQuota() - val aboveQuotaAfterDayAdvance = classUnderTest.hasExceededQuota - Timber.v("above quota after day advance: $aboveQuotaAfterDayAdvance") - - assertEquals(false, aboveQuotaAfterDayAdvance) - } - - @Test - fun `test if isAfter is affected by Timezone to make sure we do not run into Shifting Errors`() { - val testTimeUTC = DateTime( - timeInTest, - DateTimeZone.UTC - ).withChronology(GJChronology.getInstanceUTC()) - val testTimeGMT = DateTime( - timeInTest, - DateTimeZone.forID("Etc/GMT+2") - ).withChronology(GJChronology.getInstanceUTC()) - - assertEquals(testTimeGMT, testTimeUTC) - assertEquals(testTimeGMT.millis, testTimeUTC.millis) - - val testTimeUTCAfterGMT = testTimeUTC.plusMinutes(1) - - assertEquals(true, testTimeUTCAfterGMT.isAfter(testTimeGMT)) - - val testTimeGMTAfterUTC = testTimeGMT.plusMinutes(1) - - assertEquals(true, testTimeGMTAfterUTC.isAfter(testTimeUTC)) - } - - @After - fun cleanup() { - DateTimeUtils.setCurrentMillisSystem() - unmockkObject(LocalData) - } -}