mirror of
https://github.com/usetrmnl/trmnl-android.git
synced 2026-04-29 13:35:26 -07:00
Remove USE_FAKE_API infrastructure to reduce maintenance overhead
- Remove buildConfigField("Boolean", "USE_FAKE_API") from both debug and release build types
- Remove validateUseFakeApiConfig Gradle task (saved ~80 lines)
- Delete RepositoryConfigProvider.kt
- Delete app/src/main/java/ink/trmnl/android/data/fake/ directory
- Remove fake data conditional logic from TrmnlDisplayRepository
- Remove fake data conditional logic from TrmnlUserRepository
- Remove validation step from .github/workflows/android-release.yml
- Remove usesFakeApiData from AppSettingsScreen state and UI
- Remove FakeApiInfoBanner composable
- Update tests to remove fake data test cases
- Update CLAUDE.md documentation
All tests pass (89 tests), linting clean, build successful.
Co-authored-by: hossain-khan <99822+hossain-khan@users.noreply.github.com>
This commit is contained in:
co-authored by
hossain-khan
parent
c861b1fde3
commit
d94b70ffba
@@ -53,9 +53,6 @@ jobs:
|
||||
run: |
|
||||
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > keystore/trmnl-app-release.keystore
|
||||
|
||||
- name: Validate USE_FAKE_API configuration
|
||||
run: ./gradlew validateUseFakeApiConfig
|
||||
|
||||
- name: Build Release APK and AAB
|
||||
run: ./gradlew assembleRelease bundleRelease
|
||||
env:
|
||||
|
||||
@@ -172,13 +172,10 @@ The project uses optimized Gradle settings based on best practices from the [Now
|
||||
**Note:** This project uses only build TYPES (debug/release), NOT product flavors.
|
||||
|
||||
**Debug:**
|
||||
- `buildConfigField("Boolean", "USE_FAKE_API", "true")`
|
||||
- Uses debug keystore with password "android" (included in repo)
|
||||
- Fake API can be overridden in `RepositoryConfigProvider`
|
||||
- HttpLoggingInterceptor enabled with BODY level
|
||||
|
||||
**Release:**
|
||||
- `buildConfigField("Boolean", "USE_FAKE_API", "false")`
|
||||
- Requires production keystore from CI secrets or `secret.properties`
|
||||
- Keystore env vars: `KEYSTORE_PASSWORD`, `KEY_ALIAS`
|
||||
- Code shrinking: `isMinifyEnabled = true`, `isShrinkResources = true`
|
||||
|
||||
@@ -75,11 +75,6 @@ android {
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// Always force `USE_FAKE_API` to `false` for release builds
|
||||
// See https://github.com/usetrmnl/trmnl-android/issues/16
|
||||
// ℹ️ To override during local development, change the value in `RepositoryConfigProvider`
|
||||
buildConfigField("Boolean", "USE_FAKE_API", "false")
|
||||
|
||||
// Enables code shrinking, obfuscation, and optimization
|
||||
// See https://github.com/usetrmnl/trmnl-android/issues/199
|
||||
isMinifyEnabled = true
|
||||
@@ -99,13 +94,6 @@ android {
|
||||
}
|
||||
|
||||
debug {
|
||||
// Allow developers to configure this value for debug builds
|
||||
// Use fake API response for local development and testing purposes.
|
||||
// ℹ️ To override during local development, change this value to `"false"`
|
||||
// or, you can change the value in the `RepositoryConfigProvider`
|
||||
// Ideally, this value should always be set to `true` for debug builds.
|
||||
buildConfigField("Boolean", "USE_FAKE_API", "true")
|
||||
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
}
|
||||
}
|
||||
@@ -221,83 +209,3 @@ ksp {
|
||||
tasks.withType<Test> {
|
||||
jvmArgs("-XX:+EnableDynamicAgentLoading")
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation task to ensure USE_FAKE_API is correctly configured for release builds.
|
||||
* This task will fail the build if:
|
||||
* 1. Release build type has USE_FAKE_API set to anything other than "false"
|
||||
* 2. RepositoryConfigProvider doesn't return `BuildConfig.USE_FAKE_API`
|
||||
*
|
||||
* Run manually: ./gradlew validateUseFakeApiConfig
|
||||
* Automatically runs before: assembleRelease, bundleRelease
|
||||
*/
|
||||
val validateUseFakeApiConfig = tasks.register("validateUseFakeApiConfig") {
|
||||
group = "verification"
|
||||
description = "Validates that USE_FAKE_API is correctly configured for release builds"
|
||||
|
||||
// Capture file paths at configuration time to avoid configuration cache issues
|
||||
val buildGradleFile = project.file("build.gradle.kts")
|
||||
val repositoryConfigFile = project.file("src/main/java/ink/trmnl/android/data/RepositoryConfigProvider.kt")
|
||||
|
||||
doLast {
|
||||
println("🔍 Validating USE_FAKE_API configuration...")
|
||||
|
||||
// Check 1: Validate build.gradle.kts release configuration
|
||||
val buildGradleContent = buildGradleFile.readText()
|
||||
|
||||
// Find the release block and check USE_FAKE_API value
|
||||
val releaseBuildPattern = Regex(
|
||||
"""release\s*\{[^}]*buildConfigField\s*\(\s*"Boolean"\s*,\s*"USE_FAKE_API"\s*,\s*"false"\s*\)""",
|
||||
RegexOption.DOT_MATCHES_ALL
|
||||
)
|
||||
|
||||
if (!releaseBuildPattern.containsMatchIn(buildGradleContent)) {
|
||||
throw GradleException(
|
||||
"""
|
||||
❌ BUILD VALIDATION FAILED!
|
||||
|
||||
Release build type MUST have USE_FAKE_API set to "false"
|
||||
Expected: buildConfigField("Boolean", "USE_FAKE_API", "false")
|
||||
|
||||
Location: app/build.gradle.kts (release buildType)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
// Check 2: Validate RepositoryConfigProvider.kt
|
||||
if (repositoryConfigFile.exists()) {
|
||||
val repositoryConfigContent = repositoryConfigFile.readText()
|
||||
|
||||
// Check that shouldUseFakeData returns BuildConfig.USE_FAKE_API
|
||||
val correctReturnPattern = Regex(
|
||||
"""val\s+shouldUseFakeData\s*:\s*Boolean[^}]*return\s+BuildConfig\.USE_FAKE_API""",
|
||||
RegexOption.DOT_MATCHES_ALL
|
||||
)
|
||||
|
||||
if (!correctReturnPattern.containsMatchIn(repositoryConfigContent)) {
|
||||
throw GradleException(
|
||||
"""
|
||||
❌ BUILD VALIDATION FAILED!
|
||||
|
||||
RepositoryConfigProvider.shouldUseFakeData MUST return BuildConfig.USE_FAKE_API
|
||||
Expected: return BuildConfig.USE_FAKE_API
|
||||
|
||||
Location: app/src/main/java/ink/trmnl/android/data/RepositoryConfigProvider.kt
|
||||
|
||||
Do NOT hardcode or override this value in committed code!
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
} else {
|
||||
println("⚠️ RepositoryConfigProvider.kt not found, skipping validation")
|
||||
}
|
||||
|
||||
println("✅ USE_FAKE_API configuration is valid")
|
||||
}
|
||||
}
|
||||
|
||||
// Automatically run validation before release builds (after Android tasks are registered)
|
||||
afterEvaluate {
|
||||
tasks.findByName("assembleRelease")?.dependsOn(validateUseFakeApiConfig)
|
||||
tasks.findByName("bundleRelease")?.dependsOn(validateUseFakeApiConfig)
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package ink.trmnl.android.data
|
||||
|
||||
import ink.trmnl.android.BuildConfig
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Provides configuration for repository data sources. This class helps control whether repositories
|
||||
* should use fake/mock data instead of real API data, which is useful for development and testing.
|
||||
*/
|
||||
class RepositoryConfigProvider
|
||||
@Inject
|
||||
constructor() {
|
||||
/**
|
||||
* Indicates if the app should use fake data instead of real API responses.
|
||||
*
|
||||
* @return Boolean value from [BuildConfig.USE_FAKE_API]
|
||||
*/
|
||||
val shouldUseFakeData: Boolean
|
||||
get() {
|
||||
// To change this value, update the `buildConfigField` in the app's build.gradle file
|
||||
// Or, change the value here for local development. Do not commit this change.
|
||||
// ⚠️ Return value should always be `BuildConfig.USE_FAKE_API`
|
||||
return BuildConfig.USE_FAKE_API
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,6 @@ package ink.trmnl.android.data
|
||||
import com.slack.eithernet.ApiResult
|
||||
import com.slack.eithernet.exceptionOrNull
|
||||
import com.squareup.anvil.annotations.optional.SingleIn
|
||||
import ink.trmnl.android.BuildConfig.USE_FAKE_API
|
||||
import ink.trmnl.android.data.fake.generateFakeDeviceSetupInfo
|
||||
import ink.trmnl.android.data.fake.generateFakeTrmnlDisplayInfo
|
||||
import ink.trmnl.android.di.AppScope
|
||||
import ink.trmnl.android.model.SupportedDeviceModel
|
||||
import ink.trmnl.android.model.TrmnlDeviceConfig
|
||||
@@ -27,12 +24,6 @@ import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Repository class responsible for fetching and mapping display data.
|
||||
*
|
||||
* ⚠️ NOTE: [USE_FAKE_API] is set to `true` in debug builds, meaning it will
|
||||
* use mock data and avoid network calls. In release builds, it is set to `false`
|
||||
* to enable real API calls.
|
||||
*
|
||||
* You can override this behavior by updating [RepositoryConfigProvider.shouldUseFakeData] for local development.
|
||||
*/
|
||||
@SingleIn(AppScope::class)
|
||||
class TrmnlDisplayRepository
|
||||
@@ -40,12 +31,10 @@ class TrmnlDisplayRepository
|
||||
constructor(
|
||||
private val apiService: TrmnlApiService,
|
||||
private val imageMetadataStore: ImageMetadataStore,
|
||||
private val repositoryConfigProvider: RepositoryConfigProvider,
|
||||
private val androidDeviceInfoProvider: AndroidDeviceInfoProvider,
|
||||
) {
|
||||
/**
|
||||
* Fetches display data for next plugin from the server using the provided access token.
|
||||
* If the app is in debug mode, it uses mock data instead.
|
||||
*
|
||||
* @param trmnlDeviceConfig Device configuration containing the access token and other settings.
|
||||
* @return A [TrmnlDisplayInfo] object containing the display data.
|
||||
@@ -53,11 +42,6 @@ class TrmnlDisplayRepository
|
||||
suspend fun getNextDisplayData(trmnlDeviceConfig: TrmnlDeviceConfig): TrmnlDisplayInfo {
|
||||
Timber.i("Fetching next playlist item display data from server for device: ${trmnlDeviceConfig.type}")
|
||||
|
||||
if (repositoryConfigProvider.shouldUseFakeData) {
|
||||
// Avoid using real API in debug mode
|
||||
return generateFakeTrmnlDisplayInfo(imageMetadataStore = imageMetadataStore, apiUsed = "next-image")
|
||||
}
|
||||
|
||||
val result =
|
||||
apiService
|
||||
.getNextDisplayData(
|
||||
@@ -126,7 +110,6 @@ class TrmnlDisplayRepository
|
||||
|
||||
/**
|
||||
* Fetches the current display data from the server using the provided access token.
|
||||
* If the app is in debug mode, it uses mock data instead.
|
||||
*
|
||||
* ⚠️ NOTE: This API is not available on BYOS servers.
|
||||
* See https://discord.com/channels/1281055965508141100/1331360842809348106/1382863253880963124
|
||||
@@ -141,11 +124,6 @@ class TrmnlDisplayRepository
|
||||
Timber.w("Current display image data API is not available for BYOS service.")
|
||||
}
|
||||
|
||||
if (repositoryConfigProvider.shouldUseFakeData) {
|
||||
// Avoid using real API in debug mode
|
||||
return generateFakeTrmnlDisplayInfo(imageMetadataStore = imageMetadataStore, apiUsed = "current-image")
|
||||
}
|
||||
|
||||
val result =
|
||||
apiService
|
||||
.getCurrentDisplayData(
|
||||
@@ -197,11 +175,6 @@ class TrmnlDisplayRepository
|
||||
Timber.w("Device setup is only applicable for BYOS devices.")
|
||||
}
|
||||
|
||||
if (repositoryConfigProvider.shouldUseFakeData) {
|
||||
// Avoid using real API in debug mode
|
||||
return generateFakeDeviceSetupInfo()
|
||||
}
|
||||
|
||||
val result =
|
||||
apiService.setupNewDevice(
|
||||
fullApiUrl = constructApiUrl(trmnlDeviceConfig.apiBaseUrl, TrmnlApiService.SETUP_API_PATH),
|
||||
|
||||
@@ -33,7 +33,6 @@ class TrmnlUserRepository
|
||||
@Inject
|
||||
constructor(
|
||||
private val userApiService: TrmnlUserApiService,
|
||||
private val repositoryConfigProvider: RepositoryConfigProvider,
|
||||
private val androidDeviceInfoProvider: AndroidDeviceInfoProvider,
|
||||
) {
|
||||
/**
|
||||
@@ -52,23 +51,6 @@ class TrmnlUserRepository
|
||||
): Result<TrmnlUser> {
|
||||
Timber.i("Validating user API token")
|
||||
|
||||
if (repositoryConfigProvider.shouldUseFakeData) {
|
||||
// Return fake user data in debug mode
|
||||
return Result.success(
|
||||
TrmnlUser(
|
||||
id = 42,
|
||||
name = "Test User",
|
||||
email = "test@example.com",
|
||||
firstName = "Test",
|
||||
lastName = "User",
|
||||
locale = "en",
|
||||
timeZone = "Eastern Time (US & Canada)",
|
||||
timeZoneIana = "America/New_York",
|
||||
utcOffset = -14400,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
val result =
|
||||
userApiService.getUserInfo(
|
||||
fullApiUrl = constructApiUrl(apiBaseUrl, USER_INFO_API_PATH),
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package ink.trmnl.android.data.fake
|
||||
|
||||
import ink.trmnl.android.data.DeviceSetupInfo
|
||||
import ink.trmnl.android.data.ImageMetadataStore
|
||||
import ink.trmnl.android.data.RepositoryConfigProvider
|
||||
import ink.trmnl.android.data.TrmnlDisplayInfo
|
||||
import ink.trmnl.android.model.TrmnlDeviceType
|
||||
import ink.trmnl.android.util.HTTP_200
|
||||
import timber.log.Timber
|
||||
|
||||
// Contains fake implementations of network communication for local development
|
||||
// and testing without depending on real servers.
|
||||
|
||||
/**
|
||||
* Generates fake display info for debugging purposes without wasting an API request.
|
||||
*
|
||||
* ℹ️ This is only used when [RepositoryConfigProvider.shouldUseFakeData] is true.
|
||||
*/
|
||||
internal suspend fun generateFakeTrmnlDisplayInfo(
|
||||
imageMetadataStore: ImageMetadataStore,
|
||||
apiUsed: String,
|
||||
): TrmnlDisplayInfo {
|
||||
Timber.d("DEBUG: Using mock data for display info")
|
||||
val timestampMin = System.currentTimeMillis() / 60_000 // Changes every minute
|
||||
val mockImageUrl = "https://picsum.photos/300/200?grayscale&time=$timestampMin&api=$apiUsed"
|
||||
val mockRefreshRate = 600L
|
||||
|
||||
// Save mock data to the data store
|
||||
imageMetadataStore.saveImageMetadata(mockImageUrl, mockRefreshRate)
|
||||
|
||||
return TrmnlDisplayInfo(
|
||||
status = HTTP_200,
|
||||
trmnlDeviceType = TrmnlDeviceType.TRMNL,
|
||||
imageUrl = mockImageUrl,
|
||||
imageFileName = "mocked-image-" + mockImageUrl.substringAfterLast('?'),
|
||||
error = null,
|
||||
refreshIntervalSeconds = mockRefreshRate,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates fake setup info for debugging purposes without wasting an API request.
|
||||
*
|
||||
* ℹ️ This is only used when [RepositoryConfigProvider.shouldUseFakeData] is true.
|
||||
*/
|
||||
internal fun generateFakeDeviceSetupInfo(): DeviceSetupInfo =
|
||||
DeviceSetupInfo(
|
||||
success = true,
|
||||
deviceMacId = "A1:B2:C3:D4:E5:F6",
|
||||
apiKey = "mocked-api-key-${System.currentTimeMillis()}",
|
||||
message = "Mocked device setup successful",
|
||||
)
|
||||
@@ -5,7 +5,6 @@ import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -60,7 +59,6 @@ import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
@@ -85,7 +83,6 @@ import ink.trmnl.android.R
|
||||
import ink.trmnl.android.data.AppConfig.DEFAULT_REFRESH_INTERVAL_SEC
|
||||
import ink.trmnl.android.data.AppConfig.TRMNL_API_SERVER_BASE_URL
|
||||
import ink.trmnl.android.data.DeviceSetupInfo
|
||||
import ink.trmnl.android.data.RepositoryConfigProvider
|
||||
import ink.trmnl.android.data.TrmnlDeviceConfigDataStore
|
||||
import ink.trmnl.android.data.TrmnlDisplayRepository
|
||||
import ink.trmnl.android.di.AppScope
|
||||
@@ -144,7 +141,6 @@ data class AppSettingsScreen(
|
||||
val accessToken: String,
|
||||
val deviceMacId: String,
|
||||
val isByodMasterDevice: Boolean,
|
||||
val usesFakeApiData: Boolean,
|
||||
val isLoading: Boolean = false,
|
||||
val validationResult: ValidationResult? = null,
|
||||
val isDeviceSetupLoading: Boolean = false,
|
||||
@@ -273,7 +269,6 @@ class AppSettingsPresenter
|
||||
private val deviceConfigStore: TrmnlDeviceConfigDataStore,
|
||||
private val trmnlWorkScheduler: TrmnlWorkScheduler,
|
||||
private val trmnlImageUpdateManager: TrmnlImageUpdateManager,
|
||||
private val repositoryConfigProvider: RepositoryConfigProvider,
|
||||
) : Presenter<AppSettingsScreen.State> {
|
||||
@Composable
|
||||
override fun present(): AppSettingsScreen.State {
|
||||
@@ -286,7 +281,6 @@ class AppSettingsPresenter
|
||||
var validationResult by remember { mutableStateOf<ValidationResult?>(null) }
|
||||
var isDeviceSetupLoading by remember { mutableStateOf(false) }
|
||||
var deviceSetupMessage by remember { mutableStateOf<String?>(null) }
|
||||
val usesFakeApiData = repositoryConfigProvider.shouldUseFakeData
|
||||
val scope = rememberCoroutineScope()
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
@@ -354,7 +348,6 @@ class AppSettingsPresenter
|
||||
accessToken = accessToken,
|
||||
deviceMacId = deviceMacId,
|
||||
isByodMasterDevice = isByodMasterDevice,
|
||||
usesFakeApiData = usesFakeApiData,
|
||||
isLoading = isLoading,
|
||||
validationResult = validationResult,
|
||||
isDeviceSetupLoading = isDeviceSetupLoading,
|
||||
@@ -668,15 +661,6 @@ fun AppSettingsContent(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
if (state.usesFakeApiData) {
|
||||
FakeApiInfoBanner(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 24.dp),
|
||||
)
|
||||
}
|
||||
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.trmnl_logo_plain),
|
||||
contentDescription = "TRMNL Logo",
|
||||
@@ -1298,52 +1282,6 @@ private fun WorkScheduleStatusCard(
|
||||
* A composable function that displays a banner indicating that the app is in developer mode
|
||||
* and is using mock data instead of real API calls.
|
||||
*/
|
||||
@Composable
|
||||
private fun FakeApiInfoBanner(modifier: Modifier = Modifier) {
|
||||
Card(
|
||||
modifier = modifier,
|
||||
colors =
|
||||
CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer,
|
||||
),
|
||||
border = BorderStroke(1.dp, MaterialTheme.colorScheme.tertiary),
|
||||
) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Warning,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
|
||||
Column {
|
||||
Text(
|
||||
text = "Developer Mode - Using Fake API",
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "This app is currently using mock data instead of real API calls.",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Set `BuildConfig.USE_FAKE_API` to `false` using build.gradle to use real API.",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "App Settings Content - Initial State")
|
||||
@Composable
|
||||
private fun PreviewAppSettingsContentInitial() {
|
||||
@@ -1356,7 +1294,6 @@ private fun PreviewAppSettingsContentInitial() {
|
||||
accessToken = "",
|
||||
deviceMacId = "aa:bb:cc:dd:ee:ff",
|
||||
isByodMasterDevice = true,
|
||||
usesFakeApiData = true,
|
||||
isLoading = false,
|
||||
validationResult = null,
|
||||
nextRefreshJobInfo = null,
|
||||
@@ -1378,7 +1315,6 @@ private fun PreviewAppSettingsContentLoading() {
|
||||
accessToken = "some-token",
|
||||
deviceMacId = "aa:bb:cc:dd:ee:ff",
|
||||
isByodMasterDevice = true,
|
||||
usesFakeApiData = false,
|
||||
isLoading = true,
|
||||
validationResult = null,
|
||||
nextRefreshJobInfo = null,
|
||||
@@ -1400,7 +1336,6 @@ private fun PreviewAppSettingsContentSuccess() {
|
||||
accessToken = "valid-token-123",
|
||||
deviceMacId = "aa:bb:cc:dd:ee:ff",
|
||||
isByodMasterDevice = true,
|
||||
usesFakeApiData = false,
|
||||
isLoading = false,
|
||||
validationResult =
|
||||
ValidationResult.Success(
|
||||
@@ -1426,7 +1361,6 @@ private fun PreviewAppSettingsContentFailure() {
|
||||
accessToken = "invalid-token",
|
||||
deviceMacId = "aa:bb:cc:dd:ee:ff",
|
||||
isByodMasterDevice = true,
|
||||
usesFakeApiData = false,
|
||||
isLoading = false,
|
||||
validationResult =
|
||||
ValidationResult.Failure(
|
||||
@@ -1455,7 +1389,6 @@ private fun PreviewAppSettingsContentWithWork() {
|
||||
accessToken = "valid-token-123",
|
||||
deviceMacId = "aa:bb:cc:dd:ee:ff",
|
||||
isByodMasterDevice = true,
|
||||
usesFakeApiData = false,
|
||||
isLoading = false,
|
||||
validationResult = null, // Can also be Success state
|
||||
nextRefreshJobInfo =
|
||||
@@ -1487,7 +1420,6 @@ private fun PreviewWorkScheduleStatusCardScheduled() {
|
||||
accessToken = "some-token",
|
||||
deviceMacId = "AA:BB:CC:DD:EE:FF",
|
||||
isByodMasterDevice = true,
|
||||
usesFakeApiData = false,
|
||||
nextRefreshJobInfo =
|
||||
NextImageRefreshDisplayInfo(
|
||||
workerState = WorkInfo.State.ENQUEUED,
|
||||
@@ -1513,7 +1445,6 @@ private fun PreviewWorkScheduleStatusCardNoWork() {
|
||||
accessToken = "some-token",
|
||||
deviceMacId = "aa:bb:cc:dd:ee:ff",
|
||||
isByodMasterDevice = true,
|
||||
usesFakeApiData = false,
|
||||
nextRefreshJobInfo = null,
|
||||
eventSink = {},
|
||||
),
|
||||
@@ -1521,14 +1452,6 @@ private fun PreviewWorkScheduleStatusCardNoWork() {
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "Fake API Info Banner")
|
||||
@Composable
|
||||
private fun PreviewFakeApiInfoBanner() {
|
||||
TrmnlDisplayAppTheme {
|
||||
FakeApiInfoBanner()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "App Settings Content - BYOD Selected")
|
||||
@Composable
|
||||
private fun PreviewAppSettingsContentByod() {
|
||||
@@ -1541,7 +1464,6 @@ private fun PreviewAppSettingsContentByod() {
|
||||
accessToken = "byod-access-token-here",
|
||||
deviceMacId = "",
|
||||
isByodMasterDevice = false,
|
||||
usesFakeApiData = false,
|
||||
isLoading = false,
|
||||
validationResult = null,
|
||||
nextRefreshJobInfo = null,
|
||||
@@ -1564,7 +1486,6 @@ private fun PreviewAppSettingsContentByos() {
|
||||
accessToken = "byos-access-token-here",
|
||||
deviceMacId = "AA:BB:CC:DD:EE:FF",
|
||||
isByodMasterDevice = true,
|
||||
usesFakeApiData = false,
|
||||
isLoading = false,
|
||||
validationResult = null,
|
||||
nextRefreshJobInfo = null,
|
||||
|
||||
@@ -30,7 +30,6 @@ class TrmnlDisplayRepositoryTest {
|
||||
private lateinit var repository: TrmnlDisplayRepository
|
||||
private lateinit var apiService: TrmnlApiService
|
||||
private lateinit var imageMetadataStore: ImageMetadataStore
|
||||
private lateinit var repositoryConfigProvider: RepositoryConfigProvider
|
||||
private lateinit var deviceConfigDataStore: TrmnlDeviceConfigDataStore
|
||||
private lateinit var androidDeviceInfoProvider: AndroidDeviceInfoProvider
|
||||
|
||||
@@ -61,18 +60,14 @@ class TrmnlDisplayRepositoryTest {
|
||||
@Before
|
||||
fun setup() {
|
||||
apiService = mockk()
|
||||
repositoryConfigProvider = mockk()
|
||||
deviceConfigDataStore = mockk()
|
||||
imageMetadataStore = mockk(relaxed = true)
|
||||
androidDeviceInfoProvider = mockk(relaxed = true)
|
||||
|
||||
every { repositoryConfigProvider.shouldUseFakeData } returns false
|
||||
|
||||
repository =
|
||||
TrmnlDisplayRepository(
|
||||
apiService = apiService,
|
||||
imageMetadataStore = imageMetadataStore,
|
||||
repositoryConfigProvider = repositoryConfigProvider,
|
||||
androidDeviceInfoProvider = androidDeviceInfoProvider,
|
||||
)
|
||||
}
|
||||
@@ -242,46 +237,6 @@ class TrmnlDisplayRepositoryTest {
|
||||
coVerify(exactly = 0) { imageMetadataStore.saveImageMetadata(any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getNextDisplayData should return fake data when shouldUseFakeData is true`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
every { repositoryConfigProvider.shouldUseFakeData } returns true
|
||||
|
||||
// Act
|
||||
val result = repository.getNextDisplayData(testDeviceConfig)
|
||||
|
||||
// Assert
|
||||
assertThat(result.status).isEqualTo(200)
|
||||
assertThat(result.imageUrl).contains("picsum.photos")
|
||||
assertThat(result.imageFileName).contains("mocked-image-grayscale&time")
|
||||
assertThat(result.refreshIntervalSeconds).isEqualTo(600L)
|
||||
assertThat(result.error).isNull()
|
||||
|
||||
// Verify API was NOT called
|
||||
coVerify(exactly = 0) { apiService.getNextDisplayData(any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getCurrentDisplayData should return fake data when shouldUseFakeData is true`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
every { repositoryConfigProvider.shouldUseFakeData } returns true
|
||||
|
||||
// Act
|
||||
val result = repository.getCurrentDisplayData(testDeviceConfig)
|
||||
|
||||
// Assert
|
||||
assertThat(result.status).isEqualTo(200)
|
||||
assertThat(result.imageUrl).contains("picsum.photos")
|
||||
assertThat(result.imageFileName).contains("mocked-image-grayscale&time")
|
||||
assertThat(result.refreshIntervalSeconds).isEqualTo(600L)
|
||||
assertThat(result.error).isNull()
|
||||
|
||||
// Verify API was NOT called
|
||||
coVerify(exactly = 0) { apiService.getCurrentDisplayData(any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `constructApiUrl should handle URLs with and without trailing slashes`() =
|
||||
runTest {
|
||||
|
||||
Reference in New Issue
Block a user