| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- import Foundation
- @testable import Trio
- // Helper extension for Date from ISO string
- extension Date {
- static func from(isoString: String) -> Date {
- let formatter = ISO8601DateFormatter()
- formatter.formatOptions = [.withInternetDateTime, .withDashSeparatorInDate, .withColonSeparatorInTime, .withTimeZone]
- return formatter.date(from: isoString)!
- }
- var iso8601String: String {
- let formatter = ISO8601DateFormatter()
- formatter.formatOptions = [.withInternetDateTime, .withDashSeparatorInDate, .withColonSeparatorInTime, .withTimeZone]
- return formatter.string(from: self)
- }
- }
- extension CarbsEntry {
- static func forTest(createdAt: Date, carbs: Decimal) -> CarbsEntry {
- CarbsEntry(
- id: nil,
- createdAt: createdAt,
- actualDate: nil,
- carbs: carbs,
- fat: nil,
- protein: nil,
- note: nil,
- enteredBy: nil,
- isFPU: nil,
- fpuID: nil
- )
- }
- }
- extension TimeInterval {
- static func hours(_ hours: Double) -> TimeInterval {
- hours * 60 * 60
- }
- }
- extension [ComputedPumpHistoryEvent] {
- func netInsulin() -> Decimal { compactMap(\.insulin).reduce(0, +) }
- }
- extension Decimal {
- func isWithin(_ error: Decimal, of value: Decimal) -> Bool {
- (self - value).magnitude <= error
- }
- }
- extension Encodable {
- var prettyPrintedJSON: String? {
- let encoder = JSONCoding.encoder
- encoder.outputFormatting = [.prettyPrinted, .withoutEscapingSlashes, .sortedKeys]
- do {
- let data = try encoder.encode(self)
- return String(data: data, encoding: .utf8)
- } catch {
- return nil
- }
- }
- }
- extension IobResult {
- func approximatelyEquals(_ rhs: IobResult) -> Bool {
- // Compare all properties
- guard iob.isWithin(0.001, of: rhs.iob),
- activity.isWithin(0.0001, of: rhs.activity),
- basaliob.isWithin(0.001, of: rhs.basaliob),
- bolusiob.isWithin(0.001, of: rhs.bolusiob),
- netbasalinsulin.isWithin(0.05, of: rhs.netbasalinsulin),
- bolusinsulin.isWithin(0.001, of: rhs.bolusinsulin),
- time == rhs.time,
- lastBolusTime == rhs.lastBolusTime
- else {
- return false
- }
- // Compare nested IobWithZeroTemp
- guard iobWithZeroTemp.iob.isWithin(0.001, of: rhs.iobWithZeroTemp.iob),
- iobWithZeroTemp.activity.isWithin(0.0001, of: rhs.iobWithZeroTemp.activity),
- iobWithZeroTemp.basaliob.isWithin(0.001, of: rhs.iobWithZeroTemp.basaliob),
- iobWithZeroTemp.bolusiob.isWithin(0.001, of: rhs.iobWithZeroTemp.bolusiob),
- iobWithZeroTemp.netbasalinsulin.isWithin(0.05, of: rhs.iobWithZeroTemp.netbasalinsulin),
- iobWithZeroTemp.bolusinsulin.isWithin(0.001, of: rhs.iobWithZeroTemp.bolusinsulin),
- iobWithZeroTemp.time == rhs.iobWithZeroTemp.time
- else {
- return false
- }
- // Compare optional LastTemp
- if let selfTemp = lastTemp, let rhsTemp = rhs.lastTemp {
- guard let selfDuration = selfTemp.duration, let rhsDuration = rhsTemp.duration, selfDuration.isWithin(
- 0.01,
- of: rhsDuration
- ) else {
- return false
- }
- // Both are non-nil, compare their properties
- return selfTemp.rate == rhsTemp.rate &&
- selfTemp.timestamp == rhsTemp.timestamp &&
- selfTemp.started_at == rhsTemp.started_at &&
- selfTemp.date == rhsTemp.date
- } else {
- // Both should be nil for equality
- return lastTemp == nil && rhs.lastTemp == nil
- }
- }
- }
- extension ComputedPumpHistoryEvent {
- func contains(tempBolus: ComputedPumpHistoryEvent) -> Bool {
- guard type == .tempBasal, tempBolus.isTempBolus else {
- fatalError("invalid type for computed pump history event")
- }
- let start = timestamp
- let end = start + duration!.minutesToSeconds
- return start <= tempBolus.timestamp && end > tempBolus.timestamp
- }
- }
|