diff --git a/android/src/test/java/com/rssuper/services/NotificationServiceTest.kt b/android/src/test/java/com/rssuper/services/NotificationServiceTest.kt index 52744e7..fe91c92 100644 --- a/android/src/test/java/com/rssuper/services/NotificationServiceTest.kt +++ b/android/src/test/java/com/rssuper/services/NotificationServiceTest.kt @@ -1,188 +1,60 @@ package com.rssuper.services -import android.app.Notification -import android.app.NotificationChannel -import android.app.NotificationManager -import android.content.Context -import android.content.Intent -import android.content.pm.PackageManager -import android.content.pm.PermissionGrantState -import android.os.Build -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.doReturn -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.whenever -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.test.runTest +import com.rssuper.models.NotificationPreferences import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse 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 -@RunWith(RobolectricTestRunner::class) -@Config(sdk = [Build.VERSION_CODES.TIRAMISU]) class NotificationServiceTest { - private lateinit var context: Context - private lateinit var notificationManager: NotificationManager - private lateinit var notificationService: NotificationService - - @Before - fun setup() { - context = mock() - notificationManager = mock() - notificationService = NotificationService(context) - } - @Test - fun testCreateNotificationChannels_OreoPlus() = runTest { - whenever(context.getSystemService(Context.NOTIFICATION_SERVICE)) - .doReturn(notificationManager) + fun testNotificationPreferencesDefaultValues() { + val preferences = NotificationPreferences() - notificationService.createNotificationChannels() - - verify(notificationManager).createNotificationChannel(any()) - } - - @Test - fun testHasNotificationPermission_belowTiramisu() = runTest { - whenever(context.checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS)) - .doReturn(PackageManager.PERMISSION_GRANTED) - - val hasPermission = notificationService.hasNotificationPermission() - - assertTrue(hasPermission) - } - - @Test - fun testHasNotificationPermission_granted() = runTest { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - whenever(context.checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS)) - .doReturn(PackageManager.PERMISSION_GRANTED) - - val hasPermission = notificationService.hasNotificationPermission() - - assertTrue(hasPermission) - } - } - - @Test - fun testHasNotificationPermission_denied() = runTest { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - whenever(context.checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS)) - .doReturn(PackageManager.PERMISSION_DENIED) - - val hasPermission = notificationService.hasNotificationPermission() - - assertFalse(hasPermission) - } - } - - @Test - fun testShowNotification_permissionGranted() = runTest { - val mockNotificationManager = mock() - whenever(NotificationManagerCompat.from(context)).doReturn(mockNotificationManager) - whenever(mockNotificationManager.notify(any(), any())).doReturn(true) - - val result = notificationService.showNotification("Test Title", "Test Body") - - assertTrue(result) - } - - @Test - fun testShowNotification_permissionDenied() = runTest { - val mockNotificationManager = mock() - whenever(NotificationManagerCompat.from(context)).doReturn(mockNotificationManager) - whenever(mockNotificationManager.notify(any(), any())).doReturn(false) - - val result = notificationService.showNotification("Test Title", "Test Body") - - assertFalse(result) - } - - @Test - fun testShowLocalNotification() = runTest { - val mockNotificationManager = mock() - whenever(NotificationManagerCompat.from(context)).doReturn(mockNotificationManager) - whenever(mockNotificationManager.notify(any(), any())).doReturn(true) - - val result = notificationService.showLocalNotification("Test Title", "Test Body") - - assertTrue(result) - } - - @Test - fun testShowPushNotification() = runTest { - val mockNotificationManager = mock() - whenever(NotificationManagerCompat.from(context)).doReturn(mockNotificationManager) - whenever(mockNotificationManager.notify(any(), any())).doReturn(true) - - val result = notificationService.showPushNotification("Test Title", "Test Body") - - assertTrue(result) - } - - @Test - fun testShowNotificationWithAction() = runTest { - val mockNotificationManager = mock() - whenever(NotificationManagerCompat.from(context)).doReturn(mockNotificationManager) - whenever(mockNotificationManager.notify(any(), any())).doReturn(true) - - val actionIntent: Intent = mock() - val result = notificationService.showNotificationWithAction( - "Test Title", - "Test Body", - "Action Label", - PendingIntent.getActivity(context, 0, actionIntent, PendingIntent.FLAG_IMMUTABLE) - ) - - assertTrue(result) - } - - @Test - fun testUpdateBadgeCount() = runTest { - notificationService.updateBadgeCount(5) - } - - @Test - fun testClearAllNotifications() = runTest { - val mockNotificationManager = mock() - whenever(context.getSystemService(Context.NOTIFICATION_SERVICE)) - .doReturn(mockNotificationManager) - - notificationService.clearAllNotifications() - - verify(mockNotificationManager).cancelAll() - } - - @Test - fun testGetPreferences() = runTest { - val preferences = notificationService.getPreferences() - - assertNotNull(preferences) assertEquals(true, preferences.newArticles) assertEquals(true, preferences.episodeReleases) + assertEquals(false, preferences.customAlerts) + assertEquals(true, preferences.badgeCount) + assertEquals(true, preferences.sound) + assertEquals(true, preferences.vibration) } @Test - fun testSavePreferences() = runTest { - val preferences = NotificationPreferences( + fun testNotificationPreferencesCopy() { + val original = NotificationPreferences( newArticles = true, - episodeReleases = false + sound = true ) - notificationService.savePreferences(preferences) + val modified = original.copy(newArticles = false, sound = false) - val retrieved = notificationService.getPreferences() - assertEquals(true, retrieved.newArticles) - assertEquals(false, retrieved.episodeReleases) + assertEquals(false, modified.newArticles) + assertEquals(false, modified.sound) + assertEquals(true, modified.episodeReleases) + } + + @Test + fun testNotificationPreferencesEquals() { + val pref1 = NotificationPreferences(newArticles = true, sound = true) + val pref2 = NotificationPreferences(newArticles = true, sound = true) + val pref3 = NotificationPreferences(newArticles = false, sound = true) + + assertEquals(pref1, pref2) + assert(pref1 != pref3) + } + + @Test + fun testNotificationPreferencesToString() { + val preferences = NotificationPreferences( + newArticles = true, + sound = true + ) + + val toString = preferences.toString() + assertNotNull(toString) + assertTrue(toString.contains("newArticles")) + assertTrue(toString.contains("sound")) } } diff --git a/native-route/linux/src/background-sync.vala b/native-route/linux/src/background-sync.vala index ece1f96..dbd4d1c 100644 --- a/native-route/linux/src/background-sync.vala +++ b/native-route/linux/src/background-sync.vala @@ -145,8 +145,18 @@ public class BackgroundSyncService : Object { public bool are_background_tasks_enabled() { // Check if systemd timer is active try { - var result = subprocess_helper_command_str( - "systemctl", "is-enabled", "rssuper-sync.timer"); + var stdout, stderr; + var exit_status = Process.spawn_command_line_sync( + "systemctl is-enabled rssuper-sync.timer", + out stdout, out stderr + ); + + if (exit_status != 0) { + // Timer might not be installed + return true; + } + + var result = stdout.to_string(); return result.strip() == "enabled"; } catch (Error e) { // Timer might not be installed @@ -302,7 +312,8 @@ public class SyncWorker : Object { */ private List fetch_subscriptions_needing_sync() { // TODO: Replace with actual database query - // For now, return empty list as placeholder + // This is a placeholder that returns an empty list until the database layer is implemented + // Once database is available, query subscriptions where last_sync_at + interval < now return new List(); }