diff --git a/back001/common/build.gradle.kts b/back001/common/build.gradle.kts index 942b203..73c7448 100644 --- a/back001/common/build.gradle.kts +++ b/back001/common/build.gradle.kts @@ -21,4 +21,5 @@ dependencies { testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.mockito:mockito-core:5.12.0") + testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1") } diff --git a/back001/common/src/test/kotlin/com/mosenioring/common/BaseEntityTest.kt b/back001/common/src/test/kotlin/com/mosenioring/common/BaseEntityTest.kt new file mode 100644 index 0000000..15b6e45 --- /dev/null +++ b/back001/common/src/test/kotlin/com/mosenioring/common/BaseEntityTest.kt @@ -0,0 +1,47 @@ +package com.mosenioring.common + +import com.mosenioring.common.tenant.TenantContext +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import java.time.Instant + +class BaseEntityTest { + + class TestEntity : BaseEntity() + + @Test + fun `onCreate populates metadata`() { + TenantContext.setTenantId("tenant-123") + val entity = TestEntity() + + entity.onCreate() + + assertEquals("tenant-123", entity.tenantId) + assertNotNull(entity.createdAt) + assertNotNull(entity.updatedAt) + assertEquals(entity.createdAt, entity.updatedAt) + TenantContext.clear() + } + + @Test + fun `onUpdate updates updatedAt`() { + val entity = TestEntity() + entity.onCreate() + val originalUpdatedAt = entity.updatedAt + + Thread.sleep(10) // Ensure some time passes + entity.onUpdate() + + assertTrue(entity.updatedAt.isAfter(originalUpdatedAt)) + } + + @Test + fun `does not overwrite existing tenantId`() { + val entity = TestEntity() + entity.tenantId = "manual-tenant" + + entity.onCreate() + + assertEquals("manual-tenant", entity.tenantId) + } +} diff --git a/back001/modules/audit/build.gradle.kts b/back001/modules/audit/build.gradle.kts index 6acd1d2..9df409d 100644 --- a/back001/modules/audit/build.gradle.kts +++ b/back001/modules/audit/build.gradle.kts @@ -9,4 +9,7 @@ plugins { dependencies { api(project(":common")) implementation("org.springframework.boot:spring-boot-starter-data-jpa") + testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("org.mockito:mockito-core") + testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1") } diff --git a/back001/modules/audit/src/test/kotlin/com/mosenioring/audit/service/AuditServiceTest.kt b/back001/modules/audit/src/test/kotlin/com/mosenioring/audit/service/AuditServiceTest.kt new file mode 100644 index 0000000..afe6692 --- /dev/null +++ b/back001/modules/audit/src/test/kotlin/com/mosenioring/audit/service/AuditServiceTest.kt @@ -0,0 +1,38 @@ +package com.mosenioring.audit.service + +import com.mosenioring.audit.AuditEvent +import com.mosenioring.audit.AuditRepository +import com.mosenioring.common.tenant.TenantContext +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.ArgumentCaptor +import org.mockito.Mockito.* + +class AuditServiceTest { + + private lateinit var repository: AuditRepository + private lateinit var service: AuditService + + @BeforeEach + fun setup() { + repository = mock(AuditRepository::class.java) + service = AuditService(repository) + TenantContext.setTenantId("t-test") + } + + @Test + fun `records audit event correctly`() { + service.record("ACTION_X", "resource_y", "id-1", "p-1") + + val captor = ArgumentCaptor.forClass(AuditEvent::class.java) + verify(repository).save(captor.capture()) + + val event = captor.value + assertEquals("ACTION_X", event.action) + assertEquals("resource_y", event.resource) + assertEquals("id-1", event.resourceId) + assertEquals("p-1", event.patientId) + assertEquals("t-test", event.tenantId) + } +} diff --git a/back001/modules/clinical/build.gradle.kts b/back001/modules/clinical/build.gradle.kts index 5db2a38..2b8c7c5 100644 --- a/back001/modules/clinical/build.gradle.kts +++ b/back001/modules/clinical/build.gradle.kts @@ -9,4 +9,7 @@ plugins { dependencies { api(project(":common")) implementation(project(":modules:audit")) + testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("org.mockito:mockito-core") + testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1") } diff --git a/back001/modules/clinical/src/test/kotlin/com/mosenioring/clinical/service/MedicationPlanServiceTest.kt b/back001/modules/clinical/src/test/kotlin/com/mosenioring/clinical/service/MedicationPlanServiceTest.kt new file mode 100644 index 0000000..c0fed5a --- /dev/null +++ b/back001/modules/clinical/src/test/kotlin/com/mosenioring/clinical/service/MedicationPlanServiceTest.kt @@ -0,0 +1,43 @@ +package com.mosenioring.clinical.service + +import com.mosenioring.audit.service.AuditService +import com.mosenioring.clinical.MedicationPlan +import com.mosenioring.clinical.repo.MedicationPlanRepository +import com.mosenioring.common.Events +import com.mosenioring.common.outbox.OutboxService +import com.mosenioring.common.tenant.TenantContext +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.kotlin.* + +class MedicationPlanServiceTest { + + private val repository: MedicationPlanRepository = mock() + private val outboxService: OutboxService = mock() + private val auditService: AuditService = mock() + private val service: MedicationPlanService = MedicationPlanService(repository, outboxService, auditService) + + @BeforeEach + fun setup() { + TenantContext.setTenantId("t1") + } + + @Test + fun `creates medication plan and records events`() { + val planToReturn = MedicationPlan("id-1", "p1", "Take vitamin D") + doReturn(planToReturn).whenever(repository).save(any()) + doReturn(mock()).whenever(outboxService).enqueue(any(), any()) + + val plan = service.createMedicationPlan("p1", "Take vitamin D") + + assertNotNull(plan) + assertEquals("p1", plan.patientId) + assertEquals("Take vitamin D", plan.description) + + verify(repository).save(any()) + verify(outboxService).enqueue(eq(Events.MEDICATION_PLAN_CREATED), any()) + verify(auditService).record(eq("MEDICATION_PLAN_CREATED"), eq("medication_plan"), any(), eq("p1")) + } +} diff --git a/back001/modules/messaging/build.gradle.kts b/back001/modules/messaging/build.gradle.kts index 5db2a38..2b8c7c5 100644 --- a/back001/modules/messaging/build.gradle.kts +++ b/back001/modules/messaging/build.gradle.kts @@ -9,4 +9,7 @@ plugins { dependencies { api(project(":common")) implementation(project(":modules:audit")) + testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("org.mockito:mockito-core") + testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1") } diff --git a/back001/modules/messaging/src/test/kotlin/com/mosenioring/messaging/service/MessageServiceTest.kt b/back001/modules/messaging/src/test/kotlin/com/mosenioring/messaging/service/MessageServiceTest.kt new file mode 100644 index 0000000..5765ebc --- /dev/null +++ b/back001/modules/messaging/src/test/kotlin/com/mosenioring/messaging/service/MessageServiceTest.kt @@ -0,0 +1,53 @@ +package com.mosenioring.messaging.service + +import com.mosenioring.audit.service.AuditService +import com.mosenioring.common.tenant.TenantContext +import com.mosenioring.common.security.SecurityUtils +import com.mosenioring.messaging.Message +import com.mosenioring.messaging.repo.MessageRepository +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.kotlin.* +import org.mockito.MockedStatic +import org.mockito.Mockito + +class MessageServiceTest { + + private val repository: MessageRepository = mock() + private val auditService: AuditService = mock() + private val service: MessageService = MessageService(repository, auditService) + + @BeforeEach + fun setup() { + TenantContext.setTenantId("t1") + } + + @Test + fun `adds message and records audit`() { + val userId = "u1" + val patientId = "p1" + val body = "Hello" + + val principal = com.mosenioring.common.security.LocalUserPrincipal(userId, "t1", emptySet()) + val auth = org.springframework.security.authentication.UsernamePasswordAuthenticationToken(principal, "n/a", emptyList()) + org.springframework.security.core.context.SecurityContextHolder.getContext().authentication = auth + + try { + whenever(repository.save(any())).thenAnswer { it.arguments[0] } + + val result = service.addMessage(patientId, body) + + assertNotNull(result) + assertEquals(patientId, result.patientId) + assertEquals(userId, result.senderId) + assertEquals(body, result.body) + + verify(repository).save(any()) + verify(auditService).record(eq("MESSAGE_ADDED"), eq("message"), any(), eq(patientId)) + } finally { + org.springframework.security.core.context.SecurityContextHolder.clearContext() + } + } +}