0d50726490
FcmService + NotificationPayload + 4 NotificationChannels (task_reminder, task_overdue, residence_invite, subscription) parity with iOS NotificationCategories.swift. Deep-link routing from payload. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
128 lines
3.6 KiB
Kotlin
128 lines
3.6 KiB
Kotlin
package com.tt.honeyDue.notifications
|
|
|
|
import org.junit.Assert.assertEquals
|
|
import org.junit.Assert.assertNotNull
|
|
import org.junit.Assert.assertNull
|
|
import org.junit.Test
|
|
|
|
/**
|
|
* Unit tests for [NotificationPayload.parse] covering the FCM data-map shapes
|
|
* produced by the backend for the four iOS parity notification types:
|
|
* task_reminder, task_overdue, residence_invite, subscription.
|
|
*/
|
|
class NotificationPayloadTest {
|
|
|
|
@Test
|
|
fun parse_taskReminder_payload() {
|
|
val data = mapOf(
|
|
"type" to "task_reminder",
|
|
"task_id" to "123",
|
|
"title" to "Mow the lawn",
|
|
"body" to "Don't forget to mow today",
|
|
"deep_link" to "honeydue://task/123"
|
|
)
|
|
|
|
val payload = NotificationPayload.parse(data)
|
|
|
|
assertNotNull(payload)
|
|
assertEquals("task_reminder", payload!!.type)
|
|
assertEquals(123L, payload.taskId)
|
|
assertNull(payload.residenceId)
|
|
assertEquals("Mow the lawn", payload.title)
|
|
assertEquals("Don't forget to mow today", payload.body)
|
|
assertEquals("honeydue://task/123", payload.deepLink)
|
|
}
|
|
|
|
@Test
|
|
fun parse_taskOverdue_payload() {
|
|
val data = mapOf(
|
|
"type" to "task_overdue",
|
|
"task_id" to "456",
|
|
"title" to "Overdue: Clean gutters",
|
|
"body" to "This task is past due"
|
|
)
|
|
|
|
val payload = NotificationPayload.parse(data)
|
|
|
|
assertNotNull(payload)
|
|
assertEquals("task_overdue", payload!!.type)
|
|
assertEquals(456L, payload.taskId)
|
|
assertEquals("Overdue: Clean gutters", payload.title)
|
|
assertNull(payload.deepLink)
|
|
}
|
|
|
|
@Test
|
|
fun parse_residenceInvite_payload() {
|
|
val data = mapOf(
|
|
"type" to "residence_invite",
|
|
"residence_id" to "42",
|
|
"title" to "You've been invited",
|
|
"body" to "Join the home",
|
|
"deep_link" to "honeydue://residence/42"
|
|
)
|
|
|
|
val payload = NotificationPayload.parse(data)
|
|
|
|
assertNotNull(payload)
|
|
assertEquals("residence_invite", payload!!.type)
|
|
assertNull(payload.taskId)
|
|
assertEquals(42L, payload.residenceId)
|
|
assertEquals("honeydue://residence/42", payload.deepLink)
|
|
}
|
|
|
|
@Test
|
|
fun parse_subscription_payload() {
|
|
val data = mapOf(
|
|
"type" to "subscription",
|
|
"title" to "Subscription updated",
|
|
"body" to "Your plan changed"
|
|
)
|
|
|
|
val payload = NotificationPayload.parse(data)
|
|
|
|
assertNotNull(payload)
|
|
assertEquals("subscription", payload!!.type)
|
|
assertNull(payload.taskId)
|
|
assertNull(payload.residenceId)
|
|
assertEquals("Subscription updated", payload.title)
|
|
}
|
|
|
|
@Test
|
|
fun parse_malformed_returns_null_whenTypeMissing() {
|
|
val data = mapOf(
|
|
"task_id" to "1",
|
|
"title" to "x",
|
|
"body" to "y"
|
|
)
|
|
|
|
assertNull(NotificationPayload.parse(data))
|
|
}
|
|
|
|
@Test
|
|
fun parse_malformed_returns_null_whenTitleAndBodyMissing() {
|
|
val data = mapOf("type" to "task_reminder")
|
|
|
|
assertNull(NotificationPayload.parse(data))
|
|
}
|
|
|
|
@Test
|
|
fun parse_emptyMap_returns_null() {
|
|
assertNull(NotificationPayload.parse(emptyMap()))
|
|
}
|
|
|
|
@Test
|
|
fun parse_ignoresInvalidNumericIds() {
|
|
val data = mapOf(
|
|
"type" to "task_reminder",
|
|
"task_id" to "not-a-number",
|
|
"title" to "x",
|
|
"body" to "y"
|
|
)
|
|
|
|
val payload = NotificationPayload.parse(data)
|
|
|
|
assertNotNull(payload)
|
|
assertNull(payload!!.taskId)
|
|
}
|
|
}
|