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.
This commit is contained in:
421
docs/progress/P2.3-IMPLEMENTATION-CHECKLIST.md
Normal file
421
docs/progress/P2.3-IMPLEMENTATION-CHECKLIST.md
Normal file
@@ -0,0 +1,421 @@
|
||||
# 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:**
|
||||
```kotlin
|
||||
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:**
|
||||
```kotlin
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user