(vision) SCS/DDD

This commit is contained in:
2025-07-18 23:21:03 +02:00
parent 611e31e196
commit e1125a3fc0
13 changed files with 1419 additions and 0 deletions
@@ -0,0 +1,259 @@
package at.mocode.ui.viewmodel
import at.mocode.members.application.usecase.CreatePersonUseCase
import at.mocode.members.domain.model.DomPerson
import at.mocode.members.domain.repository.PersonRepository
import at.mocode.members.domain.repository.VereinRepository
import at.mocode.members.domain.service.MasterDataService
import at.mocode.enums.GeschlechtE
import at.mocode.enums.DatenQuelleE
import at.mocode.validation.ValidationResult
import com.benasher44.uuid.uuid4
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.*
import kotlinx.datetime.LocalDate
import kotlin.test.*
@OptIn(ExperimentalCoroutinesApi::class)
class CreatePersonViewModelTest {
private lateinit var mockPersonRepository: PersonRepository
private lateinit var mockVereinRepository: VereinRepository
private lateinit var mockMasterDataService: MasterDataService
private lateinit var createPersonUseCase: CreatePersonUseCase
private lateinit var viewModel: CreatePersonViewModel
private val testDispatcher = StandardTestDispatcher()
@BeforeTest
fun setup() {
Dispatchers.setMain(testDispatcher)
mockPersonRepository = object : PersonRepository {
private val persons = mutableListOf<DomPerson>()
override suspend fun save(person: DomPerson): DomPerson {
val savedPerson = person.copy(id = uuid4())
persons.add(savedPerson)
return savedPerson
}
override suspend fun findById(id: com.benasher44.uuid.Uuid): DomPerson? {
return persons.find { it.id == id }
}
override suspend fun findByOepsSatzNr(oepsSatzNr: String): DomPerson? {
return persons.find { it.oepsSatzNr == oepsSatzNr }
}
override suspend fun existsByOepsSatzNr(oepsSatzNr: String): Boolean {
return persons.any { it.oepsSatzNr == oepsSatzNr }
}
override suspend fun findAll(): List<DomPerson> {
return persons.toList()
}
override suspend fun delete(id: com.benasher44.uuid.Uuid) {
persons.removeAll { it.id == id }
}
}
mockVereinRepository = object : VereinRepository {
override suspend fun findById(id: com.benasher44.uuid.Uuid): at.mocode.members.domain.model.DomVerein? {
return null
}
override suspend fun existsById(id: com.benasher44.uuid.Uuid): Boolean {
return true
}
override suspend fun findAll(): List<at.mocode.members.domain.model.DomVerein> {
return emptyList()
}
}
mockMasterDataService = object : MasterDataService {
override suspend fun countryExists(countryId: com.benasher44.uuid.Uuid): Boolean {
return true
}
}
createPersonUseCase = CreatePersonUseCase(
personRepository = mockPersonRepository,
vereinRepository = mockVereinRepository,
masterDataService = mockMasterDataService
)
viewModel = CreatePersonViewModel(createPersonUseCase)
}
@AfterTest
fun tearDown() {
Dispatchers.resetMain()
}
@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)
}
@Test
fun `update methods should change state correctly`() {
viewModel.updateNachname("Mustermann")
viewModel.updateVorname("Max")
viewModel.updateTitel("Dr.")
viewModel.updateGeschlecht(GeschlechtE.M)
viewModel.updateEmail("max@example.com")
viewModel.updateIstGesperrt(true)
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)
}
@Test
fun `createPerson should fail with empty nachname`() = runTest {
// Given - empty nachname
viewModel.updateVorname("Max")
// When
viewModel.createPerson()
testDispatcher.scheduler.advanceUntilIdle()
// Then
assertEquals("Nachname ist erforderlich", viewModel.errorMessage)
assertFalse(viewModel.isSuccess)
assertFalse(viewModel.isLoading)
}
@Test
fun `createPerson should fail with empty vorname`() = runTest {
// Given - empty vorname
viewModel.updateNachname("Mustermann")
// When
viewModel.createPerson()
testDispatcher.scheduler.advanceUntilIdle()
// Then
assertEquals("Vorname ist erforderlich", viewModel.errorMessage)
assertFalse(viewModel.isSuccess)
assertFalse(viewModel.isLoading)
}
@Test
fun `createPerson should succeed with valid data`() = runTest {
// Given
viewModel.updateNachname("Mustermann")
viewModel.updateVorname("Max")
viewModel.updateGeschlecht(GeschlechtE.M)
viewModel.updateEmail("max@example.com")
// When
viewModel.createPerson()
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)
}
@Test
fun `createPerson should handle valid date format`() = runTest {
// Given
viewModel.updateNachname("Mustermann")
viewModel.updateVorname("Max")
viewModel.updateGeburtsdatum("1990-05-15")
// When
viewModel.createPerson()
testDispatcher.scheduler.advanceUntilIdle()
// Then
assertTrue(viewModel.isSuccess)
assertNull(viewModel.errorMessage)
assertFalse(viewModel.isLoading)
}
@Test
fun `resetForm should clear all fields`() {
// Given - set some values
viewModel.updateNachname("Mustermann")
viewModel.updateVorname("Max")
viewModel.updateEmail("max@example.com")
viewModel.updateIstGesperrt(true)
// 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)
}
@Test
fun `clearError should reset error message`() {
// Given - simulate an error
viewModel.updateNachname("") // This will cause validation error
viewModel.updateVorname("Max")
runTest {
viewModel.createPerson()
testDispatcher.scheduler.advanceUntilIdle()
}
assertNotNull(viewModel.errorMessage)
// When
viewModel.clearError()
// Then
assertNull(viewModel.errorMessage)
}
}
@@ -0,0 +1,130 @@
package at.mocode.ui.viewmodel
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.uuid4
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.*
import kotlinx.datetime.LocalDate
import kotlin.test.*
@OptIn(ExperimentalCoroutinesApi::class)
class PersonListViewModelTest {
private lateinit var mockPersonRepository: PersonRepository
private lateinit var viewModel: PersonListViewModel
private val testDispatcher = StandardTestDispatcher()
@BeforeTest
fun setup() {
Dispatchers.setMain(testDispatcher)
mockPersonRepository = object : PersonRepository {
private val persons = mutableListOf<DomPerson>()
override suspend fun save(person: DomPerson): DomPerson {
val savedPerson = person.copy(id = uuid4())
persons.add(savedPerson)
return savedPerson
}
override suspend fun findById(id: com.benasher44.uuid.Uuid): DomPerson? {
return persons.find { it.id == id }
}
override suspend fun findByOepsSatzNr(oepsSatzNr: String): DomPerson? {
return persons.find { it.oepsSatzNr == oepsSatzNr }
}
override suspend fun existsByOepsSatzNr(oepsSatzNr: String): Boolean {
return persons.any { it.oepsSatzNr == oepsSatzNr }
}
override suspend fun findAll(): List<DomPerson> {
return persons.toList()
}
override suspend fun delete(id: com.benasher44.uuid.Uuid) {
persons.removeAll { it.id == id }
}
}
}
@AfterTest
fun tearDown() {
Dispatchers.resetMain()
}
@Test
fun `initial state should be correct`() {
viewModel = PersonListViewModel(mockPersonRepository)
assertTrue(viewModel.persons.isEmpty())
assertFalse(viewModel.isLoading)
assertNull(viewModel.errorMessage)
}
@Test
fun `loadPersons should update persons list`() = runTest {
// Given
val testPerson = DomPerson(
nachname = "Test",
vorname = "User",
geschlechtE = GeschlechtE.M,
datenQuelle = DatenQuelleE.MANUELL
)
mockPersonRepository.save(testPerson)
// When
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)
}
@Test
fun `refreshPersons should reload data`() = runTest {
// Given
viewModel = PersonListViewModel(mockPersonRepository)
testDispatcher.scheduler.advanceUntilIdle()
val initialCount = viewModel.persons.size
// Add a new person to repository
val newPerson = DomPerson(
nachname = "New",
vorname = "Person",
geschlechtE = GeschlechtE.W,
datenQuelle = DatenQuelleE.MANUELL
)
mockPersonRepository.save(newPerson)
// When
viewModel.refreshPersons()
testDispatcher.scheduler.advanceUntilIdle()
// Then
assertEquals(initialCount + 1, viewModel.persons.size)
assertTrue(viewModel.persons.any { it.nachname == "New" })
}
@Test
fun `clearError should reset error message`() {
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
viewModel.clearError()
assertNull(viewModel.errorMessage)
}
}