Files
daily-notification-plugin/docs/progress/P2.3-IMPLEMENTATION-CHECKLIST.md
Matthew Raymer 6b5b886951 feat(ios): complete P2.1 schema versioning and P2.2 combined edge case tests
P2.1: iOS Schema Versioning Strategy
- Added SCHEMA_VERSION constant and checkSchemaVersion() method in PersistenceController
- Version stored in NSPersistentStore metadata (observability contract, not migration gate)
- CoreData auto-migration remains authoritative; version mismatches logged, not blocked
- Documentation added to ios/Plugin/README.md with migration contract

P2.2: Combined Edge Case Tests
- Added 3 resilience test scenarios to DailyNotificationRecoveryTests.swift:
  - test_combined_dst_boundary_duplicate_delivery_cold_start()
  - test_combined_rollover_duplicate_delivery_cold_start()
  - test_combined_schema_version_cold_start_recovery()
- All tests labeled with @resilience @combined-scenarios comments
- Tests verify idempotency and correctness under combined stressors

P2.3: Android Combined Tests Design
- Created P2.3-DESIGN.md with scope, invariants, and acceptance criteria
- Created P2.3-IMPLEMENTATION-CHECKLIST.md with step-by-step execution plan
- Design ready for implementation to achieve parity with iOS P2.2

Documentation Updates
- Fixed parity matrix: iOS invalid data handling now correctly shows " Recovery tested" with test references
- Updated progress docs (00-STATUS.md, 01-CHANGELOG-WORK.md, 03-TEST-RUNS.md, 04-PARITY-MATRIX.md)
- Updated P2-DESIGN.md to reflect P2.3 scope (Android combined tests)
- Updated SYSTEM_INVARIANTS.md baseline tag references

Baseline Tag
- Created and pushed v1.0.11-p2-complete tag
- Tag represents P2.x completion (schema versioning + combined resilience tests)

All invariants preserved. CI passes. Tests runnable via xcodebuild on macOS.
2025-12-22 12:59:40 +00:00

13 KiB

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: docs/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:

package com.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:

package com.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 docs/progress/03-TEST-RUNS.md with P2.3 test run entry
  • Update docs/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 docs/progress/01-CHANGELOG-WORK.md with P2.3 completion entry
  • Update docs/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