| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- import Foundation
- import Testing
- @testable import Trio
- struct ProfileGeneratorTests {
- // Base test inputs that match the JavaScript test setup
- private func createBaseInputs() -> (
- PumpSettings,
- BGTargets,
- [BasalProfileEntry],
- InsulinSensitivities,
- Preferences,
- CarbRatios,
- [TempTarget],
- String,
- TrioSettings
- ) {
- let pumpSettings = PumpSettings(
- insulinActionCurve: 10,
- maxBolus: 10,
- maxBasal: 2
- )
- let bgTargets = BGTargets(
- units: .mgdL,
- userPreferredUnits: .mgdL,
- targets: [
- BGTargetEntry(low: 100, high: 120, start: "00:00", offset: 0)
- ]
- )
- let basalProfile = [
- BasalProfileEntry(start: "00:00", minutes: 0, rate: 1.0)
- ]
- let isf = InsulinSensitivities(
- units: .mgdL,
- userPreferredUnits: .mgdL,
- sensitivities: [
- InsulinSensitivityEntry(sensitivity: 100, offset: 0, start: "00:00")
- ]
- )
- let preferences = Preferences()
- let carbRatios = CarbRatios(
- units: .grams,
- schedule: [
- CarbRatioEntry(start: "00:00", offset: 0, ratio: 20)
- ]
- )
- let tempTargets: [TempTarget] = []
- let model = "523"
- let trioSettings = TrioSettings()
- return (pumpSettings, bgTargets, basalProfile, isf, preferences, carbRatios, tempTargets, model, trioSettings)
- }
- @Test("Basic profile generation should create profile with correct values") func testBasicProfileGeneration() throws {
- let inputs = createBaseInputs()
- let profile = try ProfileGenerator.generate(
- pumpSettings: inputs.0,
- bgTargets: inputs.1,
- basalProfile: inputs.2,
- isf: inputs.3,
- preferences: inputs.4,
- carbRatios: inputs.5,
- tempTargets: inputs.6,
- model: inputs.7,
- clock: Date()
- )
- #expect(profile.maxIob == 0)
- #expect(profile.dia == 10)
- #expect(profile.sens == 100)
- #expect(profile.currentBasal == 1)
- #expect(profile.maxBg == 100)
- #expect(profile.minBg == 100)
- #expect(profile.carbRatio == 20)
- }
- @Test("Profile with active temp target should use temp target values") func testProfileWithTempTarget() throws {
- var inputs = createBaseInputs()
- // Create temp target 5 minutes ago that lasts 20 minutes
- let currentTime = Date()
- let creationDate = currentTime.addingTimeInterval(-5 * 60)
- let tempTarget = TempTarget(
- name: "Eating Soon",
- createdAt: creationDate,
- targetTop: 80,
- targetBottom: 80,
- duration: 20,
- enteredBy: "Test",
- reason: "Eating Soon",
- isPreset: nil,
- enabled: nil,
- halfBasalTarget: nil
- )
- inputs.6 = [tempTarget]
- let profile = try ProfileGenerator.generate(
- pumpSettings: inputs.0,
- bgTargets: inputs.1,
- basalProfile: inputs.2,
- isf: inputs.3,
- preferences: inputs.4,
- carbRatios: inputs.5,
- tempTargets: inputs.6,
- model: inputs.7,
- clock: currentTime
- )
- #expect(profile.maxIob == 0)
- #expect(profile.dia == 10)
- #expect(profile.sens == 100)
- #expect(profile.currentBasal == 1)
- #expect(profile.maxBg == 80)
- #expect(profile.minBg == 80)
- #expect(profile.carbRatio == 20)
- #expect(profile.temptargetSet == true)
- }
- @Test("Profile with expired temp target should use default values") func testProfileWithExpiredTempTarget() throws {
- var inputs = createBaseInputs()
- // Create temp target 90 minutes ago
- let currentTime = Date()
- let creationDate = currentTime.addingTimeInterval(-90 * 60)
- let tempTarget = TempTarget(
- name: "Eating Soon",
- createdAt: creationDate,
- targetTop: 80,
- targetBottom: 80,
- duration: 20,
- enteredBy: "Test",
- reason: "Eating Soon",
- isPreset: nil,
- enabled: nil,
- halfBasalTarget: nil
- )
- inputs.6 = [tempTarget]
- let profile = try ProfileGenerator.generate(
- pumpSettings: inputs.0,
- bgTargets: inputs.1,
- basalProfile: inputs.2,
- isf: inputs.3,
- preferences: inputs.4,
- carbRatios: inputs.5,
- tempTargets: inputs.6,
- model: inputs.7,
- clock: currentTime
- )
- #expect(profile.maxIob == 0)
- #expect(profile.dia == 10)
- #expect(profile.sens == 100)
- #expect(profile.currentBasal == 1)
- #expect(profile.maxBg == 100)
- #expect(profile.minBg == 100)
- #expect(profile.carbRatio == 20)
- }
- @Test("Profile with zero duration temp target should use default values") func testProfileWithZeroDurationTempTarget() throws {
- var inputs = createBaseInputs()
- // Create temp target 5 minutes ago with 0 duration
- let currentTime = Date()
- let creationDate = currentTime.addingTimeInterval(-5 * 60)
- let tempTarget = TempTarget(
- name: "Eating Soon",
- createdAt: creationDate,
- targetTop: 80,
- targetBottom: 80,
- duration: 0,
- enteredBy: "Test",
- reason: "Eating Soon",
- isPreset: nil,
- enabled: nil,
- halfBasalTarget: nil
- )
- inputs.6 = [tempTarget]
- let profile = try ProfileGenerator.generate(
- pumpSettings: inputs.0,
- bgTargets: inputs.1,
- basalProfile: inputs.2,
- isf: inputs.3,
- preferences: inputs.4,
- carbRatios: inputs.5,
- tempTargets: inputs.6,
- model: inputs.7,
- clock: currentTime
- )
- #expect(profile.maxIob == 0)
- #expect(profile.dia == 10)
- #expect(profile.sens == 100)
- #expect(profile.currentBasal == 1)
- #expect(profile.maxBg == 100)
- #expect(profile.minBg == 100)
- #expect(profile.carbRatio == 20)
- }
- @Test("Profile generation with invalid DIA should throw error") func testInvalidDIA() throws {
- var inputs = createBaseInputs()
- inputs.0 = PumpSettings(
- insulinActionCurve: 1,
- maxBolus: 10,
- maxBasal: 2
- )
- #expect(throws: ProfileError.invalidDIA(value: 1)) {
- _ = try ProfileGenerator.generate(
- pumpSettings: inputs.0,
- bgTargets: inputs.1,
- basalProfile: inputs.2,
- isf: inputs.3,
- preferences: inputs.4,
- carbRatios: inputs.5,
- tempTargets: inputs.6,
- model: inputs.7,
- clock: Date()
- )
- }
- }
- @Test("Profile generation with zero basal rate should throw error") func testCurrentBasalZero() throws {
- var inputs = createBaseInputs()
- inputs.2 = [
- BasalProfileEntry(start: "00:00", minutes: 0, rate: 0.0)
- ]
- // the reason it throws this error is due to some complex logic
- // in Javascript around the handling of nil and 0 basal rate entries
- #expect(throws: ProfileError.invalidMaxDailyBasal(value: 0)) {
- _ = try ProfileGenerator.generate(
- pumpSettings: inputs.0,
- bgTargets: inputs.1,
- basalProfile: inputs.2,
- isf: inputs.3,
- preferences: inputs.4,
- carbRatios: inputs.5,
- tempTargets: inputs.6,
- model: inputs.7,
- clock: Date()
- )
- }
- }
- @Test("Profile should store model string correctly") func testModelString() throws {
- var inputs = createBaseInputs()
- inputs.7 = "\"554\"\n"
- let profile = try ProfileGenerator.generate(
- pumpSettings: inputs.0,
- bgTargets: inputs.1,
- basalProfile: inputs.2,
- isf: inputs.3,
- preferences: inputs.4,
- carbRatios: inputs.5,
- tempTargets: inputs.6,
- model: inputs.7,
- clock: Date()
- )
- #expect(profile.model == "554")
- }
- @Test("Profile should use temptargetSet key in output json") func testTempTargetSetKey() async throws {
- var inputs = createBaseInputs()
- inputs.7 = "\"554\"\n"
- let now = Date()
- let tempTargets = [
- TempTarget(
- name: nil,
- createdAt: now - 1.hoursToSeconds,
- targetTop: 100,
- targetBottom: 80,
- duration: 120,
- enteredBy: nil,
- reason: nil,
- isPreset: nil,
- enabled: nil,
- halfBasalTarget: nil
- )
- ]
- let openAps = OpenAPS(storage: BaseFileStorage(), tddStorage: MockTDDStorage())
- let jsResult = await openAps.makeProfileJavascript(
- preferences: inputs.4,
- pumpSettings: inputs.0,
- bgTargets: inputs.1,
- basalProfile: inputs.2,
- isf: inputs.3,
- carbRatio: inputs.5,
- tempTargets: tempTargets,
- model: inputs.7,
- autotune: RawJSON.null,
- trioSettings: inputs.8
- )
- let (swiftResult, makeProfileInputs) = OpenAPSSwift.makeProfile(
- preferences: inputs.4,
- pumpSettings: inputs.0,
- bgTargets: inputs.1,
- basalProfile: inputs.2,
- isf: inputs.3,
- carbRatio: inputs.5,
- tempTargets: tempTargets,
- model: inputs.7,
- trioSettings: inputs.8,
- clock: now
- )
- let comparison = JSONCompare.createComparison(
- function: .makeProfile,
- swift: swiftResult,
- swiftDuration: 1.0,
- javascript: jsResult,
- javascriptDuration: 1.0,
- iobInputs: nil,
- mealInputs: nil,
- autosensInputs: nil,
- determineBasalInputs: nil,
- makeProfileInputs: makeProfileInputs
- )
- if comparison.resultType == .valueDifference {
- print(comparison.differences!.prettyPrintedJSON!)
- }
- #expect(comparison.resultType == .matching)
- }
- }
|