Files
workouts/WorkoutsTests/ExerciseLibraryQueryTests.swift
rzen 6e440317c4 Restructure into a three-tab app with Progress, goals, and Meditation
The UX redesign's first landing (spec in UX-REDESIGN.md): ContentView
becomes a Today / Progress / Settings TabView, "Routine" replaces
"Split" in every user-facing string and view name (code-level types
keep their names), and workout starting moves to shared
WorkoutStarter / StartedWorkoutNavigator plumbing.

- New Progress tab: weekly goal streaks, workout trends, per-exercise
  weight progression, achievements, and the full history list
  (WorkoutLogsView -> WorkoutHistoryView).
- Goals: stable categories workouts roll up to, managed from Settings.
- New Meditation exercise + starter routine; timed sits record to
  Apple Health as Mind & Body sessions.

Claude-Session: https://claude.ai/code/session_012qw2itfzKyEJ1HpsFt8Ex4
2026-07-11 07:53:01 -04:00

80 lines
3.9 KiB
Swift

import Foundation
import SwiftData
import Testing
@testable import Workouts
/// Exercises the exact fetches `ExerciseLibraryDetailView` builds its machine-settings
/// card and progression-chart gate from, against an in-memory store — locking the
/// predicate shapes to the cache schema.
@MainActor
struct ExerciseLibraryQueryTests {
private func makeContext() throws -> ModelContext {
let schema = Schema([Routine.self, Exercise.self, Workout.self, WorkoutLog.self])
// cloudKitDatabase: .none matches WorkoutsModelContainer — see SeedLibraryTests.
let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true, cloudKitDatabase: .none)
let container = try ModelContainer(for: schema, configurations: [config])
return ModelContext(container)
}
@Test func machineSettingsFetchFindsRecordedSettings() throws {
let context = try makeContext()
// PINNED: routine documents live under the on-disk "Splits/" directory.
let routine = Routine(id: "S1", name: "Upper Body", color: "indigo",
systemImage: "dumbbell.fill", order: 0,
createdAt: .now, updatedAt: .now, jsonRelativePath: "Splits/S1.json")
let exercise = Exercise(id: "E1", name: "Chest Press", order: 0, sets: 3, reps: 10,
weight: 100, loadType: LoadType.weight.rawValue,
durationTotalSeconds: 0,
machineSettings: [MachineSetting(name: "Seat Height", value: "4")])
exercise.routine = routine
context.insert(routine)
context.insert(exercise)
try context.save()
let name = "Chest Press"
let fetched = try context.fetch(FetchDescriptor<Exercise>(
predicate: #Predicate { $0.name == name }
))
#expect(fetched.count == 1)
#expect(fetched.first?.machineSettings == [MachineSetting(name: "Seat Height", value: "4")])
#expect(fetched.first?.routine?.name == "Upper Body")
}
@Test func weightedLogFetchGatesChart() throws {
let context = try makeContext()
let workout = Workout(id: "W1", routineID: nil, routineName: "Upper Body",
start: .now, end: .now, statusRaw: WorkoutStatus.completed.rawValue,
createdAt: .now, updatedAt: .now,
jsonRelativePath: "Workouts/2026/07/W1.json")
let weighted = WorkoutLog(id: "L1", exerciseName: "Chest Press", order: 0, sets: 3,
reps: 10, weight: 100, loadType: LoadType.weight.rawValue,
durationTotalSeconds: 0, currentStateIndex: 3,
statusRaw: WorkoutStatus.completed.rawValue, notes: nil, date: .now)
let bodyweight = WorkoutLog(id: "L2", exerciseName: "Plank", order: 1, sets: 3,
reps: 0, weight: 0, loadType: LoadType.duration.rawValue,
durationTotalSeconds: 60, currentStateIndex: 3,
statusRaw: WorkoutStatus.completed.rawValue, notes: nil, date: .now)
weighted.workout = workout
bodyweight.workout = workout
context.insert(workout)
context.insert(weighted)
context.insert(bodyweight)
try context.save()
let completedRaw = WorkoutStatus.completed.rawValue
let chestPress = "Chest Press"
let chestLogs = try context.fetch(FetchDescriptor<WorkoutLog>(
predicate: #Predicate { $0.exerciseName == chestPress && $0.statusRaw == completedRaw && $0.weight > 0 }
))
#expect(chestLogs.count == 1)
let plank = "Plank"
let plankLogs = try context.fetch(FetchDescriptor<WorkoutLog>(
predicate: #Predicate { $0.exerciseName == plank && $0.statusRaw == completedRaw && $0.weight > 0 }
))
#expect(plankLogs.isEmpty)
}
}