diff --git a/backend/src/test/kotlin/com/opendatamask/connector/AzureSQLConnectorTest.kt b/backend/src/test/kotlin/com/opendatamask/connector/AzureSQLConnectorTest.kt new file mode 100644 index 0000000..7523a2d --- /dev/null +++ b/backend/src/test/kotlin/com/opendatamask/connector/AzureSQLConnectorTest.kt @@ -0,0 +1,75 @@ +package com.opendatamask.connector + +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Assertions.* + +class AzureSQLConnectorTest { + + // Use H2 in MSSQLServer compatibility mode with null credentials so + // AzureSQLConnector takes the simple DriverManager.getConnection(url) path. + private val h2Url = + "jdbc:h2:mem:aztest_${System.nanoTime()};DB_CLOSE_DELAY=-1;MODE=MSSQLServer;DATABASE_TO_UPPER=FALSE" + private lateinit var connector: AzureSQLConnector + + @BeforeEach + fun setup() { + connector = AzureSQLConnector(h2Url, null, null) + java.sql.DriverManager.getConnection(h2Url).use { conn -> + conn.createStatement().execute( + "CREATE TABLE IF NOT EXISTS test_users (id INT, name VARCHAR(100), email VARCHAR(200))" + ) + conn.createStatement().execute("DELETE FROM test_users") + conn.createStatement().execute("INSERT INTO test_users VALUES (1, 'Alice', 'alice@example.com')") + conn.createStatement().execute("INSERT INTO test_users VALUES (2, 'Bob', 'bob@example.com')") + } + } + + @Test + fun `testConnection returns true for valid connection`() { + assertTrue(connector.testConnection()) + } + + @Test + fun `fetchData returns all rows without limit or filter`() { + val rows = connector.fetchData("test_users") + assertEquals(2, rows.size) + } + + @Test + fun `fetchData respects row limit`() { + val rows = connector.fetchData("test_users", limit = 1) + assertEquals(1, rows.size) + } + + @Test + fun `fetchData with whereClause filters rows`() { + val rows = connector.fetchData("test_users", whereClause = "name = 'Alice'") + assertEquals(1, rows.size) + val name = rows[0]["NAME"] ?: rows[0]["name"] + assertEquals("Alice", name) + } + + @Test + fun `writeData inserts rows and returns count`() { + val rows = listOf( + mapOf("id" to 3, "name" to "Charlie", "email" to "charlie@example.com") + ) + val count = connector.writeData("test_users", rows) + assertEquals(1, count) + val all = connector.fetchData("test_users") + assertEquals(3, all.size) + } + + @Test + fun `writeData returns 0 for empty list`() { + assertEquals(0, connector.writeData("test_users", emptyList())) + } + + @Test + fun `truncateTable removes all rows`() { + connector.truncateTable("test_users") + val rows = connector.fetchData("test_users") + assertTrue(rows.isEmpty()) + } +} diff --git a/backend/src/test/kotlin/com/opendatamask/connector/MongoDBConnectorTest.kt b/backend/src/test/kotlin/com/opendatamask/connector/MongoDBConnectorTest.kt index 622e20a..1f0921a 100644 --- a/backend/src/test/kotlin/com/opendatamask/connector/MongoDBConnectorTest.kt +++ b/backend/src/test/kotlin/com/opendatamask/connector/MongoDBConnectorTest.kt @@ -1,11 +1,13 @@ package com.opendatamask.connector +import com.mongodb.client.FindIterable import com.mongodb.client.MongoClient import com.mongodb.client.MongoCollection import com.mongodb.client.MongoDatabase import org.bson.Document import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* +import org.mockito.Mockito import org.mockito.kotlin.* class MongoDBConnectorTest { @@ -16,6 +18,70 @@ class MongoDBConnectorTest { } } + /** + * Returns a FindIterable mock that uses RETURNS_DEEP_STUBS so that the + * MongoIterable.map(...).toList() call chain in fetchData() works without NPE. + * fetchData() will return an empty list, but the filter/limit arguments + * passed to find()/limit() can still be verified. + */ + private fun mockFindIterable(): FindIterable { + val iterable = mock>(defaultAnswer = Mockito.RETURNS_DEEP_STUBS) + whenever(iterable.limit(any())).thenReturn(iterable) + return iterable + } + + @Test + fun `fetchData uses empty filter when no whereClause provided`() { + val mockCollection = mock>() + val mockDb = mock() + val mockClient = mock() + val findIterable = mockFindIterable() + + whenever(mockClient.getDatabase("testdb")).thenReturn(mockDb) + whenever(mockDb.getCollection("users")).thenReturn(mockCollection) + whenever(mockCollection.find(any())).thenReturn(findIterable) + + val connector = createConnector(mockClient) + connector.fetchData("users") + + verify(mockCollection).find(Document()) + } + + @Test + fun `fetchData applies JSON filter when whereClause provided`() { + val mockCollection = mock>() + val mockDb = mock() + val mockClient = mock() + val findIterable = mockFindIterable() + val expectedFilter = Document.parse("""{"age": 18}""") + + whenever(mockClient.getDatabase("testdb")).thenReturn(mockDb) + whenever(mockDb.getCollection("users")).thenReturn(mockCollection) + whenever(mockCollection.find(any())).thenReturn(findIterable) + + val connector = createConnector(mockClient) + connector.fetchData("users", whereClause = """{"age": 18}""") + + verify(mockCollection).find(expectedFilter) + } + + @Test + fun `fetchData applies limit when specified`() { + val mockCollection = mock>() + val mockDb = mock() + val mockClient = mock() + val findIterable = mockFindIterable() + + whenever(mockClient.getDatabase("testdb")).thenReturn(mockDb) + whenever(mockDb.getCollection("users")).thenReturn(mockCollection) + whenever(mockCollection.find(any())).thenReturn(findIterable) + + val connector = createConnector(mockClient) + connector.fetchData("users", limit = 5) + + verify(findIterable).limit(5) + } + @Test fun `writeData calls insertMany and returns row count`() { val mockCollection = mock>()