Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,8 @@ public enum WorkoutDetector {

/// #510: backfill ONLY the avgHr/maxHr/energyKcal/strain fields `real` doesn't already have, from a
/// detected bout's own computed values — never touching a field that's already present, whether
/// typed by the user, imported, or filled by an earlier pass. `real` unchanged (`==`) means it
/// already had everything, so the caller can tell whether a write is actually needed. Kotlin twin
/// typed by the user, imported, or filled by an earlier pass. `real` unchanged (`==`) means no
/// available value was filled, so the caller can tell whether a write is actually needed. Kotlin twin
/// IntelligenceEngine.backfillWorkoutFromDetectedBout.
public static func backfillWorkout(_ real: WorkoutRow, avgBpm: Int, peakHR: Int, caloriesKcal: Double?, strain: Double?) -> WorkoutRow {
WorkoutRow(
Expand All @@ -383,7 +383,8 @@ public enum WorkoutDetector {
avgHr: real.avgHr ?? avgBpm,
maxHr: real.maxHr ?? peakHR,
strain: real.strain ?? strain,
distanceM: real.distanceM, zonesJSON: real.zonesJSON, notes: real.notes)
distanceM: real.distanceM, zonesJSON: real.zonesJSON, notes: real.notes,
steps: real.steps)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,21 @@ import WhoopStore
/// though the detector had already computed a valid average for that exact window.
final class WorkoutDetectorBackfillTests: XCTestCase {

private func manualRow(avgHr: Int? = nil, maxHr: Int? = nil, energyKcal: Double? = nil, strain: Double? = nil) -> WorkoutRow {
private func manualRow(avgHr: Int? = nil, maxHr: Int? = nil, energyKcal: Double? = nil,
strain: Double? = nil, steps: Int? = 4_200) -> WorkoutRow {
WorkoutRow(startTs: 1_000, endTs: 1_600, sport: "Running", source: "manual",
durationS: 600, energyKcal: energyKcal, avgHr: avgHr, maxHr: maxHr,
strain: strain, distanceM: nil, zonesJSON: nil, notes: nil)
strain: strain, distanceM: nil, zonesJSON: nil, notes: nil, steps: steps)
}

func testFillsAllMissingFields() {
func testFillsAllMissingFieldsAndPreservesSteps() {
let real = manualRow()
let filled = WorkoutDetector.backfillWorkout(real, avgBpm: 150, peakHR: 170, caloriesKcal: 80.0, strain: 9.5)
XCTAssertEqual(filled.avgHr, 150)
XCTAssertEqual(filled.maxHr, 170)
XCTAssertEqual(filled.energyKcal, 80.0)
XCTAssertEqual(filled.strain, 9.5)
XCTAssertEqual(filled.steps, 4_200, "stored per-session steps must survive backfill")
// The rest of the row is carried over untouched.
XCTAssertEqual(filled.startTs, real.startTs)
XCTAssertEqual(filled.sport, real.sport)
Expand All @@ -37,7 +39,7 @@ final class WorkoutDetectorBackfillTests: XCTestCase {
XCTAssertEqual(filled.strain, 9.5, "a missing field must still be filled")
}

func testRowWithEverythingAlreadyPresentIsUnchanged() {
func testRowWithStepsAndEverythingAlreadyPresentIsUnchanged() {
let real = manualRow(avgHr: 140, maxHr: 160, energyKcal: 50.0, strain: 8.0)
let filled = WorkoutDetector.backfillWorkout(real, avgBpm: 150, peakHR: 170, caloriesKcal: 80.0, strain: 9.5)
XCTAssertEqual(filled, real, "nothing to fill -> byte-identical row, so the caller can skip the write")
Expand Down