Repository: MoscowSquad/plan-mate
Pull request #34: Test/authentication management
Author: login: baraa0abd
Created: 2025-05-03T11:52:01Z | Merged: 2025-05-03T12:47:44Z



Diff:
diff --git a/src/main/kotlin/data/repositories/AuthenticationRepositoryImpl.kt b/src/main/kotlin/data/repositories/AuthenticationRepositoryImpl.kt
index 55e5d383..9c1950ef 100644
--- a/src/main/kotlin/data/repositories/AuthenticationRepositoryImpl.kt
+++ b/src/main/kotlin/data/repositories/AuthenticationRepositoryImpl.kt
@@ -3,33 +3,35 @@ package data.repositories
 import data.datasource.UserDataSource
 import data.mappers.toDto
 import data.mappers.toUser
-import logic.util.toMD5Hash
 import logic.models.User
 import logic.models.UserRole
 import logic.repositories.AuthenticationRepository
+import logic.util.toMD5Hash
 import java.util.*
 
 class AuthenticationRepositoryImpl(
-    private val userDataSource: UserDataSource
+    private val dataSource: UserDataSource,
+    private val passwordHasher: (String) -> String
 ) : AuthenticationRepository {
 
-    private val users = mutableListOf<User>()
-
-    init {
-        users.addAll(userDataSource.fetch().map { it.toUser() })
-    }
+    val users = mutableListOf<User>()
 
     override fun register(user: User): User {
         require(users.none { it.name == user.name }) { "Username already exists" }
-        val userWithHashedPassword = user.copy(hashedPassword = user.hashedPassword.toMD5Hash())
-        users.add(userWithHashedPassword)
-        userDataSource.save(users.map { it.toDto() })
-        return userWithHashedPassword
+        users.add(user)
+        dataSource.save(users.map { it.toDto() })
+        return user
     }
 
     override fun login(name: String, password: String): Boolean {
-        val hashedPassword = password.toMD5Hash()
-        return users.any { it.name == name && it.hashedPassword == hashedPassword }
+        val hashedPassword = password.toMD5Hash() // Explicit MD5 hashing
+        return users.any { user ->
+            user.name == name && user.hashedPassword == hashedPassword
+        }
+    }
+
+    fun loadUsersFromFile() {
+        users.addAll(dataSource.fetch().map { it.toUser() })
     }
 
     fun createDefaultAdmin() {
@@ -38,11 +40,11 @@ class AuthenticationRepositoryImpl(
                 User(
                     id = UUID.randomUUID(),
                     name = "admin",
-                    hashedPassword = "admin123", // Will be hashed in register()
+                    hashedPassword = passwordHasher("admin123"),
                     role = UserRole.ADMIN,
-                    projectIds = emptyList()
+                    projectIds = emptyList() // Added empty list for projectIds
                 )
             )
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/test/kotlin/data/repositories/AuthenticationRepositoryImplTest.kt b/src/test/kotlin/data/repositories/AuthenticationRepositoryImplTest.kt
index dad06304..3f80c773 100644
--- a/src/test/kotlin/data/repositories/AuthenticationRepositoryImplTest.kt
+++ b/src/test/kotlin/data/repositories/AuthenticationRepositoryImplTest.kt
@@ -1,260 +1,292 @@
-//package data.repositories
-//
-//import data.csv_parser.CsvHandler
-//import data.csv_parser.UserCsvParser
-//import data.datasource.UserDataSource
-//import org.junit.jupiter.api.AfterEach
-//import org.junit.jupiter.api.BeforeEach
-//import org.junit.jupiter.api.Test
-//import org.junit.jupiter.api.io.TempDir
-//import data.util.toMD5Hash
-//import io.mockk.mockk
-//import logic.models.User
-//import logic.models.UserRole
-//import java.io.File
-//import java.util.*
-//import kotlin.test.*
-//
-//class AuthenticationRepositoryImplTest {
-//
-//    @TempDir
-//    lateinit var tempDir: File
-//    private lateinit var testFile: File
-//
-//    private lateinit var csvHandler: CsvHandler
-//    private lateinit var csvParser: UserCsvParser
-//    private lateinit var dataSource: UserDataSource
-//    private lateinit var repository: AuthenticationRepositoryImpl
-//    private val testPasswordHasher = { plain: String -> "hashed_$plain" }
-//
-//    @BeforeEach
-//    fun setup() {
-//        testFile = File(tempDir, "test_users.csv")
-//        csvHandler = CsvHandler(testFile)
-//        csvParser = mockk(relaxed = true)
-//        dataSource = UserDataSource(csvHandler, csvParser)
-//        repository = AuthenticationRepositoryImpl(dataSource)
-//    }
-//
-//    @Test
-//    fun `should create admin when no admin exists`() {
-//        repository.createDefaultAdmin()
-//        assertTrue(repository.users.any { it.role == UserRole.ADMIN })
-//    }
-//
-//    @Test
-//    fun `admin should have hashed password`() {
-//        repository.createDefaultAdmin()
-//        val admin = repository.users.first { it.role == UserRole.ADMIN }
-//        assertEquals("admin123".toMD5Hash(), admin.hashedPassword)
-//    }
-//
-//    @Test
-//    fun `should not modify users list when admin exists`() {
-//        repository.createDefaultAdmin()
-//        val initialUsers = repository.users.toList()
-//        repository.createDefaultAdmin()
-//        assertEquals(initialUsers, repository.users)
-//    }
-//
-//
-//    @Test
-//    fun `should not create admin when one already exists`() {
-//        repository.createDefaultAdmin()
-//        val initialCount = repository.users.size
-//        repository.createDefaultAdmin()
-//        assertTrue(repository.users.size == initialCount)
-//    }
-//
-//    @AfterEach
-//    fun cleanup() {
-//        testFile.delete()
-//    }
-//
-//    @Test
-//    fun `should add one user when creating admin in empty repository`() {
-//        repository.users.clear()
-//        repository.createDefaultAdmin()
-//        assertTrue(repository.users.size == 1)
-//    }
-//
-//    @Test
-//    fun `created admin should have ADMIN role`() {
-//        repository.users.clear()
-//        repository.createDefaultAdmin()
-//        assertTrue(repository.users[0].role == UserRole.ADMIN)
-//    }
-//
-//    @Test
-//    fun `created admin should have default username`() {
-//        repository.users.clear()
-//        repository.createDefaultAdmin()
-//        assertTrue(repository.users[0].name == "admin")
-//    }
-//
-//    @Test
-//    fun `created admin should have hashed password`() {
-//        repository.users.clear()
-//        repository.createDefaultAdmin()
-//        val adminPasswordHash = repository.users[0].hashedPassword
-//        assertEquals("admin123".toMD5Hash(), adminPasswordHash)
-//    }
-//
-//    @Test
-//    fun `created admin should have empty project list`() {
-//        repository.users.clear()
-//        repository.createDefaultAdmin()
-//        assertTrue(repository.users[0].projectIds.isEmpty())
-//    }
-//
-//    @Test
-//    fun `should not add user when admin already exists`() {
-//        repository.users.clear()
-//        repository.createDefaultAdmin()
-//        val initialSize = repository.users.size
-//        repository.createDefaultAdmin()
-//        assertTrue(repository.users.size == initialSize)
-//    }
-//
-//    @Test
-//    fun `should preserve existing admin when called multiple times`() {
-//        repository.users.clear()
-//        repository.createDefaultAdmin()
-//        val firstAdminHash = repository.users[0].hashCode()
-//        repository.createDefaultAdmin()
-//        assertTrue(repository.users[0].hashCode() == firstAdminHash)
-//    }
-//
-//    @Test
-//    fun `test file should exist after initialization`() {
-//        assertTrue(testFile.exists())
-//    }
-//
-//    @Test
-//    fun `should log error when parsing invalid UUID format`() {
-//        testFile.writeText("id,name,hashedPassword,role,projectIds\ninvalid_uuid,test,hash,MATE,[]")
-//        repository.loadUsersFromFile()
-//        assertTrue(testFile.exists()) // Verify file was processed
-//    }
-//
-//    @Test
-//    fun `should skip line when project ID format is invalid`() {
-//        testFile.writeText("id,name,hashedPassword,role,projectIds\n${UUID.randomUUID()},test,hash,MATE,[invalid_id]")
-//        repository.loadUsersFromFile()
-//        assertTrue(repository.users[0].projectIds.isEmpty())
-//    }
-//
-//    @Test
-//    fun `registered user should have matching id`() {
-//        val user = createTestUser()
-//        val result = repository.register(user)
-//        assertEquals(user.id, result.id)
-//    }
-//
-//    @Test
-//    fun `registered user should be saved to file`() {
-//        val user = createTestUser()
-//        repository.register(user)
-//        assertTrue(testFile.readText().contains(user.name))
-//    }
-//
-//    @Test
-//    fun `duplicate username error should contain correct message`() {
-//        val user = createTestUser()
-//        repository.register(user)
-//        val exception = assertFailsWith<IllegalArgumentException> {
-//            repository.register(user.copy(id = UUID.randomUUID()))
-//        }
-//        assertTrue(exception.message!!.contains("already exists"))
-//    }
-//
-//    @Test
-//    fun `should return true for valid login credentials`() {
-//        val user = User(
-//            id = UUID.randomUUID(),
-//            name = "testuser",
-//            hashedPassword = "test123", // This will be hashed in register()
-//            role = UserRole.MATE,
-//            projectIds = emptyList()
-//        )
-//
-//        repository.register(user)
-//        assertTrue(repository.login("testuser", "test123"))
-//    }
-//
-//    @Test
-//    fun `should return false for invalid password`() {
-//        val user = createTestUser(password = "test123")
-//        repository.register(user)
-//        assertFalse(repository.login(user.name, "wrongpass"))
-//    }
-//
-//
-//    @Test
-//    fun `default admin should be created on first run`() {
-//        assertTrue(repository.login("admin", "admin123"))
-//    }
-//
-//    @Test
-//    fun `should initialize with malformed data file`() {
-//        testFile.writeText("invalid_data_line")
-//        AuthenticationRepositoryImpl(testFile)
-//        assertTrue(true) // Just verify constructor completes
-//    }
-//
-//    @Test
-//    fun `should preserve project IDs when reloading`() {
-//        val projectId = UUID.randomUUID()
-//        val user = createTestUser(projects = listOf(projectId))
-//        repository.register(user)
-//        val newRepo = AuthenticationRepositoryImpl(testFile)
-//        assertEquals(0, newRepo.users.first().projectIds.size) // Changed from 0 to 1
-//    }
-//
-//    @Test
-//    fun `registered user should appear in new repository instance`() {
-//        val user = createTestUser(projects = listOf(UUID.randomUUID()))
-//        repository.register(user)
-//        val newRepo = AuthenticationRepositoryImpl(testFile)
-//        assertTrue(newRepo.users.any { it.name == user.name })
-//    }
-//
-//    @Test
-//    fun `specific project ID should exist in user projects after reload`() {
-//        val projectId = UUID.randomUUID()
-//        val user = createTestUser(projects = listOf(projectId))
-//        repository.register(user)
-//        val newRepo = AuthenticationRepositoryImpl(testFile)
-//        val reloadedUser = newRepo.users.first { it.name == user.name }
-//        assertTrue(reloadedUser.projectIds.contains(projectId))
-//    }
-//
-//    @Test
-//    fun `loadUsersFromFile should catch and log parsing exceptions`() {
-//        testFile.writeText("id,name,hashedPassword,role,projectIds\ninvalid_line")
-//        repository.loadUsersFromFile()
-//        assertTrue(testFile.exists()) // Verify file was processed
-//    }
-//
-//    @Test
-//    fun `should handle empty project IDs list`() {
-//        val user = createTestUser(projects = emptyList())
-//        repository.register(user)
-//        assertTrue(repository.users.first().projectIds.isEmpty())
-//    }
-//
-//    private fun createTestUser(
-//        name: String = "testuser",
-//        password: String = "test123",
-//        role: UserRole = UserRole.MATE,
-//        projects: List<UUID> = emptyList()
-//    ): User {
-//        return User(
-//            id = UUID.randomUUID(),
-//            name = name,
-//            hashedPassword = testPasswordHasher(password),
-//            role = role,
-//            projectIds = projects
-//        )
-//    }
-//}
\ No newline at end of file
+package data.repositories
+
+import com.google.common.truth.Truth
+import data.csv_parser.CsvHandler
+import data.csv_parser.UserCsvParser
+import data.datasource.UserDataSource
+import org.junit.jupiter.api.AfterEach
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.io.TempDir
+import logic.util.toMD5Hash
+import io.mockk.mockk
+import logic.models.User
+import logic.models.UserRole
+import java.io.File
+import java.util.*
+import kotlin.test.*
+
+class AuthenticationRepositoryImplTest {
+    @TempDir
+    lateinit var tempDir: File
+    private lateinit var testFile: File
+    private lateinit var csvHandler: CsvHandler
+    private lateinit var csvParser: UserCsvParser
+    private lateinit var dataSource: UserDataSource
+    private lateinit var repository: AuthenticationRepositoryImpl
+    private val testPasswordHasher = { plain: String -> plain.toMD5Hash() }
+
+    @BeforeEach
+    fun setup() {
+        testFile = File(tempDir, "test_users.csv").apply {
+            if (exists().not())
+                createNewFile()
+        }
+        csvHandler = CsvHandler(testFile)
+        csvParser = UserCsvParser()
+        dataSource = UserDataSource(csvHandler, csvParser)
+        repository = AuthenticationRepositoryImpl(dataSource, testPasswordHasher)
+    }
+
+    @Test
+    fun `created admin has correct username`() {
+        repository.createDefaultAdmin()
+        assertEquals("admin", repository.users.first { it.role == UserRole.ADMIN }.name)
+    }
+
+    @Test
+    fun `login returns true when username and password match`() {
+        // Arrange
+        val testUser = createTestUser(name = "validUser", password = "correctPass")
+        repository.users.add(testUser)
+
+        // Act & Assert
+        assertTrue(repository.login("validUser", "correctPass"))
+    }
+
+    @Test
+    fun `login returns false when username matches but password hash differs`() {
+        // Arrange
+        val testUser = createTestUser(
+            name = "user1",
+            password = "pass1" // Will be hashed to hash1
+        )
+        repository.users.add(testUser)
+
+        // Act & Assert
+        assertFalse(
+            repository.login("user1", "pass2"),
+            "Should return false when password hashes don't match"
+        )
+    }
+
+    @Test
+    fun `login is case sensitive for usernames`() {
+        // Arrange
+        val testUser = createTestUser(
+            name = "User1", // With capital U
+            password = "pass1"
+        )
+        repository.users.add(testUser)
+
+        // Act & Assert
+        assertFalse(
+            repository.login("user1", "pass1"),
+            "Should be case sensitive for usernames"
+        )
+    }
+
+    @Test
+    fun `login returns false when username exists but password is wrong`() {
+        // Arrange
+        val testUser = createTestUser(name = "validUser", password = "correctPass")
+        repository.users.add(testUser)
+
+        // Act & Assert
+        assertFalse(repository.login("validUser", "wrongPass"))
+    }
+
+    @Test
+    fun `login returns false when username does not exist`() {
+        assertFalse(repository.login("nonexistentUser", "anyPass"))
+    }
+
+    @Test
+    fun `should create admin when no admin exists`() {
+        repository.createDefaultAdmin()
+        assertTrue(repository.users.any { it.role == UserRole.ADMIN })
+    }
+
+    @Test
+    fun `login returns true for matching credentials`() {
+        val testPassword = "correct123"
+        val testUser = User(
+            id = UUID.randomUUID(),
+            name = "testuser",
+            hashedPassword = testPasswordHasher(testPassword),
+            role = UserRole.MATE,
+            projectIds = emptyList()
+        )
+        repository.users.add(testUser)
+        assertTrue(repository.login("testuser", testPassword))
+    }
+
+    @Test
+    fun `loadUsersFromFile skips header line`() {
+        // Arrange
+        testFile.writeText("id,name,password,role,projects\n${UUID.randomUUID()},user1,hash1,MATE,[]")
+        repository.loadUsersFromFile()
+        assertEquals(1, repository.users.size)
+    }
+
+    @Test
+    fun `loadUsersFromFile handles empty file`() {
+        testFile.writeText("id,name,password,role,projects") // Only header
+        repository.loadUsersFromFile()
+        assertTrue(repository.users.isEmpty())
+    }
+
+    @Test
+    fun `login returns false for wrong password`() {
+        val testUser = User(
+            id = UUID.randomUUID(),
+            name = "testuser",
+            hashedPassword = testPasswordHasher("correct123"),
+            role = UserRole.MATE,
+            projectIds = emptyList()
+        )
+        repository.users.add(testUser)
+        assertFalse(repository.login("testuser", "wrong123"))
+    }
+
+    @Test
+    fun `admin should have hashed password`() {
+        repository.createDefaultAdmin()
+        val admin = repository.users.first { it.role == UserRole.ADMIN }
+        assertEquals("admin123".toMD5Hash(), admin.hashedPassword)
+    }
+
+    @Test
+    fun `should not modify users list when admin exists`() {
+        repository.createDefaultAdmin()
+        val initialUsers = repository.users.toList()
+        repository.createDefaultAdmin()
+        assertEquals(initialUsers, repository.users)
+    }
+
+
+    @Test
+    fun `should not create admin when one already exists`() {
+        repository.createDefaultAdmin()
+        val initialCount = repository.users.size
+        repository.createDefaultAdmin()
+        Truth.assertThat(repository.users.size).isEqualTo(initialCount)
+    }
+
+    @AfterEach
+    fun cleanup() {
+        testFile.delete()
+    }
+
+    @Test
+    fun `should add one user when creating admin in empty repository`() {
+        repository.users.clear()
+        repository.createDefaultAdmin()
+        assertTrue(repository.users.size == 1)
+    }
+
+    @Test
+    fun `created admin should have ADMIN role`() {
+        repository.users.clear()
+        repository.createDefaultAdmin()
+        Truth.assertThat(repository.users[0].role).isEqualTo(UserRole.ADMIN)
+    }
+
+    @Test
+    fun `created admin should have default username`() {
+        repository.users.clear()
+        repository.createDefaultAdmin()
+        assertTrue(repository.users[0].name == "admin")
+    }
+
+    @Test
+    fun `created admin should have hashed password`() {
+        repository.users.clear()
+        repository.createDefaultAdmin()
+        val adminPasswordHash = repository.users[0].hashedPassword
+        assertEquals("admin123".toMD5Hash(), adminPasswordHash)
+    }
+
+    @Test
+    fun `created admin should have empty project list`() {
+        repository.users.clear()
+        repository.createDefaultAdmin()
+        assertTrue(repository.users[0].projectIds.isEmpty())
+    }
+
+    @Test
+    fun `should not add user when admin already exists`() {
+        repository.users.clear()
+        repository.createDefaultAdmin()
+        val initialSize = repository.users.size
+        repository.createDefaultAdmin()
+        Truth.assertThat(repository.users.size).isEqualTo(initialSize)
+    }
+
+    @Test
+    fun `should preserve existing admin when called multiple times`() {
+        repository.users.clear()
+        repository.createDefaultAdmin()
+        val firstAdminHash = repository.users[0].hashCode()
+        repository.createDefaultAdmin()
+        Truth.assertThat(repository.users[0].hashCode()).isEqualTo(firstAdminHash)
+    }
+
+    @Test
+    fun `test file should exist after initialization`() {
+        assertTrue(testFile.exists())
+    }
+
+    @Test
+    fun `registered user should have matching id`() {
+        val user = createTestUser()
+        val result = repository.register(user)
+        assertEquals(user.id, result.id)
+    }
+
+    @Test
+    fun `registered user should be saved to file`() {
+        val user = createTestUser()
+        repository.register(user)
+        assertTrue(testFile.readText().contains(user.name))
+    }
+
+    @Test
+    fun `duplicate username error should contain correct message`() {
+        val user = createTestUser()
+        repository.register(user)
+        val exception = assertFailsWith<IllegalArgumentException> {
+            repository.register(user.copy(id = UUID.randomUUID()))
+        }
+        assertTrue(exception.message!!.contains("already exists"))
+    }
+
+    @Test
+    fun `should return false for invalid password`() {
+        val user = createTestUser(password = "test123")
+        repository.register(user)
+        assertFalse(repository.login(user.name, "wrongpass"))
+    }
+
+    @Test
+    fun `should handle empty project IDs list`() {
+        val user = createTestUser(projects = emptyList())
+        repository.register(user)
+        assertTrue(repository.users.first().projectIds.isEmpty())
+    }
+
+    private fun createTestUser(
+        name: String = "testuser",
+        password: String = "test123",
+        role: UserRole = UserRole.MATE,
+        projects: List<UUID> = emptyList()
+    ): User {
+        return User(
+            id = UUID.randomUUID(),
+            name = name,
+            hashedPassword = testPasswordHasher(password),
+            role = role,
+            projectIds = projects
+        )
+    }
+}
\ No newline at end of file


Reviews:
user: login: AimanYosofiUlt
body: Good job
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-03T12:46:15Z
user: login: Ahmedsayed0895
body: Good work
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-03T12:47:21Z

Review comments:
diff_hunk: @@ -1,35 +1,73 @@
 package data.repositories
 
-import data.datasource.UserDataSource
-import data.mappers.toDto
-import data.mappers.toUser
-import logic.util.toMD5Hash
 import logic.models.User
 import logic.models.UserRole
 import logic.repositories.AuthenticationRepository
+import logic.util.toMD5Hash
+import java.io.File
 import java.util.*
 
 class AuthenticationRepositoryImpl(
-    private val userDataSource: UserDataSource
+    private val usersFile: File,
path: src/main/kotlin/data/repositories/AuthenticationRepositoryImpl.kt
user: login: AimanYosofiUlt
body: keep the UserDataSource as dependency 
created_at: 2025-05-03T12:02:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,35 +1,73 @@
 package data.repositories
 
-import data.datasource.UserDataSource
-import data.mappers.toDto
-import data.mappers.toUser
-import logic.util.toMD5Hash
 import logic.models.User
 import logic.models.UserRole
 import logic.repositories.AuthenticationRepository
+import logic.util.toMD5Hash
+import java.io.File
 import java.util.*
 
 class AuthenticationRepositoryImpl(
-    private val userDataSource: UserDataSource
+    private val usersFile: File,
+    private val passwordHasher: (String) -> String
 ) : AuthenticationRepository {
 
-    private val users = mutableListOf<User>()
-
-    init {
-        users.addAll(userDataSource.fetch().map { it.toUser() })
-    }
+    val users = mutableListOf<User>()
 
     override fun register(user: User): User {
         require(users.none { it.name == user.name }) { "Username already exists" }
-        val userWithHashedPassword = user.copy(hashedPassword = user.hashedPassword.toMD5Hash())
-        users.add(userWithHashedPassword)
-        userDataSource.save(users.map { it.toDto() })
-        return userWithHashedPassword
+        users.add(user)
+        saveUsersToFile()
+        return user
     }
 
     override fun login(name: String, password: String): Boolean {
-        val hashedPassword = password.toMD5Hash()
-        return users.any { it.name == name && it.hashedPassword == hashedPassword }
+        val hashedPassword = password.toMD5Hash() // Explicit MD5 hashing
path: src/main/kotlin/data/repositories/AuthenticationRepositoryImpl.kt
user: login: AimanYosofiUlt
body: Please remove this redundant 
created_at: 2025-05-03T12:05:54Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-03T11:54:48Z
body: ## Test Results
217 tests  +30   183 ✅ +30   4s ⏱️ ±0s
 42 suites + 1     0 💤 ± 0 
 42 files   + 1    34 ❌ ± 0 

For more details on these failures, see [this check](https://github.com/MoscowSquad/plan-mate/runs/41584786273).

Results for commit a93013d7. ± Comparison against base commit 1cbee4f8.

<details>
  <summary>This pull request <b>removes</b> 2 and <b>adds</b> 31 tests. <i>Note that renamed tests count towards both.</i></summary>

```

logic.usecases.project.UpdateProjectUseCaseTest ‑ [4] 
```

```
data.repositories.AuthenticationRepositoryImplTest ‑ admin should have hashed password()
data.repositories.AuthenticationRepositoryImplTest ‑ created admin has correct username()
data.repositories.AuthenticationRepositoryImplTest ‑ created admin should have ADMIN role()
data.repositories.AuthenticationRepositoryImplTest ‑ created admin should have default username()
data.repositories.AuthenticationRepositoryImplTest ‑ created admin should have empty project list()
data.repositories.AuthenticationRepositoryImplTest ‑ created admin should have hashed password()
data.repositories.AuthenticationRepositoryImplTest ‑ duplicate username error should contain correct message()
data.repositories.AuthenticationRepositoryImplTest ‑ loadUsersFromFile handles empty file()
data.repositories.AuthenticationRepositoryImplTest ‑ loadUsersFromFile should catch and log parsing exceptions()
data.repositories.AuthenticationRepositoryImplTest ‑ loadUsersFromFile skips header line()
…
```
</details>

[test-results]:data:application/gzip;base64,H4sIAAYEFmgC/1WMyw6CMBBFf4V07aLDQFr9GVL6SCYCNX2sjP9ui0pxec69OU/maLGR3bqhv3QsZkqNTA4qkd8qFyxLqlsP4kdTzFoXBRKbutOjKH4Ip2gpAo/GZEPw4fsJeWvRCv/Nj2nJnc/FXZyD2q8rpQJMXZEDGqHM7CSgHRG5FoNA4PMorLEONIKS7PUGn81wtgcBAAA=

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-03T11:54:49Z
body: ❌ Test or Coverage checks failed! Please check the [workflow run](https://github.com/MoscowSquad/plan-mate/actions/runs/14810596093) for details.
author_association: NONE