P4 Stream N: FCM service + NotificationChannels matching iOS categories

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>
This commit is contained in:
Trey T
2026-04-18 12:45:37 -05:00
parent 6d7b5ee990
commit 0d50726490
8 changed files with 699 additions and 2 deletions
@@ -0,0 +1,107 @@
package com.tt.honeyDue.notifications
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import androidx.test.core.app.ApplicationProvider
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
/**
* Tests for [NotificationChannels] — verify that the four iOS-parity channels
* are created with the correct importance levels and that the helper is
* idempotent across repeated invocations.
*/
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.TIRAMISU])
class NotificationChannelsTest {
private lateinit var context: Context
private lateinit var manager: NotificationManager
@Before
fun setUp() {
context = ApplicationProvider.getApplicationContext()
manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Clean slate — remove any channels left over from previous tests.
manager.notificationChannels.forEach { manager.deleteNotificationChannel(it.id) }
}
@Test
fun ensureChannels_creates_four_channels() {
NotificationChannels.ensureChannels(context)
val ids = manager.notificationChannels.map { it.id }.toSet()
assertTrue("task_reminder missing", NotificationChannels.TASK_REMINDER in ids)
assertTrue("task_overdue missing", NotificationChannels.TASK_OVERDUE in ids)
assertTrue("residence_invite missing", NotificationChannels.RESIDENCE_INVITE in ids)
assertTrue("subscription missing", NotificationChannels.SUBSCRIPTION in ids)
}
@Test
fun ensureChannels_idempotent() {
NotificationChannels.ensureChannels(context)
val firstCount = manager.notificationChannels.size
NotificationChannels.ensureChannels(context)
val secondCount = manager.notificationChannels.size
assertEquals(firstCount, secondCount)
assertEquals(4, secondCount)
}
@Test
fun taskReminder_has_default_importance() {
NotificationChannels.ensureChannels(context)
val channel = manager.getNotificationChannel(NotificationChannels.TASK_REMINDER)
assertNotNull(channel)
assertEquals(NotificationManager.IMPORTANCE_DEFAULT, channel!!.importance)
}
@Test
fun taskOverdue_has_high_importance() {
NotificationChannels.ensureChannels(context)
val channel = manager.getNotificationChannel(NotificationChannels.TASK_OVERDUE)
assertNotNull(channel)
assertEquals(NotificationManager.IMPORTANCE_HIGH, channel!!.importance)
}
@Test
fun residenceInvite_has_default_importance() {
NotificationChannels.ensureChannels(context)
val channel = manager.getNotificationChannel(NotificationChannels.RESIDENCE_INVITE)
assertNotNull(channel)
assertEquals(NotificationManager.IMPORTANCE_DEFAULT, channel!!.importance)
}
@Test
fun subscription_has_low_importance() {
NotificationChannels.ensureChannels(context)
val channel = manager.getNotificationChannel(NotificationChannels.SUBSCRIPTION)
assertNotNull(channel)
assertEquals(NotificationManager.IMPORTANCE_LOW, channel!!.importance)
}
@Test
fun channelIdForType_mapsAllKnownTypes() {
assertEquals(NotificationChannels.TASK_REMINDER, NotificationChannels.channelIdForType("task_reminder"))
assertEquals(NotificationChannels.TASK_OVERDUE, NotificationChannels.channelIdForType("task_overdue"))
assertEquals(NotificationChannels.RESIDENCE_INVITE, NotificationChannels.channelIdForType("residence_invite"))
assertEquals(NotificationChannels.SUBSCRIPTION, NotificationChannels.channelIdForType("subscription"))
}
@Test
fun channelIdForType_returnsTaskReminder_forUnknownType() {
// Unknown types fall back to task_reminder (safe default).
assertEquals(
NotificationChannels.TASK_REMINDER,
NotificationChannels.channelIdForType("mystery_type")
)
}
}