import Foundation import Testing import IndieSync @testable import Workouts /// Round-trips `SplitDocument` through `DocumentCoder` — the exact JSON codec /// (ISO-8601 dates, sorted keys, pretty-printed) `DocumentFileStore` uses to /// write and read every document in this app's iCloud container. struct SplitDocumentCodableTests { /// A whole-second epoch so ISO-8601 round-trips (no fractional-second loss). private static let stableTimestamp = Date(timeIntervalSince1970: 1_700_000_000) @Test func encodeDecodeRoundTrip() throws { let exercise = ExerciseDocument( id: ULID.make(), name: "Bench Press", order: 0, sets: 4, reps: 10, weight: 135, loadType: 0, durationSeconds: 0, weightLastUpdated: Self.stableTimestamp, weightReminderWeeks: 4, category: ExerciseCategory.warmup.rawValue ) let original = SplitDocument( schemaVersion: SplitDocument.currentSchemaVersion, id: ULID.make(), name: "Push Day", color: "blue", systemImage: "dumbbell.fill", order: 0, createdAt: Self.stableTimestamp, updatedAt: Self.stableTimestamp, exercises: [exercise], activityType: nil ) let data = try DocumentCoder.encode(original) let decoded = try DocumentCoder.decode(SplitDocument.self, from: data) #expect(decoded == original) #expect(decoded.relativePath == "Splits/\(original.id).json") } /// A pre-category exercise (no `category` key) must still decode — the field is /// optional and deliberately not schema-bumped, defaulting to the main circuit. @Test func decodesLegacyExerciseWithoutCategory() throws { let json = """ { "schemaVersion": 1, "id": "01HZZZZZZZZZZZZZZZZZZZZZZZ", "name": "Push Day", "color": "blue", "systemImage": "dumbbell.fill", "order": 0, "createdAt": "2023-11-14T22:13:20Z", "updatedAt": "2023-11-14T22:13:20Z", "exercises": [{ "id": "01HZZZZZZZZZZZZZZZZZZZZZZY", "name": "Bench Press", "order": 0, "sets": 4, "reps": 10, "weight": 135, "loadType": 1, "durationSeconds": 0, "weightReminderWeeks": 4 }] } """ let decoded = try DocumentCoder.decode(SplitDocument.self, from: Data(json.utf8)) #expect(decoded.exercises.first?.category == nil) } }