diff --git a/src/main/kotlin/data/csv_data/csv_parser/UserCsvParser.kt b/src/main/kotlin/data/csv_data/csv_parser/UserCsvParser.kt index 1cd211c..65093b1 100644 --- a/src/main/kotlin/data/csv_data/csv_parser/UserCsvParser.kt +++ b/src/main/kotlin/data/csv_data/csv_parser/UserCsvParser.kt @@ -12,14 +12,14 @@ private const val Task_IDS: Int = 5 class UserCsvParser : CsvParser { override fun parse(data: List): List { return data.drop(1).map { line -> - val it = line.split(",", limit = 5) + val parts = line.split(",", limit = 6) UserDto( - id = it[ID], - name = it[NAME], - hashedPassword = it[HASHED_PASSWORD], - role = it[ROLE], - projectIds = it[PROJECT_IDS].toStringList(), - taskIds = it[Task_IDS].toStringList() + id = parts[ID], + name = parts[NAME], + hashedPassword = parts[HASHED_PASSWORD], + role = parts[ROLE], + projectIds = if (parts.size > PROJECT_IDS) parts[PROJECT_IDS].toStringList() else emptyList(), + taskIds = if (parts.size > Task_IDS) parts[Task_IDS].toStringList() else emptyList(), ) } } @@ -29,7 +29,8 @@ class UserCsvParser : CsvParser { return emptyList() } - return this.removePrefix("[") + return this.trim() + .removePrefix("[") .removeSuffix("]") .split(",") .filter { it.trim().isNotBlank() } diff --git a/src/test/kotlin/data/mongodb_data/repositories/AuditLogRepositoryImplTest.kt b/src/test/kotlin/data/mongodb_data/repositories/AuditLogRepositoryImplTest.kt new file mode 100644 index 0000000..168a5a4 --- /dev/null +++ b/src/test/kotlin/data/mongodb_data/repositories/AuditLogRepositoryImplTest.kt @@ -0,0 +1,128 @@ +package data.mongodb_data.repositories + +import com.google.common.truth.Truth.assertThat +import data.data_source.AuditLogDataSource +import data.mongodb_data.dto.AuditLogDto +import domain.models.AuditLog.AuditType +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.mockk +import kotlinx.coroutines.test.runTest +import kotlinx.datetime.Instant +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import java.util.* + +class AuditLogRepositoryImplTest { + private lateinit var auditLogDataSource: AuditLogDataSource + private lateinit var repository: AuditLogRepositoryImpl + + private val testTaskId = UUID.fromString("00000000-0000-0000-0000-000000000100") + private val testProjectId = UUID.fromString("00000000-0000-0000-0000-000000000101") + private val timestamp = Instant.parse("2023-12-31T23:59:59.000Z") + + private val taskLogDtos = listOf( + AuditLogDto( + id = UUID.fromString("00000000-0000-0000-0000-000000000001").toString(), + action = "Created", + auditType = "TASK", + timestamp = timestamp.toString(), + entityId = testTaskId.toString() + ), + AuditLogDto( + id = UUID.fromString("00000000-0000-0000-0000-000000000004").toString(), + action = "Updated", + auditType = "TASK", + timestamp = timestamp.toString(), + entityId = testTaskId.toString() + ) + ) + + private val projectLogDtos = listOf( + AuditLogDto( + id = UUID.fromString("00000000-0000-0000-0000-000000000006").toString(), + action = "Created", + auditType = "PROJECT", + timestamp = timestamp.toString(), + entityId = testProjectId.toString() + ) + ) + + @BeforeEach + fun setUp() { + auditLogDataSource = mockk(relaxed = true) + repository = AuditLogRepositoryImpl(auditLogDataSource) + + coEvery { auditLogDataSource.getAllLogsByTaskId(testTaskId) } returns taskLogDtos + coEvery { auditLogDataSource.getAllLogsByTaskId(not(eq(testTaskId))) } returns emptyList() + coEvery { auditLogDataSource.getAllLogsByProjectId(testProjectId) } returns projectLogDtos + coEvery { auditLogDataSource.getAllLogsByProjectId(not(eq(testProjectId))) } returns emptyList() + } + + @Test + fun `getAllLogsByTaskId should delegate to data source`() = runTest { + repository.getAllLogsByTaskId(testTaskId) + + coVerify { auditLogDataSource.getAllLogsByTaskId(testTaskId) } + } + + @Test + fun `getAllLogsByTaskId should return empty list when no matching logs exist`() = runTest { + val nonExistentTaskId = UUID.fromString("00000000-0000-0000-0000-000000000111") + val result = repository.getAllLogsByTaskId(nonExistentTaskId) + + assertTrue(result.isEmpty(), "Expected empty list for non-existent task ID") + } + + @Test + fun `getAllLogsByTaskId should return mapped logs for specified task ID`() = runTest { + val result = repository.getAllLogsByTaskId(testTaskId) + val resultIds = result.map { it.id.toString() } + + assertThat(resultIds).containsExactly( + "00000000-0000-0000-0000-000000000001", + "00000000-0000-0000-0000-000000000004" + ) + } + + @Test + fun `getAllLogsByTaskId should maintain TASK type logs`() = runTest { + val result = repository.getAllLogsByTaskId(testTaskId) + val resultAuditTypes = result.map { it.auditType }.toSet() + + assertThat(resultAuditTypes).containsExactly(AuditType.TASK) + } + + @Test + fun `getAllLogsByProjectId should delegate to data source`() = runTest { + repository.getAllLogsByProjectId(testProjectId) + + coVerify { auditLogDataSource.getAllLogsByProjectId(testProjectId) } + } + + @Test + fun `getAllLogsByProjectId should return empty list when no matching logs exist`() = runTest { + val nonExistentProjectId = UUID.randomUUID() + val result = repository.getAllLogsByProjectId(nonExistentProjectId) + + assertTrue(result.isEmpty()) + } + + @Test + fun `getAllLogsByProjectId should return mapped logs for specified project ID`() = runTest { + val result = repository.getAllLogsByProjectId(testProjectId) + val resultIds = result.map { it.id.toString() } + + assertThat(resultIds).containsExactly("00000000-0000-0000-0000-000000000006") + } + + @Test + fun `getAllLogsByProjectId should maintain PROJECT type logs`() = runTest { + val result = repository.getAllLogsByProjectId(testProjectId) + val resultAuditTypes = result.map { it.auditType }.toSet() + + assertThat(resultAuditTypes).containsExactly(AuditType.PROJECT) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/data/mongodb_data/repositories/ProjectsRepositoryImplTest.kt b/src/test/kotlin/data/mongodb_data/repositories/ProjectsRepositoryImplTest.kt new file mode 100644 index 0000000..f68697a --- /dev/null +++ b/src/test/kotlin/data/mongodb_data/repositories/ProjectsRepositoryImplTest.kt @@ -0,0 +1,211 @@ +package data.mongodb_data.repositories + +import com.google.common.truth.Truth.assertThat +import data.data_source.AuditLogDataSource +import data.data_source.ProjectsDataSource +import data.data_source.UserDataSource +import data.mongodb_data.dto.ProjectDto +import data.mongodb_data.util.ensureAdminPrivileges +import data.session_manager.LoggedInUser +import data.session_manager.SessionManager +import domain.models.AuditLog.AuditType +import domain.models.Project +import domain.models.User.UserRole +import domain.util.NoExistProjectException +import domain.util.NoUserLoginException +import io.mockk.* +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import java.util.* + +class ProjectsRepositoryImplTest { + private lateinit var projectsDataSource: ProjectsDataSource + private lateinit var auditLogDataSource: AuditLogDataSource + private lateinit var userDataSource: UserDataSource + private lateinit var repository: ProjectsRepositoryImpl + + private val testProject1 = Project( + UUID.fromString("11111111-1111-1111-1111-111111111111"), + "Project 1" + ) + private val testProject2 = Project( + UUID.fromString("22222222-2222-2222-2222-222222222222"), + "Project 2" + ) + private val userId = UUID.fromString("00000000-0000-0000-0000-000000000001") + + @BeforeEach + fun setUp() { + projectsDataSource = mockk(relaxed = true) + auditLogDataSource = mockk(relaxed = true) + userDataSource = mockk(relaxed = true) + + mockkObject(SessionManager) + SessionManager.currentUser = LoggedInUser( + userId, + "testUser", + UserRole.ADMIN, + listOf(testProject1.id) + ) + + mockkStatic(::ensureAdminPrivileges) + every { ensureAdminPrivileges() } just Runs + + repository = ProjectsRepositoryImpl( + projectsDataSource, + auditLogDataSource, + userDataSource + ) + + val projectDto1 = ProjectDto( + id = testProject1.id.toString(), + name = testProject1.name + ) + + coEvery { projectsDataSource.addProject(any()) } returns true + coEvery { projectsDataSource.updateProject(any()) } returns true + coEvery { projectsDataSource.deleteProject(any()) } returns true + coEvery { projectsDataSource.getAllProjects() } returns listOf(projectDto1) + coEvery { projectsDataSource.getProjectById(testProject1.id) } returns projectDto1 + } + + @Test + fun `addProject should add project and create audit log`() = runTest { + // When + val result = repository.addProject(testProject1) + + // Then + assertThat(result).isTrue() + coVerify { projectsDataSource.addProject(any()) } + coVerify { userDataSource.assignUserToProject(testProject1.id, userId) } + coVerify { auditLogDataSource.addLog(match { + it.action.contains("Created") && + it.entityId == testProject1.id.toString() && + it.auditType == AuditType.PROJECT.toString() + }) } + } + + @Test + fun `addProject should throw NoUserLoginException when no user is logged in`() = runTest { + // Given + SessionManager.currentUser = null + + // When & Then + assertThrows { + repository.addProject(testProject1) + } + } + + @Test + fun `updateProject should update project and create audit log`() = runTest { + // When + val result = repository.updateProject(testProject1) + + // Then + assertThat(result).isTrue() + coVerify { projectsDataSource.updateProject(any()) } + coVerify { auditLogDataSource.addLog(match { + it.action.contains("Updated") && + it.entityId == testProject1.id.toString() && + it.auditType == AuditType.PROJECT.toString() + }) } + } + + @Test + fun `updateProject should return false when update fails`() = runTest { + // Given + coEvery { projectsDataSource.updateProject(any()) } returns false + + // When + val result = repository.updateProject(testProject1) + + // Then + assertThat(result).isFalse() + coVerify { auditLogDataSource.addLog(any()) } + } + + @Test + fun `deleteProject should delete project and create audit log`() = runTest { + // When + val result = repository.deleteProject(testProject1.id) + + // Then + assertThat(result).isTrue() + coVerify { projectsDataSource.deleteProject(testProject1.id) } + coVerify { auditLogDataSource.addLog(match { + it.action.contains("Deleted") && + it.entityId == testProject1.id.toString() && + it.auditType == AuditType.PROJECT.toString() + }) } + } + + @Test + fun `deleteProject should return false when delete fails`() = runTest { + // Given + coEvery { projectsDataSource.deleteProject(any()) } returns false + + // When + val result = repository.deleteProject(testProject1.id) + + // Then + assertThat(result).isFalse() + coVerify { auditLogDataSource.addLog(any()) } + } + + @Test + fun `getAllProjectsByUser should return projects for current user`() = runTest { + // Given + val projectDto1 = ProjectDto(id = testProject1.id.toString(), name = "Project 1") + val projectDto2 = ProjectDto(id = testProject2.id.toString(), name = "Project 2") + + coEvery { projectsDataSource.getAllProjects() } returns listOf(projectDto1, projectDto2) + SessionManager.currentUser = LoggedInUser( + userId, + "testUser", + UserRole.ADMIN, + listOf(testProject1.id) + ) + + // When + val result = repository.getAllProjectsByUser(userId) + + // Then + assertThat(result).hasSize(1) + assertThat(result[0].id).isEqualTo(testProject1.id) + } + + @Test + fun `getAllProjectsByUser should throw NoExistProjectException when user is not logged in`() = runTest { + // Given + SessionManager.currentUser = null + + // When & Then + assertThrows { + repository.getAllProjectsByUser(userId) + } + } + + @Test + fun `getProjectById should return project when it exists`() = runTest { + // When + val result = repository.getProjectById(testProject1.id) + + // Then + assertThat(result.id).isEqualTo(testProject1.id) + assertThat(result.name).isEqualTo(testProject1.name) + coVerify { projectsDataSource.getProjectById(testProject1.id) } + } + + @Test + fun `admin privileges should be checked for critical operations`() = runTest { + // When + repository.addProject(testProject1) + repository.updateProject(testProject1) + repository.deleteProject(testProject1.id) + + // Then + verify(exactly = 3) { ensureAdminPrivileges() } + } +} \ No newline at end of file diff --git a/src/test/kotlin/data/mongodb_data/repositories/TaskRepositoryImplTest.kt b/src/test/kotlin/data/mongodb_data/repositories/TaskRepositoryImplTest.kt new file mode 100644 index 0000000..f3fd437 --- /dev/null +++ b/src/test/kotlin/data/mongodb_data/repositories/TaskRepositoryImplTest.kt @@ -0,0 +1,143 @@ +package data.mongodb_data.repositories + +import com.google.common.truth.Truth.assertThat +import data.data_source.AuditLogDataSource +import data.data_source.TaskDataSource +import data.mongodb_data.dto.TaskDto +import data.mongodb_data.mappers.toDto +import data.mongodb_data.mappers.toTask +import domain.models.Task +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.mockk +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import java.util.* + +class TaskRepositoryImplTest { + + private lateinit var taskDataSource: TaskDataSource + private lateinit var auditLogDataSource: AuditLogDataSource + private lateinit var repository: TaskRepositoryImpl + private lateinit var testTask1: Task + private lateinit var testTask2: Task + private lateinit var testTask3: Task + private lateinit var projectId1: UUID + private lateinit var projectId2: UUID + private lateinit var testTaskDto1: TaskDto + private lateinit var testTaskDto2: TaskDto + private lateinit var testTaskDto3: TaskDto + + @BeforeEach + fun setUp() { + taskDataSource = mockk(relaxed = true) + auditLogDataSource = mockk(relaxed = true) + + projectId1 = UUID.fromString("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa") + projectId2 = UUID.fromString("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb") + + testTask1 = Task( + UUID.fromString("11111111-1111-1111-1111-111111111111"), + "Task 1", + "Description 1", + projectId1, + UUID.randomUUID(), + listOf() + ) + testTask2 = Task( + UUID.fromString("22222222-2222-2222-2222-222222222222"), + "Task 2", + "Description 2", + projectId1, + UUID.randomUUID(), + listOf() + ) + testTask3 = Task( + UUID.fromString("33333333-3333-3333-3333-333333333333"), + "Task 3", + "Description 3", + projectId2, + UUID.randomUUID(), + listOf() + ) + + testTaskDto1 = testTask1.toDto() + testTaskDto2 = testTask2.toDto() + testTaskDto3 = testTask3.toDto() + + repository = TaskRepositoryImpl(taskDataSource, auditLogDataSource) + } + + @Test + fun `getAllTasks should return all tasks from data source`() = runTest { + // Given + val taskDtos = listOf(testTaskDto1, testTaskDto2, testTaskDto3) + coEvery { taskDataSource.getAllTasks() } returns taskDtos + + // When + val result = repository.getAllTasks() + + // Then + coVerify { taskDataSource.getAllTasks() } + assertThat(result.size).isEqualTo(3) + assertThat(result).contains(testTaskDto1.toTask()) + assertThat(result).contains(testTaskDto2.toTask()) + assertThat(result).contains(testTaskDto3.toTask()) + } + + @Test + fun `getAllTasks should return empty list when data source is empty`() = runTest { + // Given + coEvery { taskDataSource.getAllTasks() } returns emptyList() + + // When + val result = repository.getAllTasks() + + // Then + assertThat(result).isEmpty() + } + + @Test + fun `getTaskById should return task from data source`() = runTest { + // Given + coEvery { taskDataSource.getTaskById(testTask1.id) } returns testTaskDto1 + + // When + val result = repository.getTaskById(testTask1.id) + + // Then + assertThat(result).isEqualTo(testTaskDto1.toTask()) + coVerify { taskDataSource.getTaskById(testTask1.id) } + } + + @Test + fun `getTaskByProjectId should return tasks with matching project ID`() = runTest { + // Given + val taskDtos = listOf(testTaskDto1, testTaskDto2) + coEvery { taskDataSource.getTaskByProjectId(projectId1) } returns taskDtos + + // When + val result = repository.getTaskByProjectId(projectId1) + + // Then + assertThat(result).hasSize(2) + assertThat(result).contains(testTaskDto1.toTask()) + assertThat(result).contains(testTaskDto2.toTask()) + coVerify { taskDataSource.getTaskByProjectId(projectId1) } + } + + @Test + fun `getTaskByProjectId should return empty list when no tasks match project ID`() = runTest { + // Given + val nonExistingProjectId = UUID.randomUUID() + coEvery { taskDataSource.getTaskByProjectId(nonExistingProjectId) } returns emptyList() + + // When + val result = repository.getTaskByProjectId(nonExistingProjectId) + + // Then + assertThat(result).isEmpty() + coVerify { taskDataSource.getTaskByProjectId(nonExistingProjectId) } + } +} \ No newline at end of file