Skip to content

Commit

Permalink
chore: add tests to the modal
Browse files Browse the repository at this point in the history
  • Loading branch information
kmruiz committed Jan 28, 2025
1 parent a970007 commit 2d1d48a
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class RunQueryModal(

fieldsForContext += Triple(fieldName.fieldName, type, input)

input.name = fieldName.fieldName
builder.addLabeledComponent(label, input, 8)
if (toolTip != null) {
builder.addTooltip(toolTip)
Expand Down Expand Up @@ -193,7 +194,7 @@ class RunQueryModal(
}
}

private fun buildQueryContextFromModal(): QueryContext {
internal fun buildQueryContextFromModal(): QueryContext {
val localVariables = fieldsForContext.groupBy { it.first }
.mapValues { it.value.first().let { it.second to it.third } }
.mapValues { (_, value) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ class NamespaceSelectorTest {
selector.databaseComboBox.isVisible = true
selector.collectionComboBox.isVisible = true
frame.isVisible = true
frame.pack()

FrameFixture(robot, frame) to selector
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,34 @@ package com.mongodb.jbplugin.codeActions.impl.runQuery
import com.intellij.database.dataSource.LocalDataSource
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.mongodb.jbplugin.accessadapter.slice.ListCollections
import com.mongodb.jbplugin.accessadapter.slice.ListDatabases
import com.mongodb.jbplugin.fixtures.IntegrationTest
import com.mongodb.jbplugin.fixtures.mockDataSource
import com.mongodb.jbplugin.fixtures.mockReadModelProvider
import com.mongodb.jbplugin.fixtures.parseJavaQuery
import com.mongodb.jbplugin.mql.BsonBoolean
import com.mongodb.jbplugin.mql.BsonString
import com.mongodb.jbplugin.mql.Node
import kotlinx.coroutines.CoroutineScope
import org.assertj.swing.core.Robot
import org.assertj.swing.core.matcher.JLabelMatcher
import org.assertj.swing.edt.GuiActionRunner
import org.assertj.swing.exception.ComponentLookupException
import org.assertj.swing.fixture.FrameFixture
import org.bson.types.ObjectId
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import org.mockito.kotlin.whenever
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.util.*
import javax.swing.JFrame
import kotlin.reflect.KClass

@IntegrationTest
class RunQueryModalTest {
Expand All @@ -24,9 +39,9 @@ class RunQueryModalTest {
val dataSource = mockDataSource()

val query = project.parseJavaQuery(
code = """
"""
public Document find() {
return this.client.getDatabase("prod").getCollection("books").find(eq("_id", id)).first();
return this.client.getDatabase("prod").getCollection("books").find(eq("_id", 1234)).first();
}
"""
)
Expand All @@ -50,9 +65,9 @@ class RunQueryModalTest {
val dataSource = mockDataSource()

val query = project.parseJavaQuery(
code = """
"""
public Document find() {
return this.client.getDatabase("prod").getCollection("books").find(eq("_id", id)).first();
return this.client.getDatabase("prod").getCollection("books").find(eq("_id", 1234)).first();
}
"""
)
Expand All @@ -64,35 +79,185 @@ class RunQueryModalTest {
}

@Test
fun `if the collection can not be inferred it does show the namespace selector`(
fun `if the collection can not be inferred it does show the namespace selector and gathers the values for the query context`(
robot: Robot,
project: Project,
coroutineScope: CoroutineScope
) {
val dataSource = mockDataSource()
val readModel = project.mockReadModelProvider()

whenever(readModel.slice(dataSource, ListDatabases.Slice)).thenReturn(
ListDatabases(
listOf(
ListDatabases.Database("db1")
)
)
)

whenever(readModel.slice(dataSource, ListCollections.Slice("db1"))).thenReturn(
ListCollections(
listOf(
ListCollections.Collection("coll1", "collection"),
)
)
)

val query = project.parseJavaQuery(
code = """
public Document find(String db, String coll) {
return this.client.getDatabase(db).getCollection(coll).find(eq("_id", id)).first();
return this.client.getDatabase(db).getCollection(coll).find(eq("_id", 1234)).first();
}
""",
"""
)

val (fixture, _) = render(robot, query, dataSource, coroutineScope)
val (fixture, modal) = render(robot, query, dataSource, coroutineScope)

fixture.comboBox("DatabaseComboBox").requireVisible()
fixture.comboBox("CollectionComboBox").requireVisible()

val queryContext = modal.buildQueryContextFromModal()
val dbInput = queryContext.expansions.getValue("database")
val collInput = queryContext.expansions.getValue("collection")

assertEquals("db1", dbInput.defaultValue)
assertEquals(BsonString, dbInput.type)

assertEquals("coll1", collInput.defaultValue)
assertEquals(BsonString, collInput.type)
}

@Test
fun `should show runtime values as inputs and build the context from there`(
robot: Robot,
project: Project,
coroutineScope: CoroutineScope
) {
val dataSource = mockDataSource()

val query = project.parseJavaQuery(
"""
public Document find(String id) {
return this.client.getDatabase("prod").getCollection("books").find(eq("_id", id)).first();
}
"""
)

val (fixture, modal) = render(robot, query, dataSource, coroutineScope)
fixture.textBox("_id").requireVisible().focus().setText("myId")

val queryContext = modal.buildQueryContextFromModal()
val idInput = queryContext.expansions.getValue("_id")

assertEquals("myId", idInput.defaultValue)
assertEquals(BsonString, idInput.type)
}

@ParameterizedTest
@MethodSource("javaTypeToFormatHint")
fun `should show hints for the specified types`(
javaType: KClass<*>,
expectedHint: String,
robot: Robot,
project: Project,
coroutineScope: CoroutineScope,
) {
val dataSource = mockDataSource()

val query = project.parseJavaQuery(
"""
public Document find(${javaType.simpleName} id) {
return this.client.getDatabase("prod").getCollection("books").find(eq("_id", id)).first();
}
"""
)

val (fixture, _) = render(robot, query, dataSource, coroutineScope)
fixture.label(JLabelMatcher.withText(expectedHint)).requireVisible()
}

@ParameterizedTest
@MethodSource("complexTypeSample")
fun `should show the warning message for complex types`(
javaType: KClass<*>,
robot: Robot,
project: Project,
coroutineScope: CoroutineScope
) {
val dataSource = mockDataSource()

val query = project.parseJavaQuery(
"""
public Document find(${javaType.qualifiedName} id) {
return this.client.getDatabase("prod").getCollection("books").find(eq("_id", id)).first();
}
"""
)

val (fixture, _) = render(robot, query, dataSource, coroutineScope)
fixture.label(
JLabelMatcher.withText(
"Unable to specify. Please fill it after generating the query."
)
).requireVisible()
}

@Test
fun `should show boolean runtime values as checkboxes`(
robot: Robot,
project: Project,
coroutineScope: CoroutineScope
) {
val dataSource = mockDataSource()

val query = project.parseJavaQuery(
"""
public Document find(boolean id) {
return this.client.getDatabase("prod").getCollection("books").find(eq("_id", id)).first();
}
"""
)

val (fixture, modal) = render(robot, query, dataSource, coroutineScope)
fixture.checkBox("_id")
.requireVisible()
.focus()
.check(true)

val queryContext = modal.buildQueryContextFromModal()
val idInput = queryContext.expansions.getValue("_id")

assertEquals(true, idInput.defaultValue)
assertEquals(BsonBoolean, idInput.type)
}

companion object {
@JvmStatic
fun javaTypeToFormatHint(): Array<Array<Any>> = arrayOf(
arrayOf(ObjectId::class, "Hexadecimal ObjectId representation"),
arrayOf(Date::class, "ISO 8601 Date: yyyy-MM-dd'T'HH:mm:ss"),
arrayOf(LocalDate::class, "ISO 8601 Date: yyyy-MM-dd'T'HH:mm:ss"),
arrayOf(LocalDateTime::class, "ISO 8601 Date: yyyy-MM-dd'T'HH:mm:ss"),
arrayOf(Instant::class, "ISO 8601 Date: yyyy-MM-dd'T'HH:mm:ss"),
)

@JvmStatic
fun complexTypeSample(): Array<Any> = arrayOf(
List::class,
Array::class,
Map::class,
StringBuffer::class,
)
}

private fun render(robot: Robot, query: Node<PsiElement>, dataSource: LocalDataSource, coroutineScope: CoroutineScope): Pair<FrameFixture, RunQueryModal> {
return GuiActionRunner.execute<Pair<FrameFixture, RunQueryModal>> {
val frame = JFrame()

val modal = RunQueryModal(query, dataSource, coroutineScope)
frame.add(modal.createCenterPanel())
frame.isVisible = true

frame.pack()
FrameFixture(robot, frame) to modal
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ import com.mongodb.client.model.*;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import java.util.List;
import java.util.Arrays;
import java.util.*;
import java.time.*;
import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Accumulators.*;
import static com.mongodb.client.model.Projections.*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ fun String.toBsonType(): BsonType {
) {
return BsonAnyOf(BsonString, BsonNull)
} else if (this == ("java.util.Date") ||
this == ("java.time.Instant") ||
this == ("java.time.LocalDate") ||
this == ("java.time.LocalDateTime")
) {
Expand All @@ -428,6 +429,10 @@ fun String.toBsonType(): BsonType {
val baseType = this.substring(0, this.length - 2)
return BsonArray(baseType.toBsonType())
} else if (this.contains("List") || this.contains("Set")) {
if (!this.contains("<")) { // not passing the generic types, so assume an array of BsonAny
return BsonArray(BsonAny)
}

val baseType = this.substringAfter("<").substringBeforeLast(">")
return BsonArray(baseType.toBsonType())
} else if (this == ("java.util.UUID")) {
Expand Down

0 comments on commit 2d1d48a

Please sign in to comment.