Specification-first + property-based TDD methodology to surface logic bugs by testing expected behavior, not current implementation. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
171 lines
5.8 KiB
Markdown
171 lines
5.8 KiB
Markdown
# TDD Test Rewrite Design
|
|
|
|
## Goal
|
|
|
|
Delete all existing tests and rewrite using true TDD methodology (specification-first + property-based) to surface logic bugs in the implementation. Document expected behavior for every tested function.
|
|
|
|
## Methodology
|
|
|
|
### Specification-First + Property-Based TDD
|
|
|
|
- **Specification-First**: Define expected behavior from requirements/intent, not current implementation
|
|
- **Property-Based**: Define invariants that must always hold regardless of input
|
|
- **Document-First**: Add input/output comments to source functions before writing tests
|
|
|
|
### The Process
|
|
|
|
1. **Read** - Examine function, callers, and context to understand intent
|
|
2. **Document** - Add `/// - Expected:` comment block to source file
|
|
3. **Test** - Write tests that verify documented expectations
|
|
4. **Batch Run** - Run all tests at the end
|
|
5. **Fix** - Failures indicate logic bugs; fix implementation to match expectations
|
|
|
|
## Documentation Standard
|
|
|
|
Every tested function gets this comment format:
|
|
|
|
```swift
|
|
/// Calculates optimal routes between game locations.
|
|
///
|
|
/// - Parameter games: List of games to route between (must be chronologically sorted)
|
|
/// - Parameter maxDailyMiles: Maximum driving distance per day (default: 500)
|
|
/// - Returns: Array of valid routes, each containing sequential games
|
|
///
|
|
/// - Expected Behavior:
|
|
/// - Empty input → returns empty array
|
|
/// - Single game → returns array with one single-game route
|
|
/// - Games on same day in distant cities → excludes impossible transitions
|
|
/// - Routes respect maxDailyMiles constraint
|
|
/// - Routes are chronologically ordered
|
|
///
|
|
/// - Invariants:
|
|
/// - All returned routes have games in chronological order
|
|
/// - No route exceeds maxDailyMiles between consecutive same-day games
|
|
func findRoutes(games: [Game], maxDailyMiles: Int = 500) -> [[Game]]
|
|
```
|
|
|
|
## Test Structure
|
|
|
|
Each test file has three sections:
|
|
|
|
```swift
|
|
@Suite("ComponentName")
|
|
struct ComponentNameTests {
|
|
|
|
// MARK: - Specification Tests
|
|
// Tests derived from "Expected Behavior" comments
|
|
|
|
// MARK: - Property Tests
|
|
// Tests that verify invariants for any input
|
|
|
|
// MARK: - Edge Case Tests
|
|
// Boundary conditions and unusual inputs
|
|
}
|
|
```
|
|
|
|
### Property-Based Patterns
|
|
|
|
| Pattern | Example | Applies To |
|
|
|---------|---------|------------|
|
|
| Idempotency | `filter(filter(x)) == filter(x)` | RouteFilters |
|
|
| Roundtrip | `decode(encode(x)) == x` | All Codable models |
|
|
| Ordering | Routes always chronological | GameDAGRouter |
|
|
| Bounds | Distance ≤ maxDailyMiles | TravelEstimator |
|
|
| Conservation | Total games in = total games out | ItineraryBuilder |
|
|
|
|
## Execution Phases
|
|
|
|
### Phase 1: Delete & Infrastructure (~15 min)
|
|
- Delete all 53 existing test files
|
|
- Create `TestFixtures.swift` with factories
|
|
- Create `MockServices.swift` with test doubles
|
|
|
|
### Phase 2: Planning Engine (~2-3 hours)
|
|
Document + test in dependency order:
|
|
1. `TravelEstimator` (no dependencies)
|
|
2. `RouteFilters` (no dependencies)
|
|
3. `PlanningModels` (data structures)
|
|
4. `GameDAGRouter` (uses TravelEstimator)
|
|
5. `ItineraryBuilder` (uses filters)
|
|
6. `ScenarioPlanner` + A/B/C/D variants
|
|
7. `TripPlanningEngine` (orchestrator)
|
|
|
|
### Phase 3: Domain Models (~1.5-2 hours)
|
|
15 files: Game, Trip, Stadium, Team, Sport, Region, Division, TripStop, TravelSegment, TripPreferences, Progress, DynamicSport, TripPoll, AnySport, AchievementDefinitions
|
|
|
|
Focus: Codable roundtrips, computed properties, classification logic
|
|
|
|
### Phase 4: Services (~2-3 hours)
|
|
23 files covering:
|
|
- Error enums (CloudKitError, SyncError, etc.)
|
|
- Result types (SyncResult, ScoreResolutionResult)
|
|
- Configuration states (unconfigured, invalid)
|
|
- Rate limiting, caching, network states
|
|
|
|
### Phase 5: Export (~30 min)
|
|
2 files: ShareableContent types, POISearchService types
|
|
|
|
### Phase 6: Run & Fix (~1-2 hours)
|
|
- Run full test suite
|
|
- Failures = logic bugs in implementation
|
|
- Fix source code to match documented expectations
|
|
- If expectation was wrong, discuss and adjust
|
|
|
|
## Bug Handling
|
|
|
|
| Situation | Action |
|
|
|-----------|--------|
|
|
| Implementation bug | Fix source code to match documented expectation |
|
|
| Expectation wrong | Discuss, update documentation, adjust test |
|
|
|
|
Failures are treated as **bugs to fix**, not tests to adjust.
|
|
|
|
## Success Criteria
|
|
|
|
1. Every public function has documented expectations in source
|
|
2. Tests verify expectations, not current implementation
|
|
3. Property invariants hold for all inputs
|
|
4. No "tests written to pass" - tests define correct behavior independently
|
|
5. All tests pass after fixing surfaced bugs
|
|
|
|
## Files to Test
|
|
|
|
### Planning (11 files)
|
|
- TripPlanningEngine.swift
|
|
- GameDAGRouter.swift
|
|
- ItineraryBuilder.swift
|
|
- RouteFilters.swift
|
|
- TravelEstimator.swift
|
|
- ScenarioPlanner.swift
|
|
- ScenarioAPlanner.swift
|
|
- ScenarioBPlanner.swift
|
|
- ScenarioCPlanner.swift
|
|
- ScenarioDPlanner.swift
|
|
- PlanningModels.swift
|
|
|
|
### Domain (15 files)
|
|
- Game.swift, Stadium.swift, Team.swift, Trip.swift
|
|
- TripStop.swift, TravelSegment.swift, TripPreferences.swift
|
|
- Sport.swift, Region.swift, Division.swift
|
|
- Progress.swift, DynamicSport.swift, TripPoll.swift
|
|
- AnySport.swift, AchievementDefinitions.swift
|
|
|
|
### Services (23 files)
|
|
- CloudKitService.swift, DataProvider.swift, BootstrapService.swift
|
|
- CanonicalSyncService.swift, BackgroundSyncManager.swift
|
|
- LocationService.swift, LocationPermissionManager.swift
|
|
- AchievementEngine.swift, EVChargingService.swift
|
|
- GameMatcher.swift, StadiumProximityMatcher.swift
|
|
- FreeScoreAPI.swift, HistoricalGameScraper.swift
|
|
- NetworkMonitor.swift, RateLimiter.swift
|
|
- PollService.swift, DeepLinkHandler.swift
|
|
- RouteDescriptionGenerator.swift, SuggestedTripsGenerator.swift
|
|
- SyncCancellationToken.swift, VisitPhotoService.swift
|
|
- PhotoMetadataExtractor.swift, StadiumIdentityService.swift
|
|
|
|
### Export (2 files)
|
|
- ShareableContent.swift
|
|
- POISearchService.swift
|
|
|
|
**Total: ~52 source files, estimated 8-10 hours**
|