feat(android): add complete DailyNotification plugin implementation
- Add full DailyNotificationPlugin with @CapacitorPlugin annotation - Implement echo method for testing plugin connectivity - Add comprehensive notification functionality with offline-first approach - Include performance optimization and error handling classes - Add WorkManager integration for background content fetching - Plugin now ready for testing with Capacitor 6 registration
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* DailyNotificationDatabaseTest.java
|
||||
*
|
||||
* Unit tests for SQLite database functionality
|
||||
* Tests schema creation, WAL mode, and basic operations
|
||||
*
|
||||
* @author Matthew Raymer
|
||||
* @version 1.0.0
|
||||
*/
|
||||
|
||||
package com.timesafari.dailynotification;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.mock.MockContext;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Unit tests for DailyNotificationDatabase
|
||||
*
|
||||
* Tests the core SQLite functionality including:
|
||||
* - Database creation and schema
|
||||
* - WAL mode configuration
|
||||
* - Table and index creation
|
||||
* - Schema version management
|
||||
*/
|
||||
public class DailyNotificationDatabaseTest extends AndroidTestCase {
|
||||
|
||||
private DailyNotificationDatabase database;
|
||||
private Context mockContext;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
// Create mock context
|
||||
mockContext = new MockContext() {
|
||||
@Override
|
||||
public File getDatabasePath(String name) {
|
||||
return new File(getContext().getCacheDir(), name);
|
||||
}
|
||||
};
|
||||
|
||||
// Create database instance
|
||||
database = new DailyNotificationDatabase(mockContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
if (database != null) {
|
||||
database.close();
|
||||
}
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test database creation and schema
|
||||
*/
|
||||
public void testDatabaseCreation() {
|
||||
assertNotNull("Database should not be null", database);
|
||||
|
||||
SQLiteDatabase db = database.getReadableDatabase();
|
||||
assertNotNull("Readable database should not be null", db);
|
||||
assertTrue("Database should be open", db.isOpen());
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test WAL mode configuration
|
||||
*/
|
||||
public void testWALModeConfiguration() {
|
||||
SQLiteDatabase db = database.getWritableDatabase();
|
||||
|
||||
// Check journal mode
|
||||
android.database.Cursor cursor = db.rawQuery("PRAGMA journal_mode", null);
|
||||
assertTrue("Should have journal mode result", cursor.moveToFirst());
|
||||
String journalMode = cursor.getString(0);
|
||||
assertEquals("Journal mode should be WAL", "wal", journalMode.toLowerCase());
|
||||
cursor.close();
|
||||
|
||||
// Check synchronous mode
|
||||
cursor = db.rawQuery("PRAGMA synchronous", null);
|
||||
assertTrue("Should have synchronous result", cursor.moveToFirst());
|
||||
int synchronous = cursor.getInt(0);
|
||||
assertEquals("Synchronous mode should be NORMAL", 1, synchronous);
|
||||
cursor.close();
|
||||
|
||||
// Check foreign keys
|
||||
cursor = db.rawQuery("PRAGMA foreign_keys", null);
|
||||
assertTrue("Should have foreign_keys result", cursor.moveToFirst());
|
||||
int foreignKeys = cursor.getInt(0);
|
||||
assertEquals("Foreign keys should be enabled", 1, foreignKeys);
|
||||
cursor.close();
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test table creation
|
||||
*/
|
||||
public void testTableCreation() {
|
||||
SQLiteDatabase db = database.getWritableDatabase();
|
||||
|
||||
// Check if tables exist
|
||||
assertTrue("notif_contents table should exist",
|
||||
tableExists(db, DailyNotificationDatabase.TABLE_NOTIF_CONTENTS));
|
||||
assertTrue("notif_deliveries table should exist",
|
||||
tableExists(db, DailyNotificationDatabase.TABLE_NOTIF_DELIVERIES));
|
||||
assertTrue("notif_config table should exist",
|
||||
tableExists(db, DailyNotificationDatabase.TABLE_NOTIF_CONFIG));
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test index creation
|
||||
*/
|
||||
public void testIndexCreation() {
|
||||
SQLiteDatabase db = database.getWritableDatabase();
|
||||
|
||||
// Check if indexes exist
|
||||
assertTrue("notif_idx_contents_slot_time index should exist",
|
||||
indexExists(db, "notif_idx_contents_slot_time"));
|
||||
assertTrue("notif_idx_deliveries_slot index should exist",
|
||||
indexExists(db, "notif_idx_deliveries_slot"));
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test schema version management
|
||||
*/
|
||||
public void testSchemaVersion() {
|
||||
SQLiteDatabase db = database.getWritableDatabase();
|
||||
|
||||
// Check user_version
|
||||
android.database.Cursor cursor = db.rawQuery("PRAGMA user_version", null);
|
||||
assertTrue("Should have user_version result", cursor.moveToFirst());
|
||||
int userVersion = cursor.getInt(0);
|
||||
assertEquals("User version should match database version",
|
||||
DailyNotificationDatabase.DATABASE_VERSION, userVersion);
|
||||
cursor.close();
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic insert operations
|
||||
*/
|
||||
public void testBasicInsertOperations() {
|
||||
SQLiteDatabase db = database.getWritableDatabase();
|
||||
|
||||
// Test inserting into notif_contents
|
||||
android.content.ContentValues values = new android.content.ContentValues();
|
||||
values.put(DailyNotificationDatabase.COL_CONTENTS_SLOT_ID, "test_slot_1");
|
||||
values.put(DailyNotificationDatabase.COL_CONTENTS_PAYLOAD_JSON, "{\"title\":\"Test\"}");
|
||||
values.put(DailyNotificationDatabase.COL_CONTENTS_FETCHED_AT, System.currentTimeMillis());
|
||||
|
||||
long rowId = db.insert(DailyNotificationDatabase.TABLE_NOTIF_CONTENTS, null, values);
|
||||
assertTrue("Insert should succeed", rowId > 0);
|
||||
|
||||
// Test inserting into notif_config
|
||||
values.clear();
|
||||
values.put(DailyNotificationDatabase.COL_CONFIG_K, "test_key");
|
||||
values.put(DailyNotificationDatabase.COL_CONFIG_V, "test_value");
|
||||
|
||||
rowId = db.insert(DailyNotificationDatabase.TABLE_NOTIF_CONFIG, null, values);
|
||||
assertTrue("Config insert should succeed", rowId > 0);
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test database file operations
|
||||
*/
|
||||
public void testDatabaseFileOperations() {
|
||||
String dbPath = database.getDatabasePath();
|
||||
assertNotNull("Database path should not be null", dbPath);
|
||||
assertTrue("Database path should not be empty", !dbPath.isEmpty());
|
||||
|
||||
// Database should exist after creation
|
||||
assertTrue("Database file should exist", database.databaseExists());
|
||||
|
||||
// Database size should be greater than 0
|
||||
long size = database.getDatabaseSize();
|
||||
assertTrue("Database size should be greater than 0", size > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to check if table exists
|
||||
*/
|
||||
private boolean tableExists(SQLiteDatabase db, String tableName) {
|
||||
android.database.Cursor cursor = db.rawQuery(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name=?",
|
||||
new String[]{tableName});
|
||||
boolean exists = cursor.moveToFirst();
|
||||
cursor.close();
|
||||
return exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to check if index exists
|
||||
*/
|
||||
private boolean indexExists(SQLiteDatabase db, String indexName) {
|
||||
android.database.Cursor cursor = db.rawQuery(
|
||||
"SELECT name FROM sqlite_master WHERE type='index' AND name=?",
|
||||
new String[]{indexName});
|
||||
boolean exists = cursor.moveToFirst();
|
||||
cursor.close();
|
||||
return exists;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* DailyNotificationRollingWindowTest.java
|
||||
*
|
||||
* Unit tests for rolling window safety functionality
|
||||
* Tests window maintenance, capacity management, and platform-specific limits
|
||||
*
|
||||
* @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 DailyNotificationRollingWindow
|
||||
*
|
||||
* Tests the rolling window safety functionality including:
|
||||
* - Window maintenance and state updates
|
||||
* - Capacity limit enforcement
|
||||
* - Platform-specific behavior (iOS vs Android)
|
||||
* - Statistics and maintenance timing
|
||||
*/
|
||||
public class DailyNotificationRollingWindowTest extends AndroidTestCase {
|
||||
|
||||
private DailyNotificationRollingWindow rollingWindow;
|
||||
private Context mockContext;
|
||||
private DailyNotificationScheduler mockScheduler;
|
||||
private DailyNotificationTTLEnforcer mockTTLEnforcer;
|
||||
private DailyNotificationStorage mockStorage;
|
||||
|
||||
@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 mock components
|
||||
mockScheduler = new MockDailyNotificationScheduler();
|
||||
mockTTLEnforcer = new MockDailyNotificationTTLEnforcer();
|
||||
mockStorage = new MockDailyNotificationStorage();
|
||||
|
||||
// Create rolling window for Android platform
|
||||
rollingWindow = new DailyNotificationRollingWindow(
|
||||
mockContext,
|
||||
mockScheduler,
|
||||
mockTTLEnforcer,
|
||||
mockStorage,
|
||||
false // Android platform
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test rolling window initialization
|
||||
*/
|
||||
public void testRollingWindowInitialization() {
|
||||
assertNotNull("Rolling window should be initialized", rollingWindow);
|
||||
|
||||
// Test Android platform limits
|
||||
String stats = rollingWindow.getRollingWindowStats();
|
||||
assertNotNull("Stats should not be null", stats);
|
||||
assertTrue("Stats should contain Android platform info", stats.contains("Android"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test rolling window maintenance
|
||||
*/
|
||||
public void testRollingWindowMaintenance() {
|
||||
// Test that maintenance can be forced
|
||||
rollingWindow.forceMaintenance();
|
||||
|
||||
// Test maintenance timing
|
||||
assertFalse("Maintenance should not be needed immediately after forcing",
|
||||
rollingWindow.isMaintenanceNeeded());
|
||||
|
||||
// Test time until next maintenance
|
||||
long timeUntilNext = rollingWindow.getTimeUntilNextMaintenance();
|
||||
assertTrue("Time until next maintenance should be positive", timeUntilNext > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test iOS platform behavior
|
||||
*/
|
||||
public void testIOSPlatformBehavior() {
|
||||
// Create rolling window for iOS platform
|
||||
DailyNotificationRollingWindow iosRollingWindow = new DailyNotificationRollingWindow(
|
||||
mockContext,
|
||||
mockScheduler,
|
||||
mockTTLEnforcer,
|
||||
mockStorage,
|
||||
true // iOS platform
|
||||
);
|
||||
|
||||
String stats = iosRollingWindow.getRollingWindowStats();
|
||||
assertNotNull("iOS stats should not be null", stats);
|
||||
assertTrue("Stats should contain iOS platform info", stats.contains("iOS"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test maintenance timing
|
||||
*/
|
||||
public void testMaintenanceTiming() {
|
||||
// Initially, maintenance should not be needed
|
||||
assertFalse("Maintenance should not be needed initially",
|
||||
rollingWindow.isMaintenanceNeeded());
|
||||
|
||||
// Force maintenance
|
||||
rollingWindow.forceMaintenance();
|
||||
|
||||
// Should not be needed immediately after
|
||||
assertFalse("Maintenance should not be needed after forcing",
|
||||
rollingWindow.isMaintenanceNeeded());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test statistics retrieval
|
||||
*/
|
||||
public void testStatisticsRetrieval() {
|
||||
String stats = rollingWindow.getRollingWindowStats();
|
||||
|
||||
assertNotNull("Statistics should not be null", stats);
|
||||
assertTrue("Statistics should contain pending count", stats.contains("pending"));
|
||||
assertTrue("Statistics should contain daily count", stats.contains("daily"));
|
||||
assertTrue("Statistics should contain platform info", stats.contains("platform"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test error handling
|
||||
*/
|
||||
public void testErrorHandling() {
|
||||
// Test with null components (should not crash)
|
||||
try {
|
||||
DailyNotificationRollingWindow errorWindow = new DailyNotificationRollingWindow(
|
||||
null, null, null, null, false
|
||||
);
|
||||
// Should not crash during construction
|
||||
} catch (Exception e) {
|
||||
// Expected to handle gracefully
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock DailyNotificationScheduler for testing
|
||||
*/
|
||||
private static class MockDailyNotificationScheduler extends DailyNotificationScheduler {
|
||||
public MockDailyNotificationScheduler() {
|
||||
super(null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean scheduleNotification(NotificationContent content) {
|
||||
return true; // Always succeed for testing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock DailyNotificationTTLEnforcer for testing
|
||||
*/
|
||||
private static class MockDailyNotificationTTLEnforcer extends DailyNotificationTTLEnforcer {
|
||||
public MockDailyNotificationTTLEnforcer() {
|
||||
super(null, null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateBeforeArming(NotificationContent content) {
|
||||
return true; // Always pass validation for testing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock DailyNotificationStorage for testing
|
||||
*/
|
||||
private static class MockDailyNotificationStorage extends DailyNotificationStorage {
|
||||
public MockDailyNotificationStorage() {
|
||||
super(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user