Files
Sportstime/docs/plans/2026-01-16-tdd-test-rewrite-design.md
Trey t 035dd6f5de docs: add TDD test rewrite design
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>
2026-01-16 11:11:46 -06:00

5.8 KiB

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:

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

@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