Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,12 @@ class AppFunctionInstrumentationTest {
)
.first()
.flatMap { it.appFunctions }
.single { it.id == AppFunctionsIds.SEND_ID }
.single { it.id == AppFunctionsIds.SEND_MESSAGE_ID }
val testRecipient = recipientsRepository.getAllRecipients().first()
val request =
ExecuteAppFunctionRequest(
targetPackageName = context.packageName,
AppFunctionsIds.SEND_ID,
AppFunctionsIds.SEND_MESSAGE_ID,
AppFunctionData.Builder(
sendMessageFunctionMetadata.parameters,
sendMessageFunctionMetadata.components,
Expand All @@ -91,8 +91,7 @@ class AppFunctionInstrumentationTest {
val successResponse = assertIs<ExecuteAppFunctionResponse.Success>(response)
assertThat(
successResponse.returnValue
.getAppFunctionData(ExecuteAppFunctionResponse.Success.PROPERTY_RETURN_VALUE)
?.getString("message"),
.getString(ExecuteAppFunctionResponse.Success.PROPERTY_RETURN_VALUE),
)
.isEqualTo("Message sent to: Alice Smith.")
// Verify that the message was actually saved in the repository
Expand All @@ -111,12 +110,12 @@ class AppFunctionInstrumentationTest {
)
.first()
.flatMap { it.appFunctions }
.single { it.id == AppFunctionsIds.SEND_ID }
.single { it.id == AppFunctionsIds.SEND_MESSAGE_ID }
val testRecipient = recipientsRepository.getAllRecipients().first()
val request =
ExecuteAppFunctionRequest(
targetPackageName = context.packageName,
AppFunctionsIds.SEND_ID,
AppFunctionsIds.SEND_MESSAGE_ID,
AppFunctionData.Builder(
sendMessageFunctionMetadata.parameters,
sendMessageFunctionMetadata.components,
Expand Down Expand Up @@ -153,7 +152,7 @@ class AppFunctionInstrumentationTest {
getRecipientsFunctionMetadata.components,
)
.setString("query", "Alice")
.setString("filterType", "INDIVIDUAL")
.setString("contactType", "INDIVIDUAL")
.build(),
)

Expand All @@ -167,7 +166,12 @@ class AppFunctionInstrumentationTest {
?.map { it.deserialize(AppFunctions.ContactSearchResult::class.java) },
)
.containsExactly(
AppFunctions.ContactSearchResult(endpointValue = "1", endpointType = "INDIVIDUAL", displayName = "Alice Smith"),
AppFunctions.ContactSearchResult(
contactDisplayName = "Alice Smith",
contactType = "INDIVIDUAL",
endpointValue = "1",
endpointDisplayName = "alice@example.com",
),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ class AppFunctionsTest {
runBlocking {
val contacts = appFunctions.searchContacts(testContext, "Alice", "INDIVIDUAL")
Assert.assertEquals(1, contacts.size)
Assert.assertEquals("Alice Smith", contacts[0].displayName)
Assert.assertEquals("Alice Smith", contacts[0].contactDisplayName)
Assert.assertEquals("INDIVIDUAL", contacts[0].contactType)
Assert.assertEquals("1", contacts[0].endpointValue)
Assert.assertEquals("alice@example.com", contacts[0].endpointDisplayName)
}
}

Expand All @@ -108,95 +111,108 @@ class AppFunctionsTest {
runBlocking {
val contacts = appFunctions.searchContacts(testContext, "Work", "GROUP")
Assert.assertEquals(1, contacts.size)
Assert.assertEquals("Work Friends", contacts[0].displayName)
Assert.assertEquals("Work Friends", contacts[0].contactDisplayName)
Assert.assertEquals("GROUP", contacts[0].contactType)
Assert.assertEquals("g1", contacts[0].endpointValue)
Assert.assertEquals("Work Friends", contacts[0].endpointDisplayName)
}
}

@Test
fun searchContacts_emptyQuery_returnsRecent() {
@Test(expected = AppFunctionInvalidArgumentException::class)
fun searchContacts_emptyQuery_fails() {
runBlocking {
val contacts = appFunctions.searchContacts(testContext, "", "INDIVIDUAL")
Assert.assertEquals(3, contacts.size)
appFunctions.searchContacts(testContext, "", "INDIVIDUAL")
}
}

@Test
fun searchContacts_anyType_returnsMatches() {
runBlocking {
// "searchAny" branch (when filterType is not INDIVIDUAL or GROUP)
val contacts = appFunctions.searchContacts(testContext, "Alice", "ANY")
Assert.assertEquals(1, contacts.size)
Assert.assertEquals("Alice Smith", contacts[0].displayName)
Assert.assertEquals("Alice Smith", contacts[0].contactDisplayName)
Assert.assertEquals("INDIVIDUAL", contacts[0].contactType)
Assert.assertEquals("1", contacts[0].endpointValue)
Assert.assertEquals("alice@example.com", contacts[0].endpointDisplayName)
}
}

@Test
fun searchContacts_duplicateNamesReturnedAsSeparate() {
runBlocking {
val contacts = appFunctions.searchContacts(testContext, "Bob", "INDIVIDUAL")
Assert.assertEquals(2, contacts.size)

Assert.assertEquals("Bob Johnson", contacts[0].contactDisplayName)
Assert.assertEquals("INDIVIDUAL", contacts[0].contactType)
Assert.assertEquals("2", contacts[0].endpointValue)
Assert.assertEquals("bob@example.com", contacts[0].endpointDisplayName)

Assert.assertEquals("Bob Johnson", contacts[1].contactDisplayName)
Assert.assertEquals("INDIVIDUAL", contacts[1].contactType)
Assert.assertEquals("7", contacts[1].endpointValue)
Assert.assertEquals("bob2@example.com", contacts[1].endpointDisplayName)
}
}

@Test
fun send_validMessage_returnsSuccess() {
fun sendMessage_validMessage_returnsSuccess() {
runTest {
val result = appFunctions.send(testContext, "1", "Hello")
val result = appFunctions.sendMessage(testContext, "1", "Hello")
Assert.assertEquals(
AppFunctions.Result(
"Message ID",
"Message sent to: Alice Smith.",
),
"Message sent to: Alice Smith.",
result,
)
}
}

@Test
fun send_withImageUris_success() {
fun sendMessage_withImageUris_success() {
runTest {
val result =
appFunctions.send(
appFunctions.sendMessage(
testContext,
"1",
"Hello",
listOf(Uri.parse("content://media/1")),
)
Assert.assertEquals(
AppFunctions.Result(
"Message ID",
"Message sent to: Alice Smith.",
),
"Message sent to: Alice Smith.",
result,
)
}
}

@Test
fun send_toGroup_success() {
fun sendMessage_toGroup_success() {
runTest {
val result = appFunctions.send(testContext, "g1", "Hello")
val result = appFunctions.sendMessage(testContext, "g1", "Hello")
Assert.assertEquals(
AppFunctions.Result(
"Message ID",
"Message sent to: Work Friends.",
),
"Message sent to: Work Friends.",
result,
)
}
}

@Test(expected = AppFunctionInvalidArgumentException::class)
fun send_emptyContent_fails() {
fun sendMessage_emptyContent_fails() {
runTest {
appFunctions.send(testContext, "1", "")
appFunctions.sendMessage(testContext, "1", "")
}
}

@Test(expected = AppFunctionElementNotFoundException::class)
fun send_invalidRecipient_fails() {
fun sendMessage_invalidRecipient_fails() {
runTest {
appFunctions.send(testContext, "nonexistent_id", "Hello")
appFunctions.sendMessage(testContext, "nonexistent_id", "Hello")
}
}

@Test(expected = AppFunctionAppUnknownException::class)
fun send_repositoryError_returnsError() {
fun sendMessage_repositoryError_returnsError() {
runTest {
messageRepository.shouldFail = true
appFunctions.send(testContext, "1", "Hello")
appFunctions.sendMessage(testContext, "1", "Hello")
}
}

Expand All @@ -216,4 +232,62 @@ class AppFunctionsTest {
}
}

@Test
fun searchMessages_allChats_returnsMatches() {
runTest {
messageRepository.saveMessage("1", DisplayMessage("Hello Alice", 1000, isInbound = true))
messageRepository.saveMessage("2", DisplayMessage("Hello Bob", 2000, isInbound = false))
messageRepository.saveMessage("1", DisplayMessage("How are you?", 3000, isInbound = false))

val results = appFunctions.searchMessages(testContext, "Hello")

Assert.assertEquals(2, results.size)

val aliceResult = results.first { it.endpointValue == "1" }
Assert.assertEquals(1, aliceResult.messages.size)
Assert.assertEquals("Hello Alice", aliceResult.messages[0].messageBody)
Assert.assertEquals(1000L, aliceResult.messages[0].timestamp)
Assert.assertEquals("Alice Smith", aliceResult.messages[0].senderDisplayName)

val bobResult = results.first { it.endpointValue == "2" }
Assert.assertEquals(1, bobResult.messages.size)
Assert.assertEquals("Hello Bob", bobResult.messages[0].messageBody)
Assert.assertEquals(2000L, bobResult.messages[0].timestamp)
Assert.assertEquals("Me", bobResult.messages[0].senderDisplayName)
}
}

@Test
fun searchMessages_specificChat_returnsMatches() {
runTest {
messageRepository.saveMessage("1", DisplayMessage("Hello Alice", 1000, isInbound = true))
messageRepository.saveMessage("2", DisplayMessage("Hello Bob", 2000, isInbound = false))

val results = appFunctions.searchMessages(testContext, "Hello", endpointValue = "1")

Assert.assertEquals(1, results.size)
Assert.assertEquals("1", results[0].endpointValue)
Assert.assertEquals("Hello Alice", results[0].messages[0].messageBody)
}
}

@Test
fun searchMessages_noMatches_returnsEmpty() {
runTest {
messageRepository.saveMessage("1", DisplayMessage("Hello Alice", 1000, isInbound = true))

val results = appFunctions.searchMessages(testContext, "Goodbye")

Assert.assertTrue(results.isEmpty())
}
}

@Test(expected = AppFunctionInvalidArgumentException::class)
fun searchMessages_emptyQuery_fails() {
runTest {
appFunctions.searchMessages(testContext, "")
}
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,32 @@ class RecipientsRepository
fun searchRecipients(
query: String?,
maxCount: Int,
): List<Recipient> {
if (query.isNullOrBlank()) {
// TODO:Return most recently contacted.
return recipients.take(maxCount)
): List<ContactSearchResult> {
val matched = if (query.isNullOrBlank()) {
recipients
} else {
recipients.filter {
it.name.contains(query, ignoreCase = true) ||
it.email.contains(
query,
ignoreCase = true,
)
}
}

return recipients.filter {
it.name.contains(query, ignoreCase = true) ||
it.email.contains(
query,
ignoreCase = true,
)
val mapped = matched.map {
ContactSearchResult(
contactDisplayName = it.name,
contactType = "INDIVIDUAL",
endpointValue = it.id,
endpointDisplayName = it.email,
)
}

return if (query.isNullOrBlank()) {
mapped.take(maxCount)
} else {
mapped
}
Comment thread
corinac123 marked this conversation as resolved.
}

Expand Down Expand Up @@ -132,24 +146,20 @@ class RecipientsRepository
* @param maxCount Maximum number of results to return per entity type.
* @return A unified list of [ContactSearchResult] containing both individuals and groups.
*/


fun searchAny(
query: String?,
maxCount: Int,
): List<ContactSearchResult> {
val individuals =
searchRecipients(query, maxCount).map {
ContactSearchResult(
endpointValue = it.id,
endpointType = "INDIVIDUAL",
displayName = it.name,
)
}
val individuals = searchRecipients(query, maxCount)
val groups =
searchGroups(query, maxCount).map {
ContactSearchResult(
contactDisplayName = it.name,
contactType = "GROUP",
endpointValue = it.id,
endpointType = "GROUP",
displayName = it.name,
endpointDisplayName = it.name,
)
}
return mutableListOf<ContactSearchResult>().apply {
Expand Down
1 change: 1 addition & 0 deletions ChatApp/shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ dependencies {
implementation(libs.androidx.appfunctions.service)
ksp(libs.androidx.appfunctions.compiler)


}
Loading