Updated on 2026-08-14
This commit is contained in:
parent
7472e7b1dc
commit
2debaafcbb
33 changed files with 1386 additions and 7 deletions
46
data/promo/build.gradle.kts
Normal file
46
data/promo/build.gradle.kts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
alias(deps.plugins.kotlin.kapt)
|
||||
alias(deps.plugins.hilt.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.data.promo"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
// region Kotlin
|
||||
implementation(deps.kotlin.coroutines)
|
||||
implementation(deps.kotlin.datetime)
|
||||
// endregion
|
||||
|
||||
// region DI
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
// endregion
|
||||
|
||||
// region Core
|
||||
api(projects.core.datasource)
|
||||
api(projects.core.utils)
|
||||
// endregion
|
||||
|
||||
// region Domain
|
||||
api(projects.domain.promo)
|
||||
// endregion
|
||||
|
||||
// region Domain models
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.promo.models)
|
||||
// endregion
|
||||
|
||||
// region Tests
|
||||
testImplementation(deps.test.coroutine)
|
||||
testImplementation(deps.test.junit5)
|
||||
testImplementation(deps.test.mockk)
|
||||
testImplementation(deps.test.truth)
|
||||
testImplementation(deps.moshi.kotlin)
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
package com.tangem.data.promo
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.data.promo.converter.PromoCampaignConverter
|
||||
import com.tangem.data.promo.store.PromoEnrollmentStore
|
||||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
import com.tangem.datasource.api.promotion.models.CreatePromotionRegistrationBody
|
||||
import com.tangem.datasource.api.promotion.models.PromotionRegistrationResponse
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.local.promotion.PromotionsSupplier
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.promo.PromoRepository
|
||||
import com.tangem.domain.promo.models.EnrollResult
|
||||
import com.tangem.domain.promo.models.PromoCampaignId
|
||||
import com.tangem.domain.promo.models.PromoCampaignState
|
||||
import com.tangem.domain.promo.models.TokenReward
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
internal class DefaultPromoRepository(
|
||||
private val promotionsSupplier: PromotionsSupplier,
|
||||
private val tangemApi: TangemTechApi,
|
||||
private val enrollmentStore: PromoEnrollmentStore,
|
||||
private val moshi: Moshi,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : PromoRepository {
|
||||
|
||||
override suspend fun getCampaignState(
|
||||
campaign: PromoCampaignId,
|
||||
userWalletId: UserWalletId,
|
||||
forceRefresh: Boolean,
|
||||
): PromoCampaignState = withContext(dispatchers.io) {
|
||||
enrollmentStore.getSyncOrNull(campaign)?.let {
|
||||
return@withContext PromoCampaignState.Enrolled(campaign, it)
|
||||
}
|
||||
val all = promotionsSupplier.getPromotions(userWalletId, forceRefresh)
|
||||
.promotions.firstOrNull { it.name == campaign.slug }?.all
|
||||
when {
|
||||
all == null -> PromoCampaignState.NotActive(campaign)
|
||||
all.status == ACTIVE_STATUS -> PromoCampaignConverter.toAvailable(campaign, all)
|
||||
else -> PromoCampaignState.NotActive(campaign)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun enroll(
|
||||
campaign: PromoCampaignId,
|
||||
tokenReward: TokenReward,
|
||||
walletIds: List<UserWalletId>,
|
||||
): EnrollResult = withContext(dispatchers.io) {
|
||||
val body = CreatePromotionRegistrationBody(
|
||||
campaignId = campaign.slug,
|
||||
walletIds = walletIds.map { it.stringValue },
|
||||
tokenReward = tokenReward.toDto(),
|
||||
)
|
||||
when (val response = tangemApi.createPromotionRegistration(body)) {
|
||||
is ApiResponse.Success -> {
|
||||
val saved = response.data.data.tokenReward.toDomain()
|
||||
enrollmentStore.store(campaign, saved)
|
||||
EnrollResult.Success(saved)
|
||||
}
|
||||
is ApiResponse.Error -> {
|
||||
val cause = response.cause
|
||||
val conflict = (cause as? ApiResponseError.HttpException)
|
||||
?.takeIf { it.code == ApiResponseError.HttpException.Code.CONFLICT }
|
||||
if (conflict != null) {
|
||||
val existing = parseConflict(conflict.errorBody)?.data?.tokenReward?.toDomain() ?: tokenReward
|
||||
enrollmentStore.store(campaign, existing)
|
||||
EnrollResult.AlreadyEnrolled(existing)
|
||||
} else {
|
||||
throw cause
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseConflict(body: String?): PromotionRegistrationResponse? {
|
||||
if (body.isNullOrBlank()) return null
|
||||
return runCatching {
|
||||
moshi.adapter(PromotionRegistrationResponse::class.java).fromJson(body)
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
private fun TokenReward.toDto() = CreatePromotionRegistrationBody.TokenRewardDto(
|
||||
tokenAddress = tokenAddress,
|
||||
networkId = networkId,
|
||||
)
|
||||
|
||||
private fun CreatePromotionRegistrationBody.TokenRewardDto.toDomain() = TokenReward(
|
||||
tokenAddress = tokenAddress,
|
||||
networkId = networkId,
|
||||
)
|
||||
|
||||
private companion object {
|
||||
const val ACTIVE_STATUS = "active"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.data.promo.converter
|
||||
|
||||
import com.tangem.datasource.api.promotion.models.PromotionsResponse.PromotionDto.All
|
||||
import com.tangem.domain.promo.models.PromoCampaignId
|
||||
import com.tangem.domain.promo.models.PromoCampaignState
|
||||
import com.tangem.domain.promo.models.PromoPayoutToken
|
||||
import com.tangem.domain.promo.models.PromoTimeline
|
||||
import kotlinx.datetime.Instant
|
||||
|
||||
internal object PromoCampaignConverter {
|
||||
|
||||
fun toAvailable(campaign: PromoCampaignId, all: All): PromoCampaignState.Available {
|
||||
return PromoCampaignState.Available(
|
||||
campaign = campaign,
|
||||
payoutTokens = all.tokens.orEmpty().map { token ->
|
||||
PromoPayoutToken(
|
||||
tokenAddress = token.tokenAddress,
|
||||
tokenSymbol = token.tokenSymbol,
|
||||
tokenName = token.tokenName,
|
||||
networkId = token.networkId,
|
||||
)
|
||||
},
|
||||
timeline = PromoTimeline(
|
||||
start = Instant.parse(all.timeline.start),
|
||||
end = Instant.parse(all.timeline.end),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.tangem.data.promo.di
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.data.promo.DefaultPromoRepository
|
||||
import com.tangem.data.promo.store.DefaultPromoEnrollmentStore
|
||||
import com.tangem.data.promo.store.PromoEnrollmentStore
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.di.NetworkMoshi
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.promotion.PromotionsSupplier
|
||||
import com.tangem.domain.promo.PromoRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object PromoDataModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providePromoEnrollmentStore(appPreferencesStore: AppPreferencesStore): PromoEnrollmentStore {
|
||||
return DefaultPromoEnrollmentStore(appPreferencesStore)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providePromoRepository(
|
||||
promotionsSupplier: PromotionsSupplier,
|
||||
tangemApi: TangemTechApi,
|
||||
enrollmentStore: PromoEnrollmentStore,
|
||||
@NetworkMoshi moshi: Moshi,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): PromoRepository {
|
||||
return DefaultPromoRepository(
|
||||
promotionsSupplier = promotionsSupplier,
|
||||
tangemApi = tangemApi,
|
||||
enrollmentStore = enrollmentStore,
|
||||
moshi = moshi,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.data.promo.store
|
||||
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectMapSync
|
||||
import com.tangem.domain.promo.models.PromoCampaignId
|
||||
import com.tangem.domain.promo.models.TokenReward
|
||||
|
||||
internal class DefaultPromoEnrollmentStore(
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
) : PromoEnrollmentStore {
|
||||
|
||||
override suspend fun getSyncOrNull(campaign: PromoCampaignId): TokenReward? {
|
||||
return appPreferencesStore
|
||||
.getObjectMapSync<TokenReward>(PreferencesKeys.PROMO_ENROLLMENTS_KEY)[campaign.slug]
|
||||
}
|
||||
|
||||
override suspend fun store(campaign: PromoCampaignId, tokenReward: TokenReward) {
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val current = mutablePreferences.getObjectMap<TokenReward>(PreferencesKeys.PROMO_ENROLLMENTS_KEY)
|
||||
mutablePreferences.setObjectMap(
|
||||
key = PreferencesKeys.PROMO_ENROLLMENTS_KEY,
|
||||
value = current + (campaign.slug to tokenReward),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.data.promo.store
|
||||
|
||||
import com.tangem.domain.promo.models.PromoCampaignId
|
||||
import com.tangem.domain.promo.models.TokenReward
|
||||
|
||||
interface PromoEnrollmentStore {
|
||||
|
||||
suspend fun getSyncOrNull(campaign: PromoCampaignId): TokenReward?
|
||||
|
||||
suspend fun store(campaign: PromoCampaignId, tokenReward: TokenReward)
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
package com.tangem.data.promo
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import com.tangem.data.promo.store.PromoEnrollmentStore
|
||||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
import com.tangem.datasource.api.promotion.models.CreatePromotionRegistrationBody
|
||||
import com.tangem.datasource.api.promotion.models.PromotionRegistrationResponse
|
||||
import com.tangem.datasource.api.promotion.models.PromotionsResponse
|
||||
import com.tangem.datasource.api.promotion.models.PromotionsResponse.PromotionDto
|
||||
import com.tangem.datasource.api.promotion.models.PromotionsResponse.PromotionDto.All
|
||||
import com.tangem.datasource.api.promotion.models.PromotionsResponse.PromotionDto.PromoToken
|
||||
import com.tangem.datasource.api.promotion.models.PromotionsResponse.PromotionDto.Timeline
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.local.promotion.PromotionsSupplier
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.promo.models.EnrollResult
|
||||
import com.tangem.domain.promo.models.PromoCampaignId
|
||||
import com.tangem.domain.promo.models.PromoCampaignState
|
||||
import com.tangem.domain.promo.models.TokenReward
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class DefaultPromoRepositoryTest {
|
||||
|
||||
private val promotionsSupplier: PromotionsSupplier = mockk()
|
||||
private val tangemApi: TangemTechApi = mockk()
|
||||
private val enrollmentStore: PromoEnrollmentStore = mockk(relaxed = true)
|
||||
private val moshi: Moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
|
||||
private val repository = DefaultPromoRepository(
|
||||
promotionsSupplier = promotionsSupplier,
|
||||
tangemApi = tangemApi,
|
||||
enrollmentStore = enrollmentStore,
|
||||
moshi = moshi,
|
||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||
)
|
||||
|
||||
private val campaign = PromoCampaignId.WhaleSwapCashback
|
||||
private val userWalletId = UserWalletId("abcdef012345")
|
||||
private val tokenReward = TokenReward("0xToken", "ethereum")
|
||||
|
||||
private fun activeDto() = PromotionDto(
|
||||
name = campaign.slug,
|
||||
all = All(
|
||||
timeline = Timeline("2026-06-23T00:00:00.000Z", "2026-08-31T20:59:59.000Z"),
|
||||
tokens = listOf(PromoToken("0xToken", "USDT", "Tether USD", "ethereum")),
|
||||
status = "active",
|
||||
link = "",
|
||||
),
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() = clearMocks(promotionsSupplier, tangemApi, enrollmentStore)
|
||||
|
||||
@Test
|
||||
fun `GIVEN locally enrolled WHEN getCampaignState THEN Enrolled without api`() = runTest {
|
||||
// Arrange
|
||||
coEvery { enrollmentStore.getSyncOrNull(campaign) } returns tokenReward
|
||||
|
||||
// Act
|
||||
val result = repository.getCampaignState(campaign, userWalletId)
|
||||
|
||||
// Assert
|
||||
assertThat(result).isEqualTo(PromoCampaignState.Enrolled(campaign, tokenReward))
|
||||
coVerify(exactly = 0) { promotionsSupplier.getPromotions(any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN active campaign present and not enrolled WHEN getCampaignState THEN Available`() = runTest {
|
||||
// Arrange
|
||||
coEvery { enrollmentStore.getSyncOrNull(campaign) } returns null
|
||||
coEvery { promotionsSupplier.getPromotions(userWalletId, any()) } returns
|
||||
PromotionsResponse(promotions = listOf(activeDto()))
|
||||
|
||||
// Act
|
||||
val result = repository.getCampaignState(campaign, userWalletId)
|
||||
|
||||
// Assert
|
||||
assertThat(result).isInstanceOf(PromoCampaignState.Available::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN campaign absent WHEN getCampaignState THEN NotActive`() = runTest {
|
||||
// Arrange
|
||||
coEvery { enrollmentStore.getSyncOrNull(campaign) } returns null
|
||||
coEvery { promotionsSupplier.getPromotions(userWalletId, any()) } returns
|
||||
PromotionsResponse(promotions = emptyList())
|
||||
|
||||
// Act
|
||||
val result = repository.getCampaignState(campaign, userWalletId)
|
||||
|
||||
// Assert
|
||||
assertThat(result).isEqualTo(PromoCampaignState.NotActive(campaign))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN campaign present but finished WHEN getCampaignState THEN NotActive`() = runTest {
|
||||
// Arrange
|
||||
coEvery { enrollmentStore.getSyncOrNull(campaign) } returns null
|
||||
val finished = activeDto().copy(all = activeDto().all!!.copy(status = "finished"))
|
||||
coEvery { promotionsSupplier.getPromotions(userWalletId, any()) } returns
|
||||
PromotionsResponse(promotions = listOf(finished))
|
||||
|
||||
// Act
|
||||
val result = repository.getCampaignState(campaign, userWalletId)
|
||||
|
||||
// Assert
|
||||
assertThat(result).isEqualTo(PromoCampaignState.NotActive(campaign))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN api returns 201 with canonical token WHEN enroll THEN Success and persists backend token`() = runTest {
|
||||
// Arrange
|
||||
val data = PromotionRegistrationResponse.RegistrationData(
|
||||
campaignId = campaign.slug,
|
||||
registeredAt = "2026-07-06T09:27:13.363Z",
|
||||
tokenReward = CreatePromotionRegistrationBody.TokenRewardDto("0xCanonical", "ethereum"),
|
||||
)
|
||||
coEvery { tangemApi.createPromotionRegistration(any()) } returns ApiResponse.Success(
|
||||
PromotionRegistrationResponse(status = "saved", message = null, data = data),
|
||||
)
|
||||
|
||||
// Act
|
||||
val result = repository.enroll(campaign, tokenReward, listOf(userWalletId))
|
||||
|
||||
// Assert
|
||||
val backendToken = TokenReward("0xCanonical", "ethereum")
|
||||
assertThat(result).isEqualTo(EnrollResult.Success(backendToken))
|
||||
coVerify(exactly = 1) { enrollmentStore.store(campaign, backendToken) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN api returns 409 WHEN enroll THEN AlreadyEnrolled with existing token`() = runTest {
|
||||
// Arrange
|
||||
val existing = """
|
||||
{"status":"already_exists","message":"exists","data":{"campaignId":"${campaign.slug}",
|
||||
"registeredAt":"2026-07-01T10:00:00.000Z","tokenReward":{"tokenAddress":"0xOther",
|
||||
"networkId":"base","userAddress":"0xExisting"}}}
|
||||
""".trimIndent()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
coEvery { tangemApi.createPromotionRegistration(any()) } returns ApiResponse.Error(
|
||||
ApiResponseError.HttpException(
|
||||
code = ApiResponseError.HttpException.Code.CONFLICT,
|
||||
message = "conflict",
|
||||
errorBody = existing,
|
||||
),
|
||||
) as ApiResponse<PromotionRegistrationResponse>
|
||||
|
||||
// Act
|
||||
val result = repository.enroll(campaign, tokenReward, listOf(userWalletId))
|
||||
|
||||
// Assert
|
||||
val expectedToken = TokenReward("0xOther", "base")
|
||||
assertThat(result).isEqualTo(EnrollResult.AlreadyEnrolled(expectedToken))
|
||||
coVerify(exactly = 1) { enrollmentStore.store(campaign, expectedToken) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN 409 with null errorBody WHEN enroll THEN AlreadyEnrolled with submitted token`() = runTest {
|
||||
// Arrange
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
coEvery { tangemApi.createPromotionRegistration(any()) } returns ApiResponse.Error(
|
||||
ApiResponseError.HttpException(
|
||||
code = ApiResponseError.HttpException.Code.CONFLICT,
|
||||
message = "conflict",
|
||||
errorBody = null,
|
||||
),
|
||||
) as ApiResponse<PromotionRegistrationResponse>
|
||||
|
||||
// Act
|
||||
val result = repository.enroll(campaign, tokenReward, listOf(userWalletId))
|
||||
|
||||
// Assert
|
||||
assertThat(result).isEqualTo(EnrollResult.AlreadyEnrolled(tokenReward))
|
||||
coVerify(exactly = 1) { enrollmentStore.store(campaign, tokenReward) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN api returns 500 WHEN enroll THEN throws`() = runTest {
|
||||
// Arrange
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
coEvery { tangemApi.createPromotionRegistration(any()) } returns ApiResponse.Error(
|
||||
ApiResponseError.HttpException(
|
||||
code = ApiResponseError.HttpException.Code.INTERNAL_SERVER_ERROR,
|
||||
message = "server",
|
||||
errorBody = null,
|
||||
),
|
||||
) as ApiResponse<PromotionRegistrationResponse>
|
||||
|
||||
// Act
|
||||
val error = runCatching { repository.enroll(campaign, tokenReward, listOf(userWalletId)) }.exceptionOrNull()
|
||||
|
||||
// Assert
|
||||
assertThat(error).isNotNull()
|
||||
coVerify(exactly = 0) { enrollmentStore.store(any(), any()) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.tangem.data.promo.converter
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.datasource.api.promotion.models.PromotionsResponse.PromotionDto.All
|
||||
import com.tangem.datasource.api.promotion.models.PromotionsResponse.PromotionDto.PromoToken
|
||||
import com.tangem.datasource.api.promotion.models.PromotionsResponse.PromotionDto.Timeline
|
||||
import com.tangem.domain.promo.models.PromoCampaignId
|
||||
import com.tangem.domain.promo.models.PromoPayoutToken
|
||||
import kotlinx.datetime.Instant
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
internal class PromoCampaignConverterTest {
|
||||
|
||||
private val campaign = PromoCampaignId.WhaleSwapCashback
|
||||
|
||||
@Test
|
||||
fun `GIVEN dto with tokens WHEN toAvailable THEN maps tokens and timeline`() {
|
||||
// Arrange
|
||||
val all = All(
|
||||
timeline = Timeline(start = "2026-06-23T00:00:00.000Z", end = "2026-08-31T20:59:59.000Z"),
|
||||
tokens = listOf(PromoToken("0xdac1", "USDT", "Tether USD", "ethereum")),
|
||||
status = "active",
|
||||
link = "",
|
||||
)
|
||||
|
||||
// Act
|
||||
val result = PromoCampaignConverter.toAvailable(campaign, all)
|
||||
|
||||
// Assert
|
||||
assertThat(result.campaign).isEqualTo(campaign)
|
||||
assertThat(result.payoutTokens).containsExactly(
|
||||
PromoPayoutToken("0xdac1", "USDT", "Tether USD", "ethereum"),
|
||||
)
|
||||
assertThat(result.timeline.start).isEqualTo(Instant.parse("2026-06-23T00:00:00.000Z"))
|
||||
assertThat(result.timeline.end).isEqualTo(Instant.parse("2026-08-31T20:59:59.000Z"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN dto with null tokens WHEN toAvailable THEN empty payout list`() {
|
||||
// Arrange
|
||||
val all = All(
|
||||
timeline = Timeline(start = "2026-06-23T00:00:00.000Z", end = "2026-08-31T20:59:59.000Z"),
|
||||
tokens = null,
|
||||
status = "active",
|
||||
link = null,
|
||||
)
|
||||
|
||||
// Act
|
||||
val result = PromoCampaignConverter.toAvailable(campaign, all)
|
||||
|
||||
// Assert
|
||||
assertThat(result.payoutTokens).isEmpty()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue