- 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>
205 lines
8.6 KiB
Markdown
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>
|