/** * DailyNotificationTTLEnforcerTest.java * * Unit tests for TTL-at-fire enforcement functionality * Tests freshness validation, TTL violation logging, and skip logic * * @author Matthew Raymer * @version 1.0.0 */ package com.timesafari.dailynotification; import android.content.Context; import android.test.AndroidTestCase; import android.test.mock.MockContext; import java.util.concurrent.TimeUnit; /** * Unit tests for DailyNotificationTTLEnforcer * * Tests the core TTL enforcement functionality including: * - Freshness validation before arming * - TTL violation detection and logging * - Skip logic for stale content * - Configuration retrieval from storage */ public class DailyNotificationTTLEnforcerTest extends AndroidTestCase { private DailyNotificationTTLEnforcer ttlEnforcer; private Context mockContext; private DailyNotificationDatabase database; @Override protected void setUp() throws Exception { super.setUp(); // Create mock context mockContext = new MockContext() { @Override public android.content.SharedPreferences getSharedPreferences(String name, int mode) { return getContext().getSharedPreferences(name, mode); } }; // Create database instance database = new DailyNotificationDatabase(mockContext); // Create TTL enforcer with SQLite storage ttlEnforcer = new DailyNotificationTTLEnforcer(mockContext, database, true); } @Override protected void tearDown() throws Exception { if (database != null) { database.close(); } super.tearDown(); } /** * Test freshness validation with fresh content */ public void testFreshContentValidation() { long currentTime = System.currentTimeMillis(); long scheduledTime = currentTime + TimeUnit.MINUTES.toMillis(30); // 30 minutes from now long fetchedAt = currentTime - TimeUnit.MINUTES.toMillis(5); // 5 minutes ago boolean isFresh = ttlEnforcer.isContentFresh("test_slot_1", scheduledTime, fetchedAt); assertTrue("Content should be fresh (5 min old, scheduled 30 min from now)", isFresh); } /** * Test freshness validation with stale content */ public void testStaleContentValidation() { long currentTime = System.currentTimeMillis(); long scheduledTime = currentTime + TimeUnit.MINUTES.toMillis(30); // 30 minutes from now long fetchedAt = currentTime - TimeUnit.HOURS.toMillis(2); // 2 hours ago boolean isFresh = ttlEnforcer.isContentFresh("test_slot_2", scheduledTime, fetchedAt); assertFalse("Content should be stale (2 hours old, exceeds 1 hour TTL)", isFresh); } /** * Test TTL violation detection */ public void testTTLViolationDetection() { long currentTime = System.currentTimeMillis(); long scheduledTime = currentTime + TimeUnit.MINUTES.toMillis(30); long fetchedAt = currentTime - TimeUnit.HOURS.toMillis(2); // 2 hours ago // This should trigger a TTL violation boolean isFresh = ttlEnforcer.isContentFresh("test_slot_3", scheduledTime, fetchedAt); assertFalse("Should detect TTL violation", isFresh); // Check that violation was logged (we can't easily test the actual logging, // but we can verify the method returns false as expected) } /** * Test validateBeforeArming with fresh content */ public void testValidateBeforeArmingFresh() { long currentTime = System.currentTimeMillis(); long scheduledTime = currentTime + TimeUnit.MINUTES.toMillis(30); long fetchedAt = currentTime - TimeUnit.MINUTES.toMillis(5); NotificationContent content = new NotificationContent(); content.setId("test_slot_4"); content.setScheduledTime(scheduledTime); content.setFetchedAt(fetchedAt); content.setTitle("Test Notification"); content.setBody("Test body"); boolean shouldArm = ttlEnforcer.validateBeforeArming(content); assertTrue("Should arm fresh content", shouldArm); } /** * Test validateBeforeArming with stale content */ public void testValidateBeforeArmingStale() { long currentTime = System.currentTimeMillis(); long scheduledTime = currentTime + TimeUnit.MINUTES.toMillis(30); long fetchedAt = currentTime - TimeUnit.HOURS.toMillis(2); NotificationContent content = new NotificationContent(); content.setId("test_slot_5"); content.setScheduledTime(scheduledTime); content.setFetchedAt(fetchedAt); content.setTitle("Test Notification"); content.setBody("Test body"); boolean shouldArm = ttlEnforcer.validateBeforeArming(content); assertFalse("Should not arm stale content", shouldArm); } /** * Test edge case: content fetched exactly at TTL limit */ public void testTTLBoundaryCase() { long currentTime = System.currentTimeMillis(); long scheduledTime = currentTime + TimeUnit.MINUTES.toMillis(30); long fetchedAt = currentTime - TimeUnit.HOURS.toMillis(1); // Exactly 1 hour ago (TTL limit) boolean isFresh = ttlEnforcer.isContentFresh("test_slot_6", scheduledTime, fetchedAt); assertTrue("Content at TTL boundary should be considered fresh", isFresh); } /** * Test edge case: content fetched just over TTL limit */ public void testTTLBoundaryCaseOver() { long currentTime = System.currentTimeMillis(); long scheduledTime = currentTime + TimeUnit.MINUTES.toMillis(30); long fetchedAt = currentTime - TimeUnit.HOURS.toMillis(1) - TimeUnit.SECONDS.toMillis(1); // 1 hour + 1 second ago boolean isFresh = ttlEnforcer.isContentFresh("test_slot_7", scheduledTime, fetchedAt); assertFalse("Content just over TTL limit should be considered stale", isFresh); } /** * Test TTL violation statistics */ public void testTTLViolationStats() { // Generate some TTL violations long currentTime = System.currentTimeMillis(); long scheduledTime = currentTime + TimeUnit.MINUTES.toMillis(30); long fetchedAt = currentTime - TimeUnit.HOURS.toMillis(2); // Trigger TTL violations ttlEnforcer.isContentFresh("test_slot_8", scheduledTime, fetchedAt); ttlEnforcer.isContentFresh("test_slot_9", scheduledTime, fetchedAt); String stats = ttlEnforcer.getTTLViolationStats(); assertNotNull("TTL violation stats should not be null", stats); assertTrue("Stats should contain violation count", stats.contains("violations")); } /** * Test error handling with invalid parameters */ public void testErrorHandling() { // Test with null slot ID boolean result = ttlEnforcer.isContentFresh(null, System.currentTimeMillis(), System.currentTimeMillis()); assertFalse("Should handle null slot ID gracefully", result); // Test with invalid timestamps result = ttlEnforcer.isContentFresh("test_slot_10", 0, 0); assertTrue("Should handle invalid timestamps gracefully", result); } /** * Test TTL configuration retrieval */ public void testTTLConfiguration() { // Test that TTL enforcer can retrieve configuration // This is indirectly tested through the freshness checks long currentTime = System.currentTimeMillis(); long scheduledTime = currentTime + TimeUnit.MINUTES.toMillis(30); long fetchedAt = currentTime - TimeUnit.MINUTES.toMillis(30); // 30 minutes ago boolean isFresh = ttlEnforcer.isContentFresh("test_slot_11", scheduledTime, fetchedAt); // Should be fresh (30 min < 1 hour TTL) assertTrue("Should retrieve TTL configuration correctly", isFresh); } }