Updated on 2026-08-14
This commit is contained in:
parent
b196684377
commit
cff04da0a8
2 changed files with 88 additions and 22 deletions
|
|
@ -1,14 +1,26 @@
|
|||
package com.tangem.common.annotations
|
||||
|
||||
import com.tangem.datasource.api.common.config.ApiConfig
|
||||
import com.tangem.datasource.api.common.config.ApiEnvironment
|
||||
|
||||
/**
|
||||
* Annotation to specify the API environment for a class or function.
|
||||
* Single API environment configuration.
|
||||
*
|
||||
* @property apiConfigId the ID of the API configuration
|
||||
* @property environment the API environment to be used (defaults to [ApiEnvironment.MOCK])
|
||||
*/
|
||||
annotation class ApiEnvConfig(
|
||||
val apiConfigId: ApiConfig.ID,
|
||||
val environment: ApiEnvironment = ApiEnvironment.MOCK,
|
||||
)
|
||||
|
||||
/**
|
||||
* Annotation to specify the API environment configurations for a class or function.
|
||||
*
|
||||
* @property value array of API environment configurations
|
||||
*/
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class ApiEnv(
|
||||
val environment: ApiEnvironment = ApiEnvironment.MOCK,
|
||||
vararg val value: ApiEnvConfig,
|
||||
)
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.common.rules
|
|||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.tangem.common.annotations.ApiEnv
|
||||
import com.tangem.datasource.api.common.config.ApiConfig
|
||||
import com.tangem.datasource.api.common.config.ApiEnvironment
|
||||
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
|
||||
import com.tangem.datasource.api.common.config.managers.MutableApiConfigsManager
|
||||
|
|
@ -15,15 +16,15 @@ import timber.log.Timber
|
|||
/**
|
||||
* A JUnit rule that sets up the API environment for tests based on annotations or instrumentation arguments.
|
||||
*
|
||||
* This rule allows tests to specify which API environment to use either through an annotation on the test method/class
|
||||
* This rule allows tests to specify which API environment to use either through annotations on the test method/class
|
||||
* or via an instrumentation argument.
|
||||
*
|
||||
* This rule only finds and stores the required API environment. The actual environment setup is performed
|
||||
* by calling the [setup] method with an appropriate [ApiConfigsManager] instance.
|
||||
* This rule supports @ApiEnv annotation with a map of API configs to configure different environments.
|
||||
* For any ApiConfig.ID not specified in annotations, MOCK environment will be used by default.
|
||||
*/
|
||||
class ApiEnvironmentRule : TestRule {
|
||||
|
||||
private var targetEnvironment: ApiEnvironment? = null
|
||||
private var targetEnvironments: Map<ApiConfig.ID, ApiEnvironment> = emptyMap()
|
||||
|
||||
/**
|
||||
* Sets up the API environment based on the provided [ApiConfigsManager].
|
||||
|
|
@ -36,43 +37,96 @@ class ApiEnvironmentRule : TestRule {
|
|||
"MutableApiConfigsManager isn't available in build type ${BuildConfig.BUILD_TYPE}."
|
||||
}
|
||||
|
||||
mutableManager.setupEnvironment()
|
||||
mutableManager.setupEnvironments()
|
||||
}
|
||||
|
||||
override fun apply(base: Statement, description: Description): Statement {
|
||||
return object : Statement() {
|
||||
override fun evaluate() {
|
||||
targetEnvironment = determineEnvironment(description)
|
||||
|
||||
targetEnvironments = determineEnvironments(description)
|
||||
base.evaluate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun determineEnvironment(description: Description): ApiEnvironment {
|
||||
private fun determineEnvironments(description: Description): Map<ApiConfig.ID, ApiEnvironment> {
|
||||
val instrumentationArgs = InstrumentationRegistry.getArguments()
|
||||
val envArg = instrumentationArgs.getString(ENV_ARGUMENT)
|
||||
val envConfigArg = instrumentationArgs.getString(ENV_CONFIGS_ARGUMENT)
|
||||
|
||||
if (!envArg.isNullOrEmpty()) return ApiEnvironment.valueOf(envArg.uppercase())
|
||||
if (!envConfigArg.isNullOrEmpty()) {
|
||||
return parseEnvironmentConfigs(envConfigArg)
|
||||
}
|
||||
|
||||
val methodAnnotation = description.getAnnotation(ApiEnv::class.java)
|
||||
if (methodAnnotation != null) return methodAnnotation.environment
|
||||
val methodEnvironments = collectApiEnvAnnotations(description)
|
||||
if (methodEnvironments.isNotEmpty()) {
|
||||
return DEFAULT_API_CONFIGS.associateWith { ApiEnvironment.MOCK } + methodEnvironments
|
||||
}
|
||||
|
||||
val classAnnotation = description.testClass.getAnnotation(ApiEnv::class.java)
|
||||
if (classAnnotation != null) return classAnnotation.environment
|
||||
val classEnvironments = collectApiEnvAnnotations(description.testClass)
|
||||
if (classEnvironments.isNotEmpty()) {
|
||||
return DEFAULT_API_CONFIGS.associateWith { ApiEnvironment.MOCK } + classEnvironments
|
||||
}
|
||||
|
||||
return ApiEnvironment.MOCK
|
||||
return DEFAULT_API_CONFIGS.associateWith { ApiEnvironment.MOCK }
|
||||
}
|
||||
|
||||
private fun MutableApiConfigsManager.setupEnvironment() {
|
||||
val environment = requireNotNull(targetEnvironment) { "Target environment is null" }
|
||||
private fun parseEnvironmentConfigs(envConfigArg: String): Map<ApiConfig.ID, ApiEnvironment> {
|
||||
return try {
|
||||
val parsedConfigs = envConfigArg.split(",")
|
||||
.map { it.trim() }
|
||||
.mapNotNull { configPair ->
|
||||
val parts = configPair.split("=").map { it.trim() }
|
||||
if (parts.size == 2) {
|
||||
try {
|
||||
val apiConfigId = ApiConfig.ID.valueOf(parts[0])
|
||||
val environment = ApiEnvironment.valueOf(parts[1])
|
||||
apiConfigId to environment
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.w("Invalid config or environment: $configPair")
|
||||
null
|
||||
}
|
||||
} else {
|
||||
Timber.w("Invalid config format: $configPair. Expected format: 'ConfigId=Environment'")
|
||||
null
|
||||
}
|
||||
}
|
||||
.toMap()
|
||||
|
||||
runBlocking { changeEnvironment(environment) }
|
||||
DEFAULT_API_CONFIGS.associateWith { ApiEnvironment.MOCK } + parsedConfigs
|
||||
} catch (e: Exception) {
|
||||
Timber.w("Failed to parse environment configs: $envConfigArg")
|
||||
DEFAULT_API_CONFIGS.associateWith { ApiEnvironment.MOCK }
|
||||
}
|
||||
}
|
||||
|
||||
Timber.i("API environment set to: ${environment.name}")
|
||||
private fun collectApiEnvAnnotations(description: Description): Map<ApiConfig.ID, ApiEnvironment> {
|
||||
return description.getAnnotation(ApiEnv::class.java)?.value
|
||||
?.associate { it.apiConfigId to it.environment } ?: emptyMap()
|
||||
}
|
||||
|
||||
private fun collectApiEnvAnnotations(testClass: Class<*>): Map<ApiConfig.ID, ApiEnvironment> {
|
||||
return testClass.getAnnotation(ApiEnv::class.java)?.value
|
||||
?.associate { it.apiConfigId to it.environment } ?: emptyMap()
|
||||
}
|
||||
|
||||
private fun MutableApiConfigsManager.setupEnvironments() {
|
||||
require(targetEnvironments.isNotEmpty()) { "Target environments map is empty" }
|
||||
|
||||
runBlocking {
|
||||
targetEnvironments.forEach { (apiConfigId, environment) ->
|
||||
changeEnvironment(apiConfigId.name, environment)
|
||||
Timber.i("$apiConfigId environment set to: ${environment.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val ENV_ARGUMENT = "testEnvironment"
|
||||
const val ENV_CONFIGS_ARGUMENT = "testEnvironmentConfigs"
|
||||
|
||||
val DEFAULT_API_CONFIGS = listOf(
|
||||
ApiConfig.ID.TangemTech,
|
||||
ApiConfig.ID.Express,
|
||||
ApiConfig.ID.TangemPay,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue