(fix) Umbau zu SCS
### API-Gateway erweitern
- Bestehenden API-Gateway-Service mit zusätzlichen Funktionen ausstatten:
- Rate Limiting implementieren
- Request/Response Logging verbessern
This commit is contained in:
+182
-77
@@ -12,6 +12,17 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.*
|
||||
import kotlin.test.*
|
||||
|
||||
/**
|
||||
* Comprehensive test suite for the CreatePersonViewModel.
|
||||
*
|
||||
* Tests cover:
|
||||
* - Initial state verification
|
||||
* - Field update operations
|
||||
* - Form validation
|
||||
* - Person creation with various inputs
|
||||
* - Form reset functionality
|
||||
* - Error handling
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class CreatePersonViewModelTest {
|
||||
|
||||
@@ -26,6 +37,25 @@ class CreatePersonViewModelTest {
|
||||
fun setup() {
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
|
||||
// Initialize mock repositories and services
|
||||
setupMockRepositories()
|
||||
|
||||
// Create the use case with mocks
|
||||
createPersonUseCase = CreatePersonUseCase(
|
||||
personRepository = mockPersonRepository,
|
||||
vereinRepository = mockVereinRepository,
|
||||
masterDataService = mockMasterDataService
|
||||
)
|
||||
|
||||
// Initialize the view model
|
||||
viewModel = CreatePersonViewModel(createPersonUseCase)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up all mock repositories and services needed for testing
|
||||
*/
|
||||
private fun setupMockRepositories() {
|
||||
// Mock person repository with in-memory storage
|
||||
mockPersonRepository = object : PersonRepository {
|
||||
private val persons = mutableListOf<DomPerson>()
|
||||
|
||||
@@ -71,6 +101,7 @@ class CreatePersonViewModelTest {
|
||||
}
|
||||
}
|
||||
|
||||
// Mock verein repository (minimal implementation)
|
||||
mockVereinRepository = object : VereinRepository {
|
||||
override suspend fun findById(id: com.benasher44.uuid.Uuid): at.mocode.members.domain.model.DomVerein? {
|
||||
return null
|
||||
@@ -121,6 +152,7 @@ class CreatePersonViewModelTest {
|
||||
}
|
||||
}
|
||||
|
||||
// Mock master data service (minimal implementation)
|
||||
mockMasterDataService = object : MasterDataService {
|
||||
override suspend fun countryExists(countryId: com.benasher44.uuid.Uuid): Boolean {
|
||||
return true
|
||||
@@ -146,14 +178,6 @@ class CreatePersonViewModelTest {
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
createPersonUseCase = CreatePersonUseCase(
|
||||
personRepository = mockPersonRepository,
|
||||
vereinRepository = mockVereinRepository,
|
||||
masterDataService = mockMasterDataService
|
||||
)
|
||||
|
||||
viewModel = CreatePersonViewModel(createPersonUseCase)
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
@@ -161,47 +185,84 @@ class CreatePersonViewModelTest {
|
||||
Dispatchers.resetMain()
|
||||
}
|
||||
|
||||
//region Initial State Tests
|
||||
|
||||
@Test
|
||||
fun `initial state should be correct`() {
|
||||
assertEquals("", viewModel.nachname)
|
||||
assertEquals("", viewModel.vorname)
|
||||
assertEquals("", viewModel.titel)
|
||||
assertEquals("", viewModel.oepsSatzNr)
|
||||
assertEquals("", viewModel.geburtsdatum)
|
||||
assertNull(viewModel.geschlecht)
|
||||
assertEquals("", viewModel.telefon)
|
||||
assertEquals("", viewModel.email)
|
||||
assertEquals("", viewModel.strasse)
|
||||
assertEquals("", viewModel.plz)
|
||||
assertEquals("", viewModel.ort)
|
||||
assertEquals("", viewModel.adresszusatz)
|
||||
assertEquals("", viewModel.feiId)
|
||||
assertEquals("", viewModel.mitgliedsNummer)
|
||||
assertEquals("", viewModel.notizen)
|
||||
assertFalse(viewModel.istGesperrt)
|
||||
assertEquals("", viewModel.sperrGrund)
|
||||
assertFalse(viewModel.isLoading)
|
||||
assertNull(viewModel.errorMessage)
|
||||
assertFalse(viewModel.isSuccess)
|
||||
// Verify all fields are initialized to empty values
|
||||
assertEquals("", viewModel.nachname, "Nachname should be empty initially")
|
||||
assertEquals("", viewModel.vorname, "Vorname should be empty initially")
|
||||
assertEquals("", viewModel.titel, "Titel should be empty initially")
|
||||
assertEquals("", viewModel.oepsSatzNr, "OepsSatzNr should be empty initially")
|
||||
assertEquals("", viewModel.geburtsdatum, "Geburtsdatum should be empty initially")
|
||||
assertNull(viewModel.geschlecht, "Geschlecht should be null initially")
|
||||
assertEquals("", viewModel.telefon, "Telefon should be empty initially")
|
||||
assertEquals("", viewModel.email, "Email should be empty initially")
|
||||
assertEquals("", viewModel.strasse, "Strasse should be empty initially")
|
||||
assertEquals("", viewModel.plz, "PLZ should be empty initially")
|
||||
assertEquals("", viewModel.ort, "Ort should be empty initially")
|
||||
assertEquals("", viewModel.adresszusatz, "Adresszusatz should be empty initially")
|
||||
assertEquals("", viewModel.feiId, "FeiId should be empty initially")
|
||||
assertEquals("", viewModel.mitgliedsNummer, "MitgliedsNummer should be empty initially")
|
||||
assertEquals("", viewModel.notizen, "Notizen should be empty initially")
|
||||
|
||||
// Verify flags are initialized correctly
|
||||
assertFalse(viewModel.istGesperrt, "IstGesperrt should be false initially")
|
||||
assertEquals("", viewModel.sperrGrund, "SperrGrund should be empty initially")
|
||||
assertFalse(viewModel.isLoading, "IsLoading should be false initially")
|
||||
assertNull(viewModel.errorMessage, "ErrorMessage should be null initially")
|
||||
assertFalse(viewModel.isSuccess, "IsSuccess should be false initially")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Update Method Tests
|
||||
|
||||
@Test
|
||||
fun `update methods should change state correctly`() {
|
||||
// When - update multiple fields
|
||||
viewModel.updateNachname("Mustermann")
|
||||
viewModel.updateVorname("Max")
|
||||
viewModel.updateTitel("Dr.")
|
||||
viewModel.updateGeschlecht(GeschlechtE.M)
|
||||
viewModel.updateEmail("max@example.com")
|
||||
viewModel.updateIstGesperrt(true)
|
||||
viewModel.updateSperrGrund("Test Sperrgrund")
|
||||
|
||||
assertEquals("Mustermann", viewModel.nachname)
|
||||
assertEquals("Max", viewModel.vorname)
|
||||
assertEquals("Dr.", viewModel.titel)
|
||||
assertEquals(GeschlechtE.M, viewModel.geschlecht)
|
||||
assertEquals("max@example.com", viewModel.email)
|
||||
assertTrue(viewModel.istGesperrt)
|
||||
// Then - verify all fields were updated correctly
|
||||
assertEquals("Mustermann", viewModel.nachname, "Nachname should be updated")
|
||||
assertEquals("Max", viewModel.vorname, "Vorname should be updated")
|
||||
assertEquals("Dr.", viewModel.titel, "Titel should be updated")
|
||||
assertEquals(GeschlechtE.M, viewModel.geschlecht, "Geschlecht should be updated")
|
||||
assertEquals("max@example.com", viewModel.email, "Email should be updated")
|
||||
assertTrue(viewModel.istGesperrt, "IstGesperrt should be updated")
|
||||
assertEquals("Test Sperrgrund", viewModel.sperrGrund, "SperrGrund should be updated")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `update methods should handle special characters`() {
|
||||
// When - update with special characters
|
||||
val nameWithSpecialChars = "Müller-Höß"
|
||||
viewModel.updateNachname(nameWithSpecialChars)
|
||||
|
||||
// Then - verify special characters are preserved
|
||||
assertEquals(nameWithSpecialChars, viewModel.nachname, "Special characters should be preserved")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `update methods should handle very long inputs`() {
|
||||
// When - update with very long input
|
||||
val longText = "A".repeat(500)
|
||||
viewModel.updateNotizen(longText)
|
||||
|
||||
// Then - verify long text is preserved
|
||||
assertEquals(longText, viewModel.notizen, "Long text should be preserved")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Validation Tests
|
||||
|
||||
@Test
|
||||
fun `createPerson should fail with empty nachname`() = runTest {
|
||||
// Given - empty nachname
|
||||
@@ -212,9 +273,9 @@ class CreatePersonViewModelTest {
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
assertEquals("Nachname ist erforderlich", viewModel.errorMessage)
|
||||
assertFalse(viewModel.isSuccess)
|
||||
assertFalse(viewModel.isLoading)
|
||||
assertEquals("Nachname ist erforderlich", viewModel.errorMessage, "Should show error for empty nachname")
|
||||
assertFalse(viewModel.isSuccess, "Should not be successful with validation error")
|
||||
assertFalse(viewModel.isLoading, "Loading state should be reset after validation")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -227,14 +288,36 @@ class CreatePersonViewModelTest {
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
assertEquals("Vorname ist erforderlich", viewModel.errorMessage)
|
||||
assertFalse(viewModel.isSuccess)
|
||||
assertFalse(viewModel.isLoading)
|
||||
assertEquals("Vorname ist erforderlich", viewModel.errorMessage, "Should show error for empty vorname")
|
||||
assertFalse(viewModel.isSuccess, "Should not be successful with validation error")
|
||||
assertFalse(viewModel.isLoading, "Loading state should be reset after validation")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createPerson should handle invalid date format`() = runTest {
|
||||
// Given - invalid date format
|
||||
viewModel.updateNachname("Mustermann")
|
||||
viewModel.updateVorname("Max")
|
||||
viewModel.updateGeburtsdatum("invalid-date")
|
||||
|
||||
// When
|
||||
viewModel.createPerson()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
assertEquals("Ungültiges Datumsformat. Verwenden Sie YYYY-MM-DD", viewModel.errorMessage,
|
||||
"Should show error for invalid date format")
|
||||
assertFalse(viewModel.isSuccess, "Should not be successful with validation error")
|
||||
assertFalse(viewModel.isLoading, "Loading state should be reset after validation")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Success Tests
|
||||
|
||||
@Test
|
||||
fun `createPerson should succeed with valid data`() = runTest {
|
||||
// Given
|
||||
// Given - valid data
|
||||
viewModel.updateNachname("Mustermann")
|
||||
viewModel.updateVorname("Max")
|
||||
viewModel.updateGeschlecht(GeschlechtE.M)
|
||||
@@ -245,31 +328,14 @@ class CreatePersonViewModelTest {
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
assertTrue(viewModel.isSuccess)
|
||||
assertNull(viewModel.errorMessage)
|
||||
assertFalse(viewModel.isLoading)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createPerson should handle invalid date format`() = runTest {
|
||||
// Given
|
||||
viewModel.updateNachname("Mustermann")
|
||||
viewModel.updateVorname("Max")
|
||||
viewModel.updateGeburtsdatum("invalid-date")
|
||||
|
||||
// When
|
||||
viewModel.createPerson()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
assertEquals("Ungültiges Datumsformat. Verwenden Sie YYYY-MM-DD", viewModel.errorMessage)
|
||||
assertFalse(viewModel.isSuccess)
|
||||
assertFalse(viewModel.isLoading)
|
||||
assertTrue(viewModel.isSuccess, "Should be successful with valid data")
|
||||
assertNull(viewModel.errorMessage, "Should not have error message")
|
||||
assertFalse(viewModel.isLoading, "Loading state should be reset after success")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createPerson should handle valid date format`() = runTest {
|
||||
// Given
|
||||
// Given - valid date format
|
||||
viewModel.updateNachname("Mustermann")
|
||||
viewModel.updateVorname("Max")
|
||||
viewModel.updateGeburtsdatum("1990-05-15")
|
||||
@@ -279,11 +345,31 @@ class CreatePersonViewModelTest {
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
assertTrue(viewModel.isSuccess)
|
||||
assertNull(viewModel.errorMessage)
|
||||
assertFalse(viewModel.isLoading)
|
||||
assertTrue(viewModel.isSuccess, "Should be successful with valid date")
|
||||
assertNull(viewModel.errorMessage, "Should not have error message")
|
||||
assertFalse(viewModel.isLoading, "Loading state should be reset after success")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createPerson should succeed with minimal required data`() = runTest {
|
||||
// Given - only required fields
|
||||
viewModel.updateNachname("Mustermann")
|
||||
viewModel.updateVorname("Max")
|
||||
|
||||
// When
|
||||
viewModel.createPerson()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
assertTrue(viewModel.isSuccess, "Should be successful with minimal required data")
|
||||
assertNull(viewModel.errorMessage, "Should not have error message")
|
||||
assertFalse(viewModel.isLoading, "Loading state should be reset after success")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Form Management Tests
|
||||
|
||||
@Test
|
||||
fun `resetForm should clear all fields`() {
|
||||
// Given - set some values
|
||||
@@ -291,18 +377,22 @@ class CreatePersonViewModelTest {
|
||||
viewModel.updateVorname("Max")
|
||||
viewModel.updateEmail("max@example.com")
|
||||
viewModel.updateIstGesperrt(true)
|
||||
viewModel.updateSperrGrund("Test Sperrgrund")
|
||||
|
||||
// When
|
||||
viewModel.resetForm()
|
||||
|
||||
// Then
|
||||
assertEquals("", viewModel.nachname)
|
||||
assertEquals("", viewModel.vorname)
|
||||
assertEquals("", viewModel.email)
|
||||
assertFalse(viewModel.istGesperrt)
|
||||
assertFalse(viewModel.isLoading)
|
||||
assertNull(viewModel.errorMessage)
|
||||
assertFalse(viewModel.isSuccess)
|
||||
// Then - verify all fields are reset
|
||||
assertEquals("", viewModel.nachname, "Nachname should be reset")
|
||||
assertEquals("", viewModel.vorname, "Vorname should be reset")
|
||||
assertEquals("", viewModel.email, "Email should be reset")
|
||||
assertFalse(viewModel.istGesperrt, "IstGesperrt should be reset")
|
||||
assertEquals("", viewModel.sperrGrund, "SperrGrund should be reset")
|
||||
|
||||
// Verify state flags are reset
|
||||
assertFalse(viewModel.isLoading, "IsLoading should be reset")
|
||||
assertNull(viewModel.errorMessage, "ErrorMessage should be reset")
|
||||
assertFalse(viewModel.isSuccess, "IsSuccess should be reset")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -310,18 +400,33 @@ class CreatePersonViewModelTest {
|
||||
// Given - simulate an error
|
||||
viewModel.updateNachname("") // This will cause validation error
|
||||
viewModel.updateVorname("Max")
|
||||
|
||||
// When
|
||||
viewModel.createPerson()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then - verify error message exists
|
||||
assertNotNull(viewModel.errorMessage)
|
||||
assertNotNull(viewModel.errorMessage, "Should have error message")
|
||||
|
||||
// When - clear the error
|
||||
viewModel.clearError()
|
||||
|
||||
// Then - verify error message is cleared
|
||||
assertNull(viewModel.errorMessage)
|
||||
assertNull(viewModel.errorMessage, "Error message should be cleared")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `loading state should be reset after createPerson completes`() = runTest {
|
||||
// Given
|
||||
viewModel.updateNachname("Mustermann")
|
||||
viewModel.updateVorname("Max")
|
||||
|
||||
// When - start creation and complete the operation
|
||||
viewModel.createPerson()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then - verify loading state is reset after completion
|
||||
assertFalse(viewModel.isLoading, "Loading state should be reset after operation completes")
|
||||
assertTrue(viewModel.isSuccess, "Operation should complete successfully")
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
||||
|
||||
+195
-46
@@ -4,13 +4,22 @@ import at.mocode.members.domain.model.DomPerson
|
||||
import at.mocode.members.domain.repository.PersonRepository
|
||||
import at.mocode.enums.GeschlechtE
|
||||
import at.mocode.enums.DatenQuelleE
|
||||
import com.benasher44.uuid.Uuid
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.*
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlin.test.*
|
||||
|
||||
/**
|
||||
* Comprehensive test suite for the PersonListViewModel.
|
||||
*
|
||||
* Tests cover:
|
||||
* - Initial state verification
|
||||
* - Loading and refreshing person data
|
||||
* - Error handling
|
||||
* - Loading state management
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class PersonListViewModelTest {
|
||||
|
||||
@@ -21,17 +30,30 @@ class PersonListViewModelTest {
|
||||
@BeforeTest
|
||||
fun setup() {
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
setupMockRepository()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the mock repository with test data
|
||||
*/
|
||||
private fun setupMockRepository() {
|
||||
val persons = mutableListOf<DomPerson>()
|
||||
|
||||
mockPersonRepository = object : PersonRepository {
|
||||
private val persons = mutableListOf<DomPerson>()
|
||||
|
||||
override suspend fun save(person: DomPerson): DomPerson {
|
||||
val savedPerson = person.copy(personId = uuid4())
|
||||
|
||||
// Remove existing person with same OEPS number if exists
|
||||
val existingIndex = persons.indexOfFirst { it.oepsSatzNr == person.oepsSatzNr }
|
||||
if (existingIndex >= 0) {
|
||||
persons.removeAt(existingIndex)
|
||||
}
|
||||
|
||||
persons.add(savedPerson)
|
||||
return savedPerson
|
||||
}
|
||||
|
||||
override suspend fun findById(id: com.benasher44.uuid.Uuid): DomPerson? {
|
||||
override suspend fun findById(id: Uuid): DomPerson? {
|
||||
return persons.find { it.personId == id }
|
||||
}
|
||||
|
||||
@@ -39,7 +61,7 @@ class PersonListViewModelTest {
|
||||
return persons.find { it.oepsSatzNr == oepsSatzNr }
|
||||
}
|
||||
|
||||
override suspend fun findByStammVereinId(vereinId: com.benasher44.uuid.Uuid): List<DomPerson> {
|
||||
override suspend fun findByStammVereinId(vereinId: Uuid): List<DomPerson> {
|
||||
return persons.filter { it.stammVereinId == vereinId }
|
||||
}
|
||||
|
||||
@@ -62,86 +84,213 @@ class PersonListViewModelTest {
|
||||
return persons.any { it.oepsSatzNr == oepsSatzNr }
|
||||
}
|
||||
|
||||
override suspend fun delete(id: com.benasher44.uuid.Uuid): Boolean {
|
||||
return persons.removeAll { it.personId == id }
|
||||
override suspend fun delete(id: Uuid): Boolean {
|
||||
val initialSize = persons.size
|
||||
persons.removeAll { it.personId == id }
|
||||
return persons.size < initialSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds test persons to the repository
|
||||
*/
|
||||
private suspend fun addTestPersons() {
|
||||
// Create and add test persons
|
||||
val testPersons = listOf(
|
||||
createTestPerson("123456", "Müller", "Hans", GeschlechtE.M),
|
||||
createTestPerson("234567", "Schmidt", "Anna", GeschlechtE.W),
|
||||
createTestPerson("345678", "Weber", "Thomas", GeschlechtE.M)
|
||||
)
|
||||
|
||||
testPersons.forEach { mockPersonRepository.save(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a test person with the given data
|
||||
*/
|
||||
private fun createTestPerson(
|
||||
oepsSatzNr: String,
|
||||
nachname: String,
|
||||
vorname: String,
|
||||
geschlecht: GeschlechtE,
|
||||
isActive: Boolean = true
|
||||
): DomPerson {
|
||||
return DomPerson(
|
||||
personId = uuid4(), // Generate a new UUID
|
||||
oepsSatzNr = oepsSatzNr,
|
||||
nachname = nachname,
|
||||
vorname = vorname,
|
||||
geschlechtE = geschlecht,
|
||||
datenQuelle = DatenQuelleE.MANUELL,
|
||||
istAktiv = isActive
|
||||
)
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
fun tearDown() {
|
||||
Dispatchers.resetMain()
|
||||
}
|
||||
|
||||
//region Initial State Tests
|
||||
|
||||
@Test
|
||||
fun `initial state should be correct`() {
|
||||
// When - create view model with empty repository
|
||||
viewModel = PersonListViewModel(mockPersonRepository)
|
||||
|
||||
assertTrue(viewModel.persons.isEmpty())
|
||||
assertFalse(viewModel.isLoading)
|
||||
assertNull(viewModel.errorMessage)
|
||||
// Then - verify initial state
|
||||
assertTrue(viewModel.persons.isEmpty(), "Persons list should be empty initially")
|
||||
assertFalse(viewModel.isLoading, "Loading state should be false initially")
|
||||
assertNull(viewModel.errorMessage, "Error message should be null initially")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Data Loading Tests
|
||||
|
||||
@Test
|
||||
fun `loadPersons should update persons list`() = runTest {
|
||||
// Given
|
||||
val testPerson = DomPerson(
|
||||
oepsSatzNr = "123456",
|
||||
nachname = "Test",
|
||||
vorname = "User",
|
||||
geschlechtE = GeschlechtE.M,
|
||||
datenQuelle = DatenQuelleE.MANUELL
|
||||
)
|
||||
mockPersonRepository.save(testPerson)
|
||||
// Given - repository with test data
|
||||
addTestPersons()
|
||||
|
||||
// When
|
||||
// When - initialize view model (which triggers loadPersons)
|
||||
viewModel = PersonListViewModel(mockPersonRepository)
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
assertEquals(1, viewModel.persons.size)
|
||||
assertEquals("Test", viewModel.persons.first().nachname)
|
||||
assertEquals("User", viewModel.persons.first().vorname)
|
||||
assertFalse(viewModel.isLoading)
|
||||
assertNull(viewModel.errorMessage)
|
||||
// Then - verify persons list is populated
|
||||
assertEquals(3, viewModel.persons.size, "Should load all test persons")
|
||||
assertTrue(
|
||||
viewModel.persons.any { it.nachname == "Müller" && it.vorname == "Hans" },
|
||||
"Should contain person Müller Hans"
|
||||
)
|
||||
assertTrue(
|
||||
viewModel.persons.any { it.nachname == "Schmidt" && it.vorname == "Anna" },
|
||||
"Should contain person Schmidt Anna"
|
||||
)
|
||||
assertTrue(
|
||||
viewModel.persons.any { it.nachname == "Weber" && it.vorname == "Thomas" },
|
||||
"Should contain person Weber Thomas"
|
||||
)
|
||||
assertFalse(viewModel.isLoading, "Loading state should be reset after loading")
|
||||
assertNull(viewModel.errorMessage, "Should not have error message after successful loading")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `refreshPersons should reload data`() = runTest {
|
||||
// Given
|
||||
// Given - view model with initial data loaded
|
||||
addTestPersons()
|
||||
viewModel = PersonListViewModel(mockPersonRepository)
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
val initialCount = viewModel.persons.size
|
||||
|
||||
// Add a new person to repository
|
||||
val newPerson = DomPerson(
|
||||
oepsSatzNr = "789012",
|
||||
nachname = "New",
|
||||
vorname = "Person",
|
||||
geschlechtE = GeschlechtE.W,
|
||||
datenQuelle = DatenQuelleE.MANUELL
|
||||
// When - add a new person and refresh
|
||||
val newPerson = createTestPerson(
|
||||
"999999",
|
||||
"New",
|
||||
"Person",
|
||||
GeschlechtE.D
|
||||
)
|
||||
mockPersonRepository.save(newPerson)
|
||||
|
||||
// When
|
||||
viewModel.refreshPersons()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then
|
||||
assertEquals(initialCount + 1, viewModel.persons.size)
|
||||
assertTrue(viewModel.persons.any { it.nachname == "New" })
|
||||
// Then - verify new person is included
|
||||
assertEquals(initialCount + 1, viewModel.persons.size, "Should have one more person after refresh")
|
||||
assertTrue(
|
||||
viewModel.persons.any { it.nachname == "New" && it.vorname == "Person" },
|
||||
"Should contain newly added person after refresh"
|
||||
)
|
||||
assertFalse(viewModel.isLoading, "Loading state should be reset after refresh")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clearError should reset error message`() {
|
||||
fun `loadPersons should handle empty repository`() = runTest {
|
||||
// Given - empty repository (already set up in setup())
|
||||
|
||||
// When - initialize view model
|
||||
viewModel = PersonListViewModel(mockPersonRepository)
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then - verify empty list is handled correctly
|
||||
assertTrue(viewModel.persons.isEmpty(), "Persons list should be empty with empty repository")
|
||||
assertFalse(viewModel.isLoading, "Loading state should be reset even with empty result")
|
||||
assertNull(viewModel.errorMessage, "Should not have error with empty repository")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `loading state should be reset after operations complete`() = runTest {
|
||||
// Given
|
||||
viewModel = PersonListViewModel(mockPersonRepository)
|
||||
|
||||
// Simulate an error (this would normally happen in a real error scenario)
|
||||
// For testing, we can't easily simulate repository errors with our mock
|
||||
// but we can test the clearError functionality
|
||||
// Add some test data to verify operation works
|
||||
addTestPersons()
|
||||
|
||||
viewModel.clearError()
|
||||
assertNull(viewModel.errorMessage)
|
||||
// When - refresh and complete the operation
|
||||
viewModel.refreshPersons()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then - verify loading state is reset after completion
|
||||
assertFalse(viewModel.isLoading, "Loading state should be reset after operation completes")
|
||||
assertTrue(viewModel.persons.isNotEmpty(), "Persons list should be populated after successful refresh")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Error Handling Tests
|
||||
|
||||
@Test
|
||||
fun `clearError should reset error message`() {
|
||||
// Given - view model
|
||||
viewModel = PersonListViewModel(mockPersonRepository)
|
||||
|
||||
// When - clear error (even when no error exists)
|
||||
viewModel.clearError()
|
||||
|
||||
// Then - verify no error message
|
||||
assertNull(viewModel.errorMessage, "Error message should be null after clearError")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `error handling should be robust`() = runTest {
|
||||
// Given - view model with initial data loaded
|
||||
addTestPersons()
|
||||
viewModel = PersonListViewModel(mockPersonRepository)
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Capture initial state
|
||||
val initialPersons = viewModel.persons.toList()
|
||||
|
||||
// When - simulate a refresh operation that might cause errors
|
||||
viewModel.refreshPersons()
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
// Then - verify data is still intact regardless of potential errors
|
||||
assertEquals(initialPersons.size, viewModel.persons.size,
|
||||
"Person list size should be maintained even with potential errors")
|
||||
|
||||
// And error handling mechanism works
|
||||
viewModel.clearError()
|
||||
assertNull(viewModel.errorMessage, "Should be able to clear any potential errors")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Search Tests
|
||||
|
||||
@Test
|
||||
fun `repository search should work correctly`() = runTest {
|
||||
// Given - repository with test data
|
||||
addTestPersons()
|
||||
|
||||
// When - search for a specific person
|
||||
val searchResults = mockPersonRepository.findByName("Müller", 10)
|
||||
|
||||
// Then - verify correct results
|
||||
assertEquals(1, searchResults.size, "Should find one person with name Müller")
|
||||
assertEquals("Müller", searchResults.first().nachname, "Should find person with correct last name")
|
||||
assertEquals("Hans", searchResults.first().vorname, "Should find person with correct first name")
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user