package com.tt.honeyDue import android.content.Context import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.tt.honeyDue.data.DataManager import com.tt.honeyDue.data.PersistenceManager import com.tt.honeyDue.fixtures.TestResidence import com.tt.honeyDue.fixtures.TestTask import com.tt.honeyDue.fixtures.TestUser import com.tt.honeyDue.models.LoginRequest import com.tt.honeyDue.models.RegisterRequest import com.tt.honeyDue.network.APILayer import com.tt.honeyDue.network.ApiResult import com.tt.honeyDue.storage.TaskCacheManager import com.tt.honeyDue.storage.TaskCacheStorage import com.tt.honeyDue.storage.ThemeStorageManager import com.tt.honeyDue.storage.TokenManager import kotlinx.coroutines.runBlocking import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue import org.junit.Before import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters /** * Phase 1 — Seed tests run sequentially before the parallel suites. * * Ports `iosApp/HoneyDueUITests/AAA_SeedTests.swift`. The AAA prefix keeps * these tests alphabetically first under JUnit's default sorter so seed * state (a verified test user, a residence, a task) exists before * `Suite*` tests run in parallel. `SuiteZZ_CleanupTests` (future) removes * the leftover data at the end of a run. * * These hit the real dev backend configured in `ApiConfig.CURRENT_ENV`. * If the backend is unreachable the tests fail fast — no silent skip. */ @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class AAA_SeedTests { private val testUser: TestUser = TestUser.seededTestUser() private val adminUser: TestUser = TestUser.seededAdminUser() @Before fun setUp() { val context = ApplicationProvider.getApplicationContext() if (!DataManager.isInitializedValue()) { // Mirror MainActivity.onCreate minus UI deps so APILayer has // everything it needs to persist the auth token. DataManager.initialize( tokenMgr = TokenManager.getInstance(context), themeMgr = ThemeStorageManager.getInstance(context), persistenceMgr = PersistenceManager.getInstance(context), ) } // Task cache is consulted during prefetchAllData — initialize to // avoid NPEs inside the APILayer success path. TaskCacheStorage.initialize(TaskCacheManager.getInstance(context)) } @Test fun a01_seedTestUserCreated() = runBlocking { // Try logging in first; account may already exist on the dev backend. val loginResult = APILayer.login( LoginRequest(username = testUser.username, password = testUser.password), ) if (loginResult is ApiResult.Success) { assertNotNull("Auth token must be populated after login", loginResult.data.token) return@runBlocking } val registerResult = APILayer.register( RegisterRequest( username = testUser.username, email = testUser.email, password = testUser.password, firstName = testUser.firstName, lastName = testUser.lastName, ), ) assertTrue( "Expected to create seed testuser; got $registerResult", registerResult is ApiResult.Success, ) } @Test fun a02_seedAdminUserExists() = runBlocking { val loginResult = APILayer.login( LoginRequest(username = adminUser.username, password = adminUser.password), ) if (loginResult is ApiResult.Success) { assertNotNull("Auth token populated for admin login", loginResult.data.token) return@runBlocking } val registerResult = APILayer.register( RegisterRequest( username = adminUser.username, email = adminUser.email, password = adminUser.password, firstName = adminUser.firstName, lastName = adminUser.lastName, ), ) assertTrue( "Expected to create seed admin; got $registerResult", registerResult is ApiResult.Success, ) } @Test fun a03_seedResidenceCreated() = runBlocking { // Ensure we have a session for the test user. val loginResult = APILayer.login( LoginRequest(username = testUser.username, password = testUser.password), ) assertTrue( "Must be logged in as testuser before creating residence", loginResult is ApiResult.Success, ) val residenceResult = APILayer.createResidence( TestResidence.house().toCreateRequest(), ) assertTrue( "Expected to create seed residence; got $residenceResult", residenceResult is ApiResult.Success, ) } @Test fun a04_seedTaskCreatedOnResidence() = runBlocking { val loginResult = APILayer.login( LoginRequest(username = testUser.username, password = testUser.password), ) assertTrue( "Must be logged in as testuser before creating task", loginResult is ApiResult.Success, ) // Use the first residence that comes back from `prefetchAllData`, which // APILayer.login already kicked off. Fall back to creating one. val residences = DataManager.residences.value val residenceId = residences.firstOrNull()?.id ?: run { val create = APILayer.createResidence(TestResidence.house().toCreateRequest()) (create as? ApiResult.Success)?.data?.id ?: error("Cannot create residence for task seed: $create") } val taskResult = APILayer.createTask( TestTask.basic(residenceId = residenceId).toCreateRequest(), ) assertTrue( "Expected to create seed task; got $taskResult", taskResult is ApiResult.Success, ) } // ---- Helpers ---- private fun DataManager.isInitializedValue(): Boolean { // DataManager exposes `isInitialized` as a StateFlow. return try { val field = DataManager::class.java.getDeclaredField("_isInitialized") field.isAccessible = true @Suppress("UNCHECKED_CAST") val flow = field.get(DataManager) as kotlinx.coroutines.flow.MutableStateFlow flow.value } catch (e: Throwable) { false } } }