// Les boutons de saisie rapide viennent des préréglages de l'utilisateur. // // ⚠️ **Ce qui est en jeu.** Le widget enregistrait un café à 100 ml, en dur. Le // serveur avait pourtant tranché la question, et documenté pourquoi : « 60 ml, // c'est un expresso. L'ancien bouton "Café" imposait ce volume à tous les // cafés, y compris un mug de 250 ml : le journal était faux. » Il propose // désormais expresso 60 et mug 250, et ces préréglages sont **personnalisables** // (`GET/PUT /api/drinks/presets`). Le widget ignorait tout cela — 100 ml ne // correspond à aucun préréglage. // // Les volumes voyagent donc dans le snapshot. Le repli, pour un snapshot écrit // par une version antérieure, reprend les valeurs du serveur — jamais les 100 ml. import XCTest @testable import CoachModel final class QuickDrinkTests: XCTestCase { private func snapshot(_ drinks: [CoachWidgetSnapshot.QuickDrink]?) -> CoachWidgetSnapshot { var s = CoachWidgetSnapshot(updatedAt: Date(timeIntervalSince1970: 1_755_000_000)) s.quickDrinks = drinks return s } func testTheUsersPresetsAreUsedAsIs() { let presets = [ CoachWidgetSnapshot.QuickDrink(ref: "water", label: "Verre", volumeMl: 200), CoachWidgetSnapshot.QuickDrink(ref: "coffee", label: "Expresso", volumeMl: 60), ] XCTAssertEqual(snapshot(presets).resolvedQuickDrinks, presets) } func testWithoutPresetsTheFallbackMatchesTheServer() { let fallback = snapshot(nil).resolvedQuickDrinks XCTAssertEqual(fallback.count, 3) XCTAssertEqual(fallback.map(\.volumeMl), [200, 500, 60]) XCTAssertEqual(fallback.map(\.ref), ["water", "water", "coffee"]) } func testTheCoffeeIsNeverOneHundredMillilitres() { // La valeur qui ne correspondait à aucun préréglage. for drink in snapshot(nil).resolvedQuickDrinks where drink.ref == "coffee" { XCTAssertNotEqual(drink.volumeMl, 100) } } func testAnEmptyListFallsBackRatherThanShowNothing() { XCTAssertEqual(snapshot([]).resolvedQuickDrinks.count, 3) } func testAtMostThreeButtonsFit() { let many = (1...8).map { i in CoachWidgetSnapshot.QuickDrink(ref: "water", label: "P\(i)", volumeMl: Double(i) * 100) } XCTAssertEqual(snapshot(many).resolvedQuickDrinks.count, 3) } func testAnAbsurdVolumeIsRefused() { // Un préréglage corrompu ne doit pas produire un bouton qui enregistre // n'importe quoi : on retombe sur le repli. let bad = [CoachWidgetSnapshot.QuickDrink(ref: "water", label: "X", volumeMl: 0)] XCTAssertEqual(snapshot(bad).resolvedQuickDrinks.map(\.volumeMl), [200, 500, 60]) } func testAnOlderSnapshotStillDecodes() throws { let json = """ {"updatedAt": 1755000000, "waterMlToday": 500} """.data(using: .utf8)! let decoded = try JSONDecoder().decode(CoachWidgetSnapshot.self, from: json) XCTAssertEqual(decoded.waterMlToday, 500) XCTAssertEqual(decoded.resolvedQuickDrinks.count, 3) } func testPresetsSurviveTheDayRollover() throws { // Les préréglages ne sont pas une donnée du jour : ils ne se périment pas. var s = snapshot([CoachWidgetSnapshot.QuickDrink(ref: "coffee", label: "Mug", volumeMl: 250)]) s.waterMlToday = 800 let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: s.updatedAt)! let aged = s.asOf(tomorrow) XCTAssertEqual(aged.waterMlToday, 0, "l'eau du jour se remet à zéro") XCTAssertEqual(aged.resolvedQuickDrinks.first?.volumeMl, 250) } }