Files
SportstimeAPI/.planning/phases/01-semantic-position-model/01-02-PLAN.md
Trey t a7d0e7f049 fix(01): revise plans based on checker feedback
- 01-01: Reframe must_haves.truths to user-observable behaviors
- 01-01: Remove unimplemented key_link (wiring happens in later phases)
- 01-02: Add test case for DATA-04 (custom items at any position)
- 01-02: Add must_haves.truths entry for custom item flexibility
- 01-02: Document LocalItineraryItem is standalone (no sibling models needed)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 13:47:12 -06:00

205 lines
8.6 KiB
Markdown

---
phase: 01-semantic-position-model
plan: 02
type: execute
wave: 2
depends_on: ["01-01"]
files_modified:
- SportsTimeTests/SortOrderProviderTests.swift
- SportsTimeTests/SemanticPositionPersistenceTests.swift
autonomous: true
must_haves:
truths:
- "User can persist an item's position, reload, and find it in the same location"
- "Moving travel segment to different day updates its day property"
- "Inserting between two items gets sortOrder between their values (e.g., 1.0 and 2.0 -> 1.5)"
- "Games remain fixed at their schedule-determined positions"
- "Custom items can be placed at any sortOrder value (negative, zero, positive)"
artifacts:
- path: "SportsTimeTests/SortOrderProviderTests.swift"
provides: "Unit tests for SortOrderProvider"
min_lines: 80
- path: "SportsTimeTests/SemanticPositionPersistenceTests.swift"
provides: "Integration tests for position persistence"
min_lines: 100
key_links:
- from: "SortOrderProviderTests"
to: "SortOrderProvider"
via: "Test imports and calls provider methods"
pattern: "SortOrderProvider\\."
- from: "SemanticPositionPersistenceTests"
to: "LocalItineraryItem"
via: "Creates and persists items via SwiftData"
pattern: "LocalItineraryItem"
---
<objective>
Create comprehensive tests verifying the semantic position model works correctly.
Purpose: Prove that requirements DATA-01 through DATA-05 and PERS-01 through PERS-03 are satisfied. Tests must verify: sortOrder calculation correctness, midpoint insertion math, day derivation accuracy, and persistence survival across SwiftData reload.
Output: Two test files covering unit tests for SortOrderProvider and integration tests for persistence behavior.
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01-semantic-position-model/01-RESEARCH.md
@.planning/phases/01-semantic-position-model/01-01-SUMMARY.md
# Source files
@SportsTime/Core/Models/Domain/SortOrderProvider.swift
@SportsTime/Core/Models/Domain/ItineraryItem.swift
@SportsTime/Core/Models/Local/SavedTrip.swift
</context>
<tasks>
<task type="auto">
<name>Task 1: Create SortOrderProvider unit tests</name>
<files>SportsTimeTests/SortOrderProviderTests.swift</files>
<action>
Create a new test file `SortOrderProviderTests.swift` with tests for all SortOrderProvider methods.
Test cases to include:
**initialSortOrder tests:**
- `test_initialSortOrder_midnight_returns100`: 00:00 -> 100.0
- `test_initialSortOrder_noon_returns820`: 12:00 -> 100 + 720 = 820.0
- `test_initialSortOrder_7pm_returns1240`: 19:00 -> 100 + 1140 = 1240.0
- `test_initialSortOrder_1159pm_returns1539`: 23:59 -> 100 + 1439 = 1539.0
**sortOrderBetween tests:**
- `test_sortOrderBetween_integers_returnsMidpoint`: (1.0, 2.0) -> 1.5
- `test_sortOrderBetween_negativeAndPositive_returnsMidpoint`: (-1.0, 1.0) -> 0.0
- `test_sortOrderBetween_fractionals_returnsMidpoint`: (1.5, 1.75) -> 1.625
**sortOrderBefore tests:**
- `test_sortOrderBefore_positive_returnsLower`: 1.0 -> 0.0
- `test_sortOrderBefore_negative_returnsLower`: -1.0 -> -2.0
**sortOrderAfter tests:**
- `test_sortOrderAfter_positive_returnsHigher`: 1.0 -> 2.0
- `test_sortOrderAfter_zero_returnsOne`: 0.0 -> 1.0
**needsNormalization tests:**
- `test_needsNormalization_wellSpaced_returnsFalse`: items with gaps > 1e-10
- `test_needsNormalization_tinyGap_returnsTrue`: items with gap < 1e-10
- `test_needsNormalization_empty_returnsFalse`: empty array
- `test_needsNormalization_singleItem_returnsFalse`: one item
**normalize tests:**
- `test_normalize_reassignsIntegerSpacing`: after normalize, sortOrders are 1.0, 2.0, 3.0...
- `test_normalize_preservesOrder`: relative order unchanged after normalize
Use `@testable import SportsTime` at top.
</action>
<verify>
Tests compile and pass:
```bash
xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' -only-testing:SportsTimeTests/SortOrderProviderTests test 2>&1 | grep -E "(Test Case|passed|failed)"
```
</verify>
<done>SortOrderProviderTests.swift exists with 16+ test cases, all tests pass</done>
</task>
<task type="auto">
<name>Task 2: Create persistence integration tests</name>
<files>SportsTimeTests/SemanticPositionPersistenceTests.swift</files>
<action>
Create a new test file `SemanticPositionPersistenceTests.swift` with integration tests for semantic position persistence.
These tests verify PERS-01, PERS-02, PERS-03, and DATA-04 requirements.
Test cases to include:
**Position persistence (PERS-01):**
- `test_itineraryItem_positionSurvivesEncodeDecode`: Create ItineraryItem with specific day/sortOrder, encode to JSON, decode, verify day and sortOrder match exactly
- `test_localItineraryItem_positionSurvivesSwiftData`: Create LocalItineraryItem, save to SwiftData ModelContext, fetch back, verify day and sortOrder match
**Semantic-only state (PERS-02):**
- `test_itineraryItem_allPositionPropertiesAreCodable`: Verify ItineraryItem.day and .sortOrder are included in Codable output (not transient)
**Midpoint insertion (PERS-03):**
- `test_midpointInsertion_50Times_maintainsPrecision`: Insert 50 times between adjacent items, verify all sortOrders are distinct
- `test_midpointInsertion_producesCorrectValue`: Insert between sortOrder 1.0 and 2.0, verify result is 1.5
**Day property updates (DATA-02, DATA-05):**
- `test_travelItem_dayCanBeUpdated`: Create travel item with day=1, update to day=3, verify day property changed
- `test_item_belongsToExactlyOneDay`: Verify item.day is a single Int, not optional or array
**Game immutability (DATA-03):**
- `test_gameItem_sortOrderDerivedFromTime`: Create game item for 7pm game, verify sortOrder is ~1240.0 (100 + 19*60)
**Custom item flexibility (DATA-04):**
- `test_customItem_canBePlacedAtAnyPosition`: Create custom items with sortOrder values at negative (-5.0, before all games), between games (500.0), and after all games (2000.0). Verify all three persist correctly and can coexist on the same day sorted correctly.
Use in-memory SwiftData ModelContainer for tests. Note: LocalItineraryItem is standalone with no relationships - it can be registered alone:
```swift
let config = ModelConfiguration(isStoredInMemoryOnly: true)
let container = try ModelContainer(for: LocalItineraryItem.self, configurations: config)
```
Import XCTest, SwiftData, and `@testable import SportsTime`.
</action>
<verify>
Tests compile and pass:
```bash
xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' -only-testing:SportsTimeTests/SemanticPositionPersistenceTests test 2>&1 | grep -E "(Test Case|passed|failed)"
```
</verify>
<done>SemanticPositionPersistenceTests.swift exists with 9+ test cases, all tests pass</done>
</task>
<task type="auto">
<name>Task 3: Run full test suite to verify no regressions</name>
<files></files>
<action>
Run the complete test suite to verify:
1. All new tests pass
2. No existing tests broken by new code
3. Build and test cycle completes successfully
If any tests fail, investigate and fix before completing the plan.
</action>
<verify>
```bash
xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' test 2>&1 | tail -30
```
Look for "** TEST SUCCEEDED **" at the end.
</verify>
<done>Full test suite passes with no failures, including all new and existing tests</done>
</task>
</tasks>
<verification>
1. SortOrderProviderTests.swift exists with 16+ test methods covering all SortOrderProvider functions
2. SemanticPositionPersistenceTests.swift exists with 9+ test methods covering persistence requirements
3. All tests pass when run individually and as part of full suite
4. Tests verify the success criteria from ROADMAP.md Phase 1:
- Position survives reload (tested via encode/decode and SwiftData)
- Travel day update works (tested via day property mutation)
- Midpoint insertion works (tested via 50-iteration precision test)
- Games use time-based sortOrder (tested via initialSortOrder)
- Custom items can be placed anywhere (tested via negative/between/after positions)
</verification>
<success_criteria>
- 25+ new test cases across 2 test files
- All tests pass
- Tests directly verify Phase 1 requirements DATA-01 through DATA-05 and PERS-01 through PERS-03
- No regression in existing tests
</success_criteria>
<output>
After completion, create `.planning/phases/01-semantic-position-model/01-02-SUMMARY.md`
</output>