# P2.3 Implementation Checklist: Android Combined Edge Case Tests **Purpose:** Step-by-step implementation guide for P2.3, breaking down the design into actionable tasks with acceptance criteria and rollback guidance. **Owner:** Development Team **Last Updated:** 2025-12-22 **Status:** implementation-ready **Baseline:** `v1.0.11-p2-complete` **Design Reference:** `doc/progress/P2.3-DESIGN.md` --- ## Overview **Goal:** Achieve parity with iOS P2.2 by adding automated combined edge case tests for Android. **Scope:** - Enable Android test infrastructure (currently disabled) - Create test helpers (in-memory Room database, test data injection) - Implement 2-3 combined test scenarios mirroring iOS P2.2 **Estimated Time:** 12-20 hours (can be spread over multiple sessions) --- ## Pre-Implementation Checklist Before starting, verify: - [ ] Baseline tag exists: `v1.0.11-p2-complete` - [ ] CI is green: `./ci/run.sh` passes - [ ] P2.3 design reviewed and approved - [ ] Test framework choice decided (Robolectric vs pure unit tests) - [ ] Android test directory structure understood --- ## P2.3.1: Enable Android Test Infrastructure **Goal:** Re-enable Android tests with modern AndroidX testing framework. **Estimated Time:** 2-4 hours ### Step 1.1: Review Current Test Configuration **Action:** - Read `android/build.gradle` lines 48-63 (test configuration) - Understand why tests are disabled (comment: "tests reference deprecated/removed code") - Identify what needs to be updated **Acceptance:** - [ ] Current test configuration understood - [ ] Deprecated API usage identified (if any) --- ### Step 1.2: Add AndroidX Test Dependencies **Action:** - Add test dependencies to `android/build.gradle`: - `junit:junit:4.13.2` (or latest) - `androidx.test:core:1.5.0` (or latest) - `androidx.test.ext:junit:1.1.5` (or latest) - `org.robolectric:robolectric:4.11.1` (if using Robolectric) - `androidx.room:room-testing:2.6.1` (for in-memory Room testing) **File:** `android/build.gradle` **Acceptance:** - [ ] Test dependencies added to `dependencies {}` block - [ ] Versions compatible with existing AndroidX versions - [ ] No version conflicts --- ### Step 1.3: Update Test Configuration **Action:** - Remove or update `testOptions { unitTests.all { enabled = false } }` - Remove or update `sourceSets { test { java { srcDirs = [] } } }` - Enable unit tests: `testOptions { unitTests.includeAndroidResources = true }` (if using Robolectric) **File:** `android/build.gradle` **Acceptance:** - [ ] Test configuration updated - [ ] Tests are enabled (not disabled) - [ ] Test source directory is accessible --- ### Step 1.4: Create Test Directory Structure **Action:** - Create `android/src/test/java/com/timesafari/dailynotification/` if it doesn't exist - Create placeholder test file: `DailyNotificationRecoveryTests.kt` (or `.java`) - Add minimal test to verify infrastructure works **Example placeholder test:** ```kotlin package org.timesafari.dailynotification import org.junit.Test import org.junit.Assert.* class DailyNotificationRecoveryTests { @Test fun test_infrastructure_works() { assertTrue("Test infrastructure is working", true) } } ``` **Acceptance:** - [ ] Test directory structure created - [ ] Placeholder test file created - [ ] Test compiles without errors --- ### Step 1.5: Verify Test Infrastructure **Action:** - Run `cd android && ./gradlew test` (or `./gradlew :android:test` from root) - Verify test runs successfully - Check CI compatibility: ensure `./ci/run.sh` can run tests or skip gracefully **Acceptance:** - [ ] `./gradlew test` runs successfully - [ ] Placeholder test passes - [ ] CI compatibility verified (tests run in CI or documented as manual) **Rollback:** If tests fail to compile/run, revert `android/build.gradle` changes and investigate dependency conflicts. --- ## P2.3.2: Create Test Infrastructure Helpers **Goal:** Create test helpers similar to iOS `TestDBFactory.swift`. **Estimated Time:** 4-6 hours ### Step 2.1: Create In-Memory Room Database Factory **Action:** - Create `android/src/test/java/com/timesafari/dailynotification/TestDBFactory.kt` (or `.java`) - Implement factory method that creates in-memory Room database - Use `Room.inMemoryDatabaseBuilder()` for isolation **Example structure:** ```kotlin package org.timesafari.dailynotification import androidx.room.Room import androidx.room.RoomDatabase object TestDBFactory { fun createInMemoryDatabase(context: Context): DailyNotificationDatabase { return Room.inMemoryDatabaseBuilder( context, DailyNotificationDatabase::class.java ).allowMainThreadQueries() .build() } } ``` **Acceptance:** - [ ] `TestDBFactory` created - [ ] Factory method creates in-memory database - [ ] Database is isolated (each test gets fresh instance) --- ### Step 2.2: Create Test Data Injection Helpers **Action:** - Add helper methods to `TestDBFactory` (or separate `TestDataHelper`): - `injectInvalidSchedule()` - creates schedule with empty ID or null fields - `injectDuplicateSchedule()` - creates duplicate schedule entries - `injectPastSchedule()` - creates schedule with past `nextRunAt` - `injectDSTBoundarySchedule()` - creates schedule at DST boundary **Acceptance:** - [ ] Test data injection helpers created - [ ] Helpers support invalid data scenarios - [ ] Helpers support duplicate delivery scenarios - [ ] Helpers support DST boundary scenarios --- ### Step 2.3: Create Mock Context Helper (if needed) **Action:** - If using Robolectric, create mock context helper - If using pure unit tests, create minimal context mock - Ensure context provides necessary services (SharedPreferences, etc.) **Acceptance:** - [ ] Mock context helper created (if needed) - [ ] Context provides necessary services - [ ] Context is isolated per test --- ### Step 2.4: Verify Test Helpers Work **Action:** - Create simple test that uses `TestDBFactory` and data injection helpers - Verify database creation works - Verify data injection works - Verify database cleanup works (teardown) **Acceptance:** - [ ] Test helpers work in isolation - [ ] Database creation verified - [ ] Data injection verified - [ ] Cleanup verified **Rollback:** If helpers don't work, investigate Room in-memory database setup or mock context issues. --- ## P2.3.3: Implement Combined Test Scenarios **Goal:** Add 2-3 combined edge case tests mirroring iOS P2.2. **Estimated Time:** 6-10 hours ### Step 3.1: Implement Scenario A - DST Boundary + Duplicate Delivery + Cold Start **Action:** - Create test: `test_combined_dst_boundary_duplicate_delivery_cold_start()` - Test steps: 1. Create notification scheduled at DST boundary (use `ZonedDateTime` with DST transition) 2. Simulate duplicate delivery events (rapid succession - call delivery handler twice) 3. Trigger cold start recovery (call `ReactivationManager.performRecovery()`) 4. Verify: idempotency (running twice yields identical state) 5. Verify: deduplication (only one logical delivery recorded) 6. Verify: DST-consistent next time (next scheduled time accounts for DST) **File:** `android/src/test/java/com/timesafari/dailynotification/DailyNotificationRecoveryTests.kt` **Acceptance:** - [ ] Test created with `@Test` annotation - [ ] Test labeled with `@resilience @combined-scenarios` comment - [ ] Test verifies idempotency - [ ] Test verifies deduplication - [ ] Test verifies DST-consistent next time - [ ] Test passes deterministically **Reference:** iOS equivalent: `test_combined_dst_boundary_duplicate_delivery_cold_start()` in `ios/Tests/DailyNotificationRecoveryTests.swift` --- ### Step 3.2: Implement Scenario B - Rollover + Duplicate Delivery + Cold Start **Action:** - Create test: `test_combined_rollover_duplicate_delivery_cold_start()` - Test steps: 1. Create notification that was just delivered (past time) 2. Trigger rollover (first delivery - mark as delivered, schedule next) 3. Simulate duplicate delivery immediately (call delivery handler again) 4. Trigger cold start recovery 5. Verify: rollover idempotency (no double-apply of state transitions) 6. Verify: duplicate delivery doesn't double-apply state transitions 7. Verify: cold start reconciliation produces correct state **File:** `android/src/test/java/com/timesafari/dailynotification/DailyNotificationRecoveryTests.kt` **Acceptance:** - [ ] Test created with `@Test` annotation - [ ] Test labeled with `@resilience @combined-scenarios` comment - [ ] Test verifies rollover idempotency - [ ] Test verifies duplicate delivery handling - [ ] Test verifies cold start reconciliation - [ ] Test passes deterministically **Reference:** iOS equivalent: `test_combined_rollover_duplicate_delivery_cold_start()` in `ios/Tests/DailyNotificationRecoveryTests.swift` --- ### Step 3.3: Implement Scenario C - Schema Version + Cold Start Recovery (Optional) **Action:** - Create test: `test_combined_schema_version_cold_start_recovery()` (if time permits) - Test steps: 1. Verify Room database version is observable (check `Database.getVersion()`) 2. Test recovery with version metadata present 3. Verify version doesn't interfere with recovery 4. Verify recovery works identically with version metadata **File:** `android/src/test/java/com/timesafari/dailynotification/DailyNotificationRecoveryTests.kt` **Acceptance:** - [ ] Test created with `@Test` annotation (optional) - [ ] Test labeled with `@resilience @combined-scenarios` comment - [ ] Test verifies schema version observability - [ ] Test verifies version doesn't interfere with recovery - [ ] Test passes deterministically **Reference:** iOS equivalent: `test_combined_schema_version_cold_start_recovery()` in `ios/Tests/DailyNotificationRecoveryTests.swift` --- ### Step 3.4: Verify All Tests Pass **Action:** - Run `./gradlew test` to verify all new tests pass - Run tests multiple times to verify determinism - Check for flaky tests and fix if needed **Acceptance:** - [ ] All new tests pass - [ ] Tests are deterministic (run multiple times, same results) - [ ] No flaky tests --- ### Step 3.5: Update Documentation **Action:** - Update `doc/progress/03-TEST-RUNS.md` with P2.3 test run entry - Update `doc/progress/04-PARITY-MATRIX.md` to mark "Combined edge case tests" as ✅ for Android - Add direct test references (file path + test names) to parity matrix - Update `doc/progress/01-CHANGELOG-WORK.md` with P2.3 completion entry - Update `doc/progress/00-STATUS.md` to mark P2.3 complete **Acceptance:** - [ ] Test run entry added to `03-TEST-RUNS.md` - [ ] Parity matrix updated with ✅ and direct test references - [ ] Changelog entry added - [ ] Status doc updated --- ## Post-Implementation Verification ### CI Verification **Action:** - Run `./ci/run.sh` to verify all checks pass - Verify Android tests run in CI (or are documented as manual) - Check for any new lint/build errors **Acceptance:** - [ ] `./ci/run.sh` passes - [ ] Android tests run in CI (or documented as manual) - [ ] No new lint/build errors --- ### Parity Verification **Action:** - Compare Android combined tests with iOS P2.2 tests - Verify test scenarios are equivalent in intent (not necessarily identical mechanics) - Verify parity matrix reflects accurate status **Acceptance:** - [ ] Android tests mirror iOS intent - [ ] Parity matrix accurately reflects status - [ ] Test references are direct and traceable --- ## Rollback Plan If P2.3 implementation encounters issues: ### Rollback P2.3.3 (Test Scenarios) **Action:** - Remove test scenario files - Revert documentation updates - Keep test infrastructure (P2.3.1, P2.3.2) for future use ### Rollback P2.3.2 (Test Helpers) **Action:** - Remove test helper files - Revert to minimal test infrastructure ### Rollback P2.3.1 (Test Infrastructure) **Action:** - Revert `android/build.gradle` changes - Disable tests again (restore original configuration) - Remove test directory if created **Full Rollback:** - Revert all P2.3 changes - Restore baseline: `git checkout v1.0.11-p2-complete` --- ## Success Criteria P2.3 is complete when: 1. **All P2.3 items completed** (P2.3.1, P2.3.2, P2.3.3) 2. **All acceptance criteria met** (per step) 3. **All invariants preserved** (verified by CI) 4. **Documentation updated** (progress docs, parity matrix, changelog) 5. **Parity achieved** (Android has automated combined tests matching iOS intent) --- ## Next Steps After P2.3 After P2.3 completion: 1. **Tag baseline:** `v1.0.11-p2.3-complete` (optional but recommended) 2. **Consider P2.4:** iOS CI automation (macOS runners) if desired 3. **Consider P1.5b:** Remove iOS/App test harness from published tree --- **Last Updated:** 2025-12-22 **Status:** Implementation-Ready **Next Action:** Begin P2.3.1 - Enable Android Test Infrastructure