fixing web-app
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
package at.mocode.members.service
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
import org.springframework.context.annotation.ComponentScan
|
||||
|
||||
/**
|
||||
* Main application class for the Members Service.
|
||||
*
|
||||
* This service provides APIs for managing members and their data.
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@ComponentScan(basePackages = ["at.mocode.members"])
|
||||
class MembersServiceApplication
|
||||
|
||||
/**
|
||||
* Main entry point for the Members Service application.
|
||||
*/
|
||||
fun main(args: Array<String>) {
|
||||
runApplication<MembersServiceApplication>(*args)
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
package at.mocode.members.service.config
|
||||
|
||||
import at.mocode.core.utils.database.DatabaseConfig
|
||||
import at.mocode.core.utils.database.DatabaseFactory
|
||||
import at.mocode.members.infrastructure.persistence.MemberTable
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Profile
|
||||
import jakarta.annotation.PostConstruct
|
||||
import jakarta.annotation.PreDestroy
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.jetbrains.exposed.sql.SchemaUtils
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
||||
/**
|
||||
* Database configuration for the Members Service.
|
||||
*
|
||||
* This configuration ensures that Database.connect() is called properly
|
||||
* before any Exposed operations are performed.
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("!test")
|
||||
class MembersDatabaseConfiguration {
|
||||
|
||||
private val log = LoggerFactory.getLogger(MembersDatabaseConfiguration::class.java)
|
||||
|
||||
@PostConstruct
|
||||
fun initializeDatabase() {
|
||||
log.info("Initializing database schema for Members Service...")
|
||||
|
||||
try {
|
||||
// Database connection is already initialized by the gateway
|
||||
// Only initialize the schema for this service
|
||||
transaction {
|
||||
SchemaUtils.createMissingTablesAndColumns(MemberTable)
|
||||
log.info("Members database schema initialized successfully")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
log.error("Failed to initialize database schema", e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
fun closeDatabase() {
|
||||
log.info("Closing database connection for Members Service...")
|
||||
try {
|
||||
DatabaseFactory.close()
|
||||
log.info("Database connection closed successfully")
|
||||
} catch (e: Exception) {
|
||||
log.error("Error closing database connection", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test-specific database configuration.
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("test")
|
||||
class MembersTestDatabaseConfiguration {
|
||||
|
||||
private val log = LoggerFactory.getLogger(MembersTestDatabaseConfiguration::class.java)
|
||||
|
||||
@PostConstruct
|
||||
fun initializeTestDatabase() {
|
||||
log.info("Initializing test database connection for Members Service...")
|
||||
|
||||
try {
|
||||
// Use H2 in-memory database for tests
|
||||
val testConfig = DatabaseConfig(
|
||||
jdbcUrl = "jdbc:h2:mem:members_test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE",
|
||||
username = "sa",
|
||||
password = "",
|
||||
driverClassName = "org.h2.Driver",
|
||||
maxPoolSize = 5,
|
||||
minPoolSize = 1,
|
||||
autoMigrate = true
|
||||
)
|
||||
|
||||
DatabaseFactory.init(testConfig)
|
||||
log.info("Test database connection initialized successfully")
|
||||
|
||||
// Initialize database schema for tests
|
||||
transaction {
|
||||
SchemaUtils.createMissingTablesAndColumns(MemberTable)
|
||||
log.info("Test members database schema initialized successfully")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
log.error("Failed to initialize test database connection", e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
fun closeTestDatabase() {
|
||||
log.info("Closing test database connection for Members Service...")
|
||||
try {
|
||||
DatabaseFactory.close()
|
||||
log.info("Test database connection closed successfully")
|
||||
} catch (e: Exception) {
|
||||
log.error("Error closing test database connection", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
+245
@@ -0,0 +1,245 @@
|
||||
package at.mocode.members.service.integration
|
||||
|
||||
import at.mocode.infrastructure.messaging.client.EventPublisher
|
||||
import at.mocode.members.api.rest.MemberController
|
||||
import at.mocode.members.domain.model.Member
|
||||
import at.mocode.members.domain.repository.MemberRepository
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.datetime.LocalDate
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.test.context.TestPropertySource
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/**
|
||||
* Integration tests for the Members Service.
|
||||
*
|
||||
* These tests verify the complete functionality including
|
||||
* - REST API endpoints
|
||||
* - Database operations
|
||||
* - Event publishing
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles("test")
|
||||
@TestPropertySource(properties = [
|
||||
"spring.datasource.url=jdbc:h2:mem:testdb",
|
||||
"spring.kafka.bootstrap-servers=localhost:9092"
|
||||
])
|
||||
@ContextConfiguration(classes = [MemberServiceIntegrationTest.TestConfig::class])
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class MemberServiceIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("memberRepositoryImpl")
|
||||
private lateinit var memberRepository: MemberRepository
|
||||
|
||||
@Configuration
|
||||
class TestConfig {
|
||||
@Bean
|
||||
fun eventPublisher(): EventPublisher = mockk(relaxed = true)
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() = runBlocking {
|
||||
// Clean up database before each test
|
||||
// Note: In a real implementation, you might want to use @Transactional or @DirtiesContext
|
||||
println("[DEBUG_LOG] Setting up test - cleaning database")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should create member successfully`() = runBlocking {
|
||||
println("[DEBUG_LOG] Testing member creation")
|
||||
|
||||
// Given
|
||||
val createRequest = MemberController.CreateMemberRequest(
|
||||
firstName = "John",
|
||||
lastName = "Doe",
|
||||
email = "john.doe@example.com",
|
||||
phone = "+43123456789",
|
||||
dateOfBirth = LocalDate(1990, 1, 15),
|
||||
membershipNumber = "M001",
|
||||
membershipStartDate = LocalDate(2024, 1, 1),
|
||||
membershipEndDate = null,
|
||||
isActive = true,
|
||||
address = "123 Test Street, Vienna",
|
||||
emergencyContact = "Jane Doe: +43987654321"
|
||||
)
|
||||
|
||||
// When
|
||||
val member = Member(
|
||||
firstName = createRequest.firstName,
|
||||
lastName = createRequest.lastName,
|
||||
email = createRequest.email,
|
||||
phone = createRequest.phone,
|
||||
dateOfBirth = createRequest.dateOfBirth,
|
||||
membershipNumber = createRequest.membershipNumber,
|
||||
membershipStartDate = createRequest.membershipStartDate,
|
||||
membershipEndDate = createRequest.membershipEndDate,
|
||||
isActive = createRequest.isActive,
|
||||
address = createRequest.address,
|
||||
emergencyContact = createRequest.emergencyContact
|
||||
)
|
||||
|
||||
val savedMember = memberRepository.save(member)
|
||||
|
||||
// Then
|
||||
assertNotNull(savedMember)
|
||||
assertEquals(createRequest.firstName, savedMember.firstName)
|
||||
assertEquals(createRequest.lastName, savedMember.lastName)
|
||||
assertEquals(createRequest.email, savedMember.email)
|
||||
assertEquals(createRequest.membershipNumber, savedMember.membershipNumber)
|
||||
assertTrue(savedMember.isActive)
|
||||
|
||||
println("[DEBUG_LOG] Member created successfully with ID: ${savedMember.memberId}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should find member by membership number`() = runBlocking {
|
||||
println("[DEBUG_LOG] Testing find member by membership number")
|
||||
|
||||
// Given
|
||||
val member = Member(
|
||||
firstName = "Jane",
|
||||
lastName = "Smith",
|
||||
email = "jane.smith@example.com",
|
||||
membershipNumber = "M002",
|
||||
membershipStartDate = LocalDate(2024, 1, 1),
|
||||
isActive = true
|
||||
)
|
||||
memberRepository.save(member)
|
||||
|
||||
// When
|
||||
val foundMember = memberRepository.findByMembershipNumber("M002")
|
||||
|
||||
// Then
|
||||
assertNotNull(foundMember)
|
||||
assertEquals("Jane", foundMember.firstName)
|
||||
assertEquals("Smith", foundMember.lastName)
|
||||
assertEquals("M002", foundMember.membershipNumber)
|
||||
|
||||
println("[DEBUG_LOG] Member found by membership number: ${foundMember.memberId}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should find member by email`() = runBlocking {
|
||||
println("[DEBUG_LOG] Testing find member by email")
|
||||
|
||||
// Given
|
||||
val member = Member(
|
||||
firstName = "Bob",
|
||||
lastName = "Johnson",
|
||||
email = "bob.johnson@example.com",
|
||||
membershipNumber = "M003",
|
||||
membershipStartDate = LocalDate(2024, 1, 1),
|
||||
isActive = true
|
||||
)
|
||||
memberRepository.save(member)
|
||||
|
||||
// When
|
||||
val foundMember = memberRepository.findByEmail("bob.johnson@example.com")
|
||||
|
||||
// Then
|
||||
assertNotNull(foundMember)
|
||||
assertEquals("Bob", foundMember.firstName)
|
||||
assertEquals("Johnson", foundMember.lastName)
|
||||
assertEquals("bob.johnson@example.com", foundMember.email)
|
||||
|
||||
println("[DEBUG_LOG] Member found by email: ${foundMember.memberId}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should count active members`() = runBlocking {
|
||||
println("[DEBUG_LOG] Testing count active members")
|
||||
|
||||
// Given
|
||||
val activeMember = Member(
|
||||
firstName = "Active",
|
||||
lastName = "Member",
|
||||
email = "active@example.com",
|
||||
membershipNumber = "M004",
|
||||
membershipStartDate = LocalDate(2024, 1, 1),
|
||||
isActive = true
|
||||
)
|
||||
|
||||
val inactiveMember = Member(
|
||||
firstName = "Inactive",
|
||||
lastName = "Member",
|
||||
email = "inactive@example.com",
|
||||
membershipNumber = "M005",
|
||||
membershipStartDate = LocalDate(2024, 1, 1),
|
||||
isActive = false
|
||||
)
|
||||
|
||||
memberRepository.save(activeMember)
|
||||
memberRepository.save(inactiveMember)
|
||||
|
||||
// When
|
||||
val activeCount = memberRepository.countActive()
|
||||
val totalCount = memberRepository.countAll()
|
||||
|
||||
// Then
|
||||
assertTrue(activeCount >= 1, "Should have at least 1 active member")
|
||||
assertTrue(totalCount >= 2, "Should have at least 2 total members")
|
||||
|
||||
println("[DEBUG_LOG] Active members: $activeCount, Total members: $totalCount")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should validate duplicate membership number`() = runBlocking {
|
||||
println("[DEBUG_LOG] Testing duplicate membership number validation")
|
||||
|
||||
// Given
|
||||
val member1 = Member(
|
||||
firstName = "First",
|
||||
lastName = "Member",
|
||||
email = "first@example.com",
|
||||
membershipNumber = "M006",
|
||||
membershipStartDate = LocalDate(2024, 1, 1),
|
||||
isActive = true
|
||||
)
|
||||
memberRepository.save(member1)
|
||||
|
||||
// When
|
||||
val exists = memberRepository.existsByMembershipNumber("M006")
|
||||
|
||||
// Then
|
||||
assertTrue(exists, "Should detect existing membership number")
|
||||
|
||||
println("[DEBUG_LOG] Duplicate membership number validation passed")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should validate duplicate email`() = runBlocking {
|
||||
println("[DEBUG_LOG] Testing duplicate email validation")
|
||||
|
||||
// Given
|
||||
val member = Member(
|
||||
firstName = "Email",
|
||||
lastName = "Test",
|
||||
email = "email.test@example.com",
|
||||
membershipNumber = "M007",
|
||||
membershipStartDate = LocalDate(2024, 1, 1),
|
||||
isActive = true
|
||||
)
|
||||
memberRepository.save(member)
|
||||
|
||||
// When
|
||||
val exists = memberRepository.existsByEmail("email.test@example.com")
|
||||
|
||||
// Then
|
||||
assertTrue(exists, "Should detect existing email")
|
||||
|
||||
println("[DEBUG_LOG] Duplicate email validation passed")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<configuration>
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE" />
|
||||
</root>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user